Read The Fucking Source Code
引言
- Android AppWidget相对偏冷门。
-
开门见山一张图,复杂问题庖解牛。
1. AppWidget简介
- Android widget 也称为桌面插件,其是android系统应用开发层面的一部分,但是又有特殊用途,而且会成为整个android系统的亮点。Android中的AppWidget与google widget和中移动的widget并不是一个概念,这里的AppWidget只是把一个进程的控件嵌入到别外一个进程的窗口里的一种方法。
- AppWidget的服务核心在AppWidgetService中,它是系统应用,在SystemServer进程中。
- AppWidget的提供方由应用提供(对大部分应用开发者来说,了解操作这一块就够了)。
- AppWidget的显示方,基本上运行在Launcher中。
- AppWidget支持的控件是由局限性的,比如不支持RecyclerView等。
- RemoteViews 在Android中的使用场景主要有:自定义通知栏和桌面小部件。
2. AppWidget提供方
2.1 XML <appwidget-provider>
- minHeight、minWidth 定义Widget的最小高度和最小宽度(Widget可以通过拉伸来调整尺寸大小)。
- previewImage 定义添加小部件时显示的图标。
- initialLayout 定义了小部件使用的布局。
- updatePeriodMillis 定义小部件自动更新的周期,单位为毫秒。
- resizeMode 指定了 widget 的调整尺寸的规则。可取的值有: “horizontal”, “vertical”, “none”。“horizontal"意味着widget可以水平拉伸,“vertical”意味着widget可以竖值拉伸,“none”意味着widget不能拉伸;默认值是"none”。
- widgetCategory 指定了 widget 能显示的地方:能否显示在 home Screen 或 lock screen 或 两者都可以。它的取值包括:“home_screen” 和 “keyguard”。Android 4.2 引入。
2.2 AppWidgetProvider重载方法
- onUpdate()当Widget被添加或者被更新时会调用该方法。上边我们提到通过配置updatePeriodMillis可以定期更新Widget。但是当我们在widget的配置文件中声明了android:configure的时候,添加Widget时则不会调用onUpdate方法。
- onAppWidgetOptionsChanged()这个方法会在添加Widget或者改变Widget的大小时候被调用。在这个方法中我们还可以根据Widget的大小来选择性的显示或隐藏某些控件。
- onDeleted(Context, int[])当控件被删除的时候调用该方法。
- onEnable(Context) 第一个加入到屏幕上。
- onDisabled(Context)最后一个widget从屏幕移除。
- onReceive(Context, Intent) 当接收到广播的时候会被调用。
2.3 AppWidgetProvider的使用经验
- 作为AppWidgetProvider的实现者,一定要实现onUpdate函数,因为这个函数决定widget的显示方式,如果没有这个函数widget根本没办法出现。
- onUpdate的实现基本上遵循下面的流程:
- 创建RemoteViews。
- 调用AppWidgetManager的updateAppWidget去更新widget。
3. AppWidget显示方
3.1 AppWidgetHost
- AppWidgetHost 是实际控制widget的地方,大家注意,widget不是一个单独的用户界面程序,他必须寄生在某个程序(activity)中,这样如果程序要支持widget寄生就要实现AppWidgetHost。
- 它的主要功能有两个:
- 监听来自AppWidgetService的事件。
- 另外一个功能就是创建AppWidgetHostView。
- RemoteViews不是真正的View,只是View的描述,而 AppWidgetHostView才是真正的View。这里先创建AppWidgetHostView,然后通过AppWidgetService查询 appWidgetId对应的RemoteViews,最后把RemoteViews传递给AppWidgetHostView去updateAppWidget。
- AppWidgetHost和AppWidgetHostView是在框架中定义的两个基类。应用程序可以利用这两个类来实现自己的Host。Launcher是缺省的桌面,它是一个Host的实现者。
- AppWidgetHostView是真正的View,但它只是一个容器,用来容纳实际的AppWidget的View。这个AppWidget的View是根据RemoteViews的描述来创建。
3.2 Launcher3中对widget的使用理解
- Launcher3对所有widget的遍历是在AppWidgetManagerCompat及其子类中。
- 通过AppWidgetManager的getInstalledProvidersForProfile / getInstalledProvidersForPackage(Android版本差异),获取到AppWidgetProviderInfo的集合。
- LauncherAppWidgetHost负责监听更新更新和创建LauncherAppWidgetHostView。
- LauncherAppWidgetHostView扩展了AppWidgetHostView,实现了对长按事件的处理。
- LauncherAppWidgetHost扩展了AppWidgetHost,这里只是重载了onCreateView,创建LauncherAppWidgetHostView的实例。
3.3 Launcher3核心代码
4. AppWidget服务方
4.1 服务框架
- AppWidgetService是框架的的核心类,是系统 service之一,它负责widgets的管理工作。加载,删除,定时事件等都需要AppWidgetService的处理。开机自启动的。
- AppWidgetService存在的目的主要是解开AppWidgetProvider和AppWidgetHost之间的耦合。如果 AppWidgetProvider和AppWidgetHost的关系固定死了,AppWidget就无法在任意进程里显示了。而有了 AppWidgetService,AppWidgetProvider根本不需要知道自己的AppWidget在哪里显示了。
- AppWidgetManager 负责widget视图的实际更新以及相关管理。
5. 我对整体AppWidget的理解
AppWidgetProvider中的onUpdate()参数appWidgetIds为什么是个数组?
- 我的理解是:一个AppWidgetProvider可能被多个地方使用,可能会有几个实例存在,数组就是对应的多个实例的存在场景,可以进行区分更新。但是一般来说,只会有一个。
AppWidget的更新过程可以说的通俗一点吗?
- Widget更新:提供方把应用信息 + RemoteViews包装好发给发给服务方(这些只是信息结构体,其实并不是View),显示方监听从服务方的回调,在回调中可以拿到这些信息结构体。
- 理解方式:虽然我们的目的是更新View,但是我们不能用更新View的思路去理解,只能用更新Data的思路去理解。
- 马夫与马:更新频繁当然不好,因为虽然在应用提供方不涉及View的频繁加载,但是在显示方(要通过数据结构生成View),这就是系统原生为什么把AppWidget 的被动刷新频率下限设定为半小时。就怕马夫(提供方)赶马(显示方),马累死了。
RemoteViews的理解
- RemoteViews并不是一个真正的View,它没有实现View的接口,而只是一个用于描述View的实体。比如:创建View需要的资源ID和各个控件的事件响应方法。RemoteViews会通过进程间通信机制传递给AppWidgetHost。
- 现在我们可以看出,Android中的AppWidget与google widget和中移动的widget并不是一个概念,这里的AppWidget只是把一个进程的控件嵌入到别外一个进程的窗口里的一种方法。View在另 外一个进程里显示,但事件的处理方法还是在原来的进程里。
全流程处理时序简单说明。
- AppWidgetService启动:SystemServer服务,开机启动。
- AppWidgetProviderInfo获取:AppWidgetService通过PMS针对注册了ACTION_APPWIDGET_UPDATE("android.appwidget.action.APPWIDGET_UPDATE")的静态广播进行扫描查询。
- meta-data解析:查询到的就是AppWidget,然后从其配置的meta-data中的"appwidget-provider"对应的xml文件开始解析生成AppWidgetProviderInfo结构体。
- Launcher3获取Widget信息:Launcher通过AppWidgetManager向AppWidgetService按需拿到所有的AppWidget信息,可以进行展示。
- Launcher3显示Widget信息:Launcher创建AppWidgetHost,通过上面拿到的Widget信息生成对应的AppWidgetHostView进行展示。
- Launcher3更新Widget信息:AppWidgetHost创建监听AppWidgetService的更新,进行接收回调显示更新。
- AppWidget被动刷新:AppWidgetService会根据AppWidgetProviderInfo的配置维持一个30分钟下限的更新时钟,来给AppWidgetProvider来发送更新通知。
- AppWidget主动刷新:应用侧可以拿到AppWidgetManager来进行主动刷新。
6. 附录(Framework层的代码路径及结构)
小编的博客系列
优秀博客推荐
Android官网
Android列表小部件(Widget)开发详解
Android UI组件----AppWidget控件入门详解
Android之AppWidget(桌面小部件)开发浅析
Android之Widget
Android中Launcher对于AppWidget处理的分析:AppWidgetHost角色
Android widget使用心得
Android Widget小组件开发(一)
android开发之widget控件突然停止更新的原因