上一篇是对Dev Guide中关于SDK的翻译,这篇文章的目的就是对AppWidget有一个深入的介绍。

一、首先介绍一下在开机过程中系统对AppWidget做了什么

当SystemServer.java运行到init2()的时候,通过SystemManager.addService(Context.AppWidget_Service,appWidget)将AppWidgetService服务加到服务队列里面,当所有服务加载完毕后会调用appWidgetF.systemReady(safeMode)进入到AppWidgetService.java,在这个方法中做了三件事:

1、遍历所有的安装包,找到符合条件的ACTION:ACTION_APPWIDGET_UPDATE和<meta-data android:name="android.appwidget.provider"/>的receiver,解析相关信息,保存到本地数据成员中。

2、读取本地文件数据:/data/system/appwidgets.xml,所有已安装到桌面的widget的信息都保存在这个文件里。读出来,也保存到本地数据成员里。

3、注册了三个消息:ACTION_BOOT_COMPLETED(系统启动到桌面就会发送此消息),ACTION_PACKAGE_ADDED(有新 APK包安装到系统),ACTION_PACKAGE_REMOVED(有APK包被删除)。当系统启动到桌面后,AppWidgetService接收 到了ACTION_BOOT_COMPLETED消息,它会去检查本地数据成员,如果有已经安装到桌面的widget,它会上发 ACTION_APPWIDGET_ENABLED和ACTION_APPWIDGET_UPDATE消息。如果有widget设置了updatePeriodMillis的属性,它就会开始计时(这个是通过AlarmManager来实现的),到时间时,就会再次发送ACTION_APPWIDGET_UPDATE消息。



二、与AppWidget相关的类有:

RemoteViews.java

* A class that describes a view hierarchy that can be displayed in
* another process. The hierarchy is inflated from a layout resource
* file, and this class provides some basic operations for modifying
* the content of the inflated hierarchy

上面是Google给的关于RemoteViews的解释,大家不要被它的名字给欺骗了,说白了它就是作为一个描述view信息的载体,通过它可以在进程间传递,在另一个进程中由AppWidgetHostView去获取RemoteViews所承载的信息并且显示出来。

AppWidgetProvider.java

这个类继承BroadcastReceiver,并且重写了它里面的方法,里面通常使用的是onUpdate()方法对AppWidget更新

AppWidgetManager.java

* Updates AppWidget state; gets information about installed AppWidget providers and other
* AppWidget related state.



AppWidgetHost.java

* AppWidgetHost provides the interaction with the AppWidget service for apps,
* like the home screen, that want to embed AppWidgets in their UI.

AppWidgetHostView.java

* Provides the glue to show AppWidget views. This class offers automatic animation
* between updates, and will try recycling old views for each incoming
* {@link RemoteViews}.

AppWidgetService.java

具体实现AppWidgetHost和AppWidgetManager中的方法。


[img]http://dl.iteye.com/upload/attachment/477272/3b7ae13e-59bf-31bd-9a81-c587a52aec9a.jpg[/img]


对上图的一个解释:

当我们把一个AppWidget放在桌面的时候其实这个AppWidget是停靠在Launcher的一个View上面,被停靠的这个Activity我理解为“宿主”,而AppWidget是运行在一个独立的进程中,所以AppWidget要与这个“宿主”通信的话就需要IPC。

当RemoteViews把AppWidget的View信息传递“宿主”的时候,通过AppWidgetHost获得AppWidgetHostView的实例,这样 按照RemoteViews中的信息将AppWidget的View绘制到“宿主”中来。至此,我们的Widget就显示在“宿主”上了。

下图是对AppWidgetManager和AppWidgetHost做的解释,作为管理类,各自完成不同的管理任务。


[img]http://dl.iteye.com/upload/attachment/477274/11cbccd3-3966-3b3d-b3e0-664ce528f03c.jpg[/img]


在网上看到这么一段关于AppWidget的比喻,贴来大家看看

Android AppWidget框架妄析: Android中的借尸还魂


Android, AppWidget, 借尸还魂
由于初识Android不久,所以一切分析皆可有误,故而只能为之妄析。 题目起的比较恐怖,然非我本意。 只是实在找不到更加贴切的,可以对AppWidget框架一针而见血的比喻了。 闲话少说,且看如何个借尸还魂。

首看魂者何来。 大家都知道Widget的宗旨,就是要在同一屏幕(界面上)运行多个具有独立功能的小插件,从而丰富功能的同时简化操作。那么,在Android的4大组件中,何人可以充当该角色,抑或需要再独立设计一个组件? Activity? 非也!! Activity是UI呈现和用户交互的一个组件,具有独特的Task管理机制,同一时刻,框架只允许一个Activity与用户交互并呈现。 而Widget的特点是,多实例的并发交互性。 所以,Activity不能满足,不能满足同时多个Widget的并发交互和呈现。 既然不能前台,那么只能在后台Running, Service or BroadCastReceiver? 由于Widget需要处理众多的事件交互,所以,BroadCastReceiver更加合适。 既然找到了合适的,那么也就没有必要再创造新的。 够用就可以,不是越多越好,这也是软件设计的准则。 OK, AppWidget的魂已经找到,BroadCastReceive也, 所以,Android中的AppWidget其本质就是一个BroadCastReceive组件。

再看尸者何来。 尸者,阳间之物也。 虽已死(本身无用),却能见光(呈现)也。 任何一个期望在其之上运行Widget的前台的应用(Activity),其实就是一个Widget宿主。 其本身而言,无任何Widget功能,但却可以和用户交互并呈现,从此点而言,可谓尸也。 Android中的AppHost即为尸也。

最后我们看如何还魂。 AppWidget为魂,功能强大,为所欲为,但却始终位于阴间(后台运行),无法见日,故而众人不可观之。 AppHost为尸,虽见天日,却已无所可为。 我们何不将此二者互补那?? 但是,阴阳两隔,必须使用特殊的方式, 此即为还魂术。 通过还魂术,可使得魂寄于尸而见天日。 还魂术就是阴间通往阳间的大道。 Android中的还魂术即为RemoteView。 在Android中,由于进程边界的存在,使得AppWidget与AppHost也阴阳两隔,默认是无法直接沟通的。 采用RemoteView,让AppWidget将一切需要呈现的描述构建到RemoteView中,AppHost中再基于该描述,重新创建于属于自己进程中的View进而显示。