Android AppWidget

Read The Fucking Source Code

引言
  • Android AppWidget相对偏冷门。
  • 开门见山一张图,复杂问题庖解牛。


    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

  • 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核心代码

Launcher3核心代码-按需遍历Widget集合
Launcher3核心代码-Widget的监听生成

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 Framework 全家桶

优秀博客推荐

Android官网
Android列表小部件(Widget)开发详解
Android UI组件----AppWidget控件入门详解
Android之AppWidget(桌面小部件)开发浅析
Android之Widget
Android中Launcher对于AppWidget处理的分析:AppWidgetHost角色
Android widget使用心得
Android Widget小组件开发(一)
android开发之widget控件突然停止更新的原因

你可能感兴趣的:(Android AppWidget)