Android AppWidget核心之AppWidgetService

田海立@CSDN

2012-9-12

 

本文讲述Android中AppWidget系统的核心AppWidgetService。从AppWidgetService提供的外部接口、内部数据结构、初始化过程以及典型场景的实现等几方面来阐述。

前面讲述AppWidget的诸文中已经讲到,AndroidAppWidget系统里的Host角色和Provider角色的实例通过AppwidgetHost/ AppWidgetManager组件提供的方法参与AppWidget系统。实际AppwidgetHost/ AppWidgetManager采用AndroidAIDL方式通过IAppWidgetService与AppWidgetService交互,从而实现了运行于不同进程中的Host/Provider/Service协同运作。另外,AppWidgetHost通过实现IAppWidgetHost提供Callback给AppWidgetService,实现Provider更新时通知Host;而对Provider的通知是直接向Provider定向发广播消息[7]

 

一、AppWidgetService的外部接口


下图描述了AppWidgetService的外部接口。

Android AppWidget核心之AppWidgetService_第1张图片

图一、AppWidgetService的外部接口

 

AppWidgetService通过IAppWidgetService这个标准的AIDL方式向外界提供服务。

AppWidgetHost提供了Host端操作所需要的方法,而这些方式实际是通过AIDL方式调用IAppWidgetService实现的;AppWidgetManager提供了Host端和Provider端操作所需要的方法,而这些方式也是通过AIDL方式调用IAppWidgetService实现的。

 

二、AppWidgetService的内部数据结构——静态分析

 

下图是AppWidgetService中的数据结构。

 Android AppWidget核心之AppWidgetService_第2张图片

图二、AppWidgetService内部数据结构

 

mInstalledProviders:ArrayList<AppWidgetService.Provider>记录所有已经安装的AppWidgetProvider【《Android中AppWidget的分析与应用:AppWidgetProvider》中已经描述了单个AppWidgetProvider被AppWidget系统所识别的过程;本文Section#3.1 (1)会描述加载所有系统中的AppWidgetProvider的过程】。其中,

  •   uid: int记录该AppWidgetProvider运行所在进程的uid;
  •   info: AppWidgetProviderInfo记录AppWidgetProvider的信息;
  •   instances: ArrayList<AppWidgetId>记录与该AppWidgetProvider绑定的信息(可与多个Host绑定);
  •   broadcast: PendingIntent该Provider需要被周期性刷新时(updatePeriodMillis属性值大于0),周期性产生AppWidgetManager.ACTION_APPWIDGET_UPDATE来更新Provider;
  •   zombie记录该AppWidgetProvider状态是否僵死,因为是管理多个进程的,如果Provider所在的进程僵死,对与之绑定的Host也要做处理。
  •   tag: int保存状态时记录index值。

mHosts: ArrayList<AppWidgetService.Host>记录AppWidgetHost信息。其中,

  •   uid: int记录该AppWidgetHost运行所在进程的uid;
  •   hostId: int记录AppWidgetHost的Id,由AppWidgetHost注册时指定;
  •   packageName: String记录
  •   instances: ArrayList<AppWidgetId>记录与该AppWidgetHost绑定的信息(可与多个Provider绑定);
  •   callbacks: IAppWidgetHost用来在与该Host绑定的Provider变化时候通知Host。
  •   zombie记录该AppWidgetHost状态是否僵死,因为是管理多个进程的,如果Host所在的进程僵死,对与之绑定的Provider也要做处理。
  •   tag: int保存状态时记录index值。

mAppWidgetIds:ArrayList<AppWidgetService.AppWidgetId>记录所有Provider与Host绑定的信息。其中,

  •   appWidgetId: int记录绑定好的id,由allocateAppWidgetId()申请;
  •   provider: AppWidgetService.Provider绑定的Provider端;
  •   host: AppWidgetService.Host绑定的Host 端;
  •   views: RemoteViews 记录Provider端创建的RemoteViews,会用之通知Host端显示Provider端提供的内容。

 

现在看这些属性可能比较空泛,在后续的分析过程中再回头看这些属性的才能充分理解其含义。

 

三、AppWidgetService初始化

AppWidgetService运行于system_process进程中,其初始化有两个过程:

  •  systemReady时的初始化;
  •  收到ACTION_BOOT_COMPLETED之后的初始化。

 

3.1 AppWidgetService.systemReady()初始化

在SystemServer的init2()阶段,启动ServerThread。ServerThread线程中,创建AppWidgetService实例,并加入ServiceManager中,然后执行AppWidgetService.systemReady()。

 

AppWidgetService.systemReady()中,执行下面三个操作

 

(1)执行loadAppWidgetList(),从Android系统中查找所有已经被安装的AppWidgetProvider,解析其中的信息放到AppWidgetService.Provider中,再添加到mInstalledProviders:ArrayList<Provider>中。

    void loadAppWidgetList() {
       PackageManager pm = mPackageManager;
 
       Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
       List<ResolveInfo> broadcastReceivers =pm.queryBroadcastReceivers(intent,
               PackageManager.GET_META_DATA);
 
       final int N = broadcastReceivers == null ? 0 :broadcastReceivers.size();
       for (int i=0; i<N; i++) {
           ResolveInfo ri = broadcastReceivers.get(i);
           addProviderLocked(ri);
        }
    }

1.      检索系统中所有Intent-filter的action为AppWidgetManager.ACTION_APPWIDGET_UPDATE的Receiver,并放入broadcastReceivers:List<ResolveInfo>;[Line#2~ 6]

2.      对每一个这样的Broadcast Receiver,加入到已安装AppWidgetProvider列表mInstalledProviders:ArrayList<Provider>中。[Line#8~ 12]

加入到AppWidgetProvider列表是通过addProviderLocked()实现:

   boolean addProviderLocked(ResolveInfo ri) {
       Provider p = parseProviderInfoXml(newComponentName(ri.activityInfo.packageName,
                    ri.activityInfo.name), ri);
        if(p != null) {
           mInstalledProviders.add(p);
            return true;
        }else {
           return false;
        }
    }

parseProviderInfoXml()解析meta-data中AppWidgetManager.META_DATA_APPWIDGET_PROVIDER("android.appwidget.provider")指向的xml文件所配置的Provider的配置信息,放到Provider的info:AppWidgetProviderInfo中。另外,Provider的UID也会被检索出来。

Provider被加入到mInstalledProviders:ArrayList<Provider>中。

具体AppWidgetProvider如何配置,可参看《Android中AppWidget的分析与应用:AppWidgetProvider》

 

(2)执行loadStateLocked(),从/data/system/appwidgets.xml文件中读取Provider、Host与Provider绑定的信息,把这些信息放入中mInstalledProviders, mHostsmAppWidgetIds中。

下面是系统中只有Launcher上放置了一个“电量控制”AppWidget的对应的appwidgets.xml文件

<gs>
    <ppkg="com.android.settings"
      cl="com.android.settings.widget.SettingsAppWidgetProvider"/>
    <hpkg="com.android.launcher" id="400" />
    <gid="1" h="0" p="0" />
</gs>

其中的,

  •  节点p对应AppWidgetProvider。pkg和cl分别是packageName和className。
  •  节点h对应AppWidgetHost。pkg是packageName;id是HostId。
  •  节点g是Provider和Host的绑定关系。id是其标志,在绑定之前通过allocateAppWidgetId时申请;h对应AppWidgetHost的index;p对应AppWidgetProvider的index。

 

下面看Section#2图二中的数据结构如何建立起来的:

对于p节点,通过packageName和className,检索步骤(1)中获得的mInstalledProviders:ArrayList<Provider>可得到具体的Provider信息。Provider暂时保存;

对于h节点,创建AppWidgetService.Host对象,并通过packageName获得uid,通过解析id获得的hostId是16进制的。Host对象加入到mHosts: ArrayList<Host>中;

对于g节点,创建AppWidgetId对象,xml解析出的id也是十六进制的,赋值给appWidgetId。通过p指向Provider的index(16进制),找到Provider,赋值给AppWidgetId.provider。通过h指向Host的index(16进制),找到Host,赋值给AppWidgetId.host。provider和host的instances都指向本AppWidgetId对象。然后把该AppWidgetId对象加入到mAppWidgetIds: ArrayList<AppWidgetId>中。

 

这样Provider对象、Host对象,以及与Provider的绑定关系也就分别利用mInstalledProviders:ArrayList<AppWidgetService.Provider>, mHosts: ArrayList<AppWidgetService.Host>, 和mAppWidgetIds: ArrayList<AppWidgetService.AppWidgetId>建立起来了。

 

(3)注册四个BroadcastReceiver:

1) Intent.ACTION_BOOT_COMPLETED

    Android系统启动完成之后,AppWidgetService还需要一些初始化工作

2) Intent.ACTION_CONFIGURATION_CHANGED

    Android配置信息改变

3) Intent.ACTION_PACKAGE_ADDED/Intent.ACTION_PACKAGE_REMOVED

    应用被添加/删除

4) Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE / Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE

    sdcard的安装与缷载

 

3.2 系统启动完成之后AppWidgetService的初始化工作

Android系统启动完成之后,会发出Intent.ACTION_BOOT_COMPLETED广播。

 

AppWidgetService在Intent.ACTION_BOOT_COMPLETED广播的Receiver中sendInitialBroadcasts()内处理下列事项:

检索mInstalledProviders获取所有的Provider,通过Provider.instances获得已经绑定的那些AppWidgetProvider。

对这些Provider,

  •  发出AppWidgetManager.ACTION_APPWIDGET_ENABLED广播。
  •  发出AppWidgetManager.ACTION_APPWIDGET_UPDATE广播。
  •  根据Provider的配置如果updatePeriodMillis大于0,注册定时更新广播。会向该Provider周期性发送AppWidgetManager.ACTION_APPWIDGET_UPDATE广播。

 

3.3 整个初始化过程

上述的AppWidgetService的初始化工作是AppWidget系统初始化的一部分,还有AppWidgetHost和AppWidgetProvider的参与的初始化过程。

 

下面是完整的过程:

(1)AppWidgetService.systemReady()初始化

本文Section#3.1,完成:

  •  从PackageManager获取所有系统中所有的AppWidgetProvider,初始化mInstalledProviders;
  •  从文件/data/system/appwidgets.xml中读取已经绑定的AppWidget,初始化mHosts和mAppWidgetIds;
  •  注册所需的Broadcast Receivers。

 

(2)Launcher利用AppWidgetHost的startListening()注册。

AppWidgetHost构造了IAppWidgetHost对象,并通过IAppWidgetService的startListening()把Callback,HostId和RemoteViews的List传递给Service。AppWidgetService中的实现如下:

    public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
            List<RemoteViews> updatedViews) {
       int callingUid = enforceCallingUid(packageName);
       synchronized (mAppWidgetIds) {
           Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
           host.callbacks = callbacks;
 
           updatedViews.clear();
 
           ArrayList<AppWidgetId> instances = host.instances;
           int N = instances.size();
           int[] updatedIds = new int[N];
           for (int i=0; i<N; i++) {
               AppWidgetId id = instances.get(i);
               updatedIds[i] = id.appWidgetId;
               updatedViews.add(id.views);
           }
           return updatedIds;
        }
    }

  •  通过lookupOrAddHostLocked()检索mHosts:ArrayList<AppWidgetService.Host>中是否已经有Host对象。如果不存在则创建新的Host对象,并加入到mHosts中;[Line#5]
  •  把AppWidgetHost的Callback保存到Host.callbacks里。这里只是AppWidgetHost在Service的远端代理;[Line#6]
  •  Host.instances里保存的是与Host绑定关系,从中:
    •  获得AppWidgetId;
    •  把已经存在的RemoteViews加入到AppWidgetHost端的RemoteViews的List中。

 

返回所有与该Host已经发生绑定关系的AppWidgetId。

 

(3)Launcher正常启动加载数据库中的AppWidget

《Android中Launcher对于AppWidget处理的分析:AppWidgetHost角色》#3中具体描述里这一过程。

第一次开机,从default_workspace.xml中加载初始化AppWidget,并加入到数据模型中;

从数据库中读取AppWidget信息,并创建AppWidgetHostView本地显示。

 

(4)系统启动之后,AppWidgetService的初始化

本文Section#3.2

对所有有绑定关系的AppWidgetProvider发送Enable和Update广播,并根据配置建立周期性刷新机制。

 

(5)AppWidgetProvider接收到广播后都会调用onEnabled()方法和onUpdate()方法执行操作。

《Android中AppWidget的分析与应用:AppWidgetProvider》中具体描述里这一过程。

  •  在onEnabled()方法中进行一些初始化操作;
  •  在onUpdate()方法中创建RemoteViews布局对象;并通过AppWidgetManager.updateAppWidget(intappWidgetId, RemoteViews remoteViews)方法通知AppWidgetService用RemoteViews对象更新appWidgetId所对应的AppWidgetHost.

AppWidgetService接收到了appWidgetId和RemoteViews后,通过appWidgetId查找Provider与Host的绑定关系,并通过IAppWidgetHost的Callback通知Host用RemoteViews中的布局更新到AppWidgetHostView布局对象中。

这样,AppWidget就显示在了Launcher中。

 

四、AppWidgetService的实现

AppWidgetService通过IAppWidgetService提供各种服务;AppWidgetHost和AppWidgetManager封装了这个方法的使用。AppWidgetHost角色通过AppWidgetHost和AppWidgetManager使用AppWidgetService的服务;AppWidgetProvider角色通过AppWidgetManager使用AppWidgetService的服务,AppWidgetService直接操作或通知AppWidgetProvider。

Section#2中的内部数据结构mInstalledProviders, mHostsmAppWidgetIds对于处理这些机制至关重要。

 

本章结合典型场景来看AppWidgetService通过AppWidgetHost和AppWidgetManager提供的各种功能的实现。

 

看AppWidgetService提供的服务的实现,可以从前面所有本系列文章中的时序图中,沿着所有AppWidgetManager和AppWidgetHost对象生命线的看。主要有:

AppWidgetHost.startListening()

    AppWidgetHost向AppWidget系统中注册。

AppWidgetHost.allocateAppWidgetId()

    AppWidgetHost申请AppWidgetId。AppWidgetHost(e.g.Launcher)选取AppWidgetProvider之前,先向AppWidget系统申请,《Android中选取并绑定AppWidget》#1中描述这一场景。Launcher中第一次从配置中读取AppWidget信息,也要先申请AppWidgetId,《Android中Launcher对于AppWidget处理的分析:AppWidgetHost角色》#2中描述这一场景。实现在AppWidgetService中,本文4.2讲述。

AppWidgetManager.getInstalledProviders()

    获得系统中所有的AppWidgetProvider。AppWidgetPicker列举系统中所有的AppWidgetProvider,《Android中选取并绑定AppWidget》#3中描述这一场景。实现在AppWidgetService中,本文4.3讲述。

AppWidgetManager.bindAppWidgetId()

    绑定AppWidgetId与AppWidgetProvider。AppWidgetPicker中用户选择了某个AppWidgetProvider,用申请到的AppWidgetId与之绑定。《Android中选取并绑定AppWidget》#4中描述这一场景。Launcher中第一次从配置中读取AppWidget信息,也要先申请AppWidgetId,《Android中Launcher对于AppWidget处理的分析:AppWidgetHost角色》#2中描述这一场景。实现在AppWidgetService中,本文4.4讲述。

AppWidgetManager.getAppWidgetInfo()

    通过AppWidgetId获取与之绑定的AppWidgetProviderInfo。Android中选取并绑定AppWidget之后,通过AppWidgetId就可以获取与之绑定的AppWidgetProviderInfo。选取之后的Launcher中有使用的示例,《Android中Launcher对于AppWidget处理的分析:AppWidgetHost角色》#1和#3.4中描述这一场景。本文4.5讲述AppWidgetService中的实现。

IAppWidgetService.getAppWidgetViews()

    获得AppWidgetProvider提供的RemoteViews。AppWidgetHost创建AppWidgetHostView之后,如果AppWidgetProvider已经提供了RemoteViews,这里可以获取出来,设置给AppWidgetHostView。《Android中Launcher对于AppWidget处理的分析:AppWidgetHost角色》#1图二中描述这一场景。本文4.6讲述AppWidgetService中的实现。

AppWidgetManager.updateAppWidget()

    设置AppWidgetProvider提供的RemoteViews。AppWidgetProvider创建了RemoteViews之后,通过这个接口告知AppWidgetServic,《Android中AppWidget的分析与应用:AppWidgetProvider》#5图三中描述这一场景。本文4.7讲述AppWidgetService中的实现。

 

4.1 AppWidgetHost的注册

原型:publicint[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,List<RemoteViews> updatedViews)

AppWidgetHost有IAppWidgetHost(通过Callbacks)的实现mCallbacks:AppWidgetHost.Callbacks。在AppWidgetHost.startListening()中,AppWidgetHost通过IAppWidgetService把IAppWidgetHost,HostId等信息注册到AppWidgetService中。

从3.3(2)可以看具体实现。mHosts里面存放注册的AppWidgetHost;mHosts.instances里存放的是与该Host绑定的信息:AppWidgetId。AppWidgetId的views存储的是Provider创建并提供的RemoteViews,此时可能已经有内容,把它加入到AppWidgetHost传入的参数中。AppWidgetHostView如果已经存在,AppWidgetHost会通过AppWidgetHostView.updateAppWidget()用RemoteViews更新AppWidgetHostView。

 

4.2 申请AppWidgetId

原型:publicint allocateAppWidgetId(String packageName, int hostId)

实现:

生成一个Id:int;

通过packageName和hostId检索mHosts中的AppWidgetService.Host对象;

创建AppWidgetId对象,把appWidgetId和hostId保存起来,并把它加入到Host的instances中以及mAppWidgetIds: AppWidgetService.AppWidgetId中。

 

4.3 获得所有的AppWidgetProvider列表

原型:publicList<AppWidgetProviderInfo> getInstalledProviders()

实现:系统初始化时,所有的AppWidgetProvider的信息都已经被检索出来并放入了mInstalledProviders:AppWidgetService.Provider。这里直接把mInstalledProviders里的info组成链表即可。

 

4.4 绑定AppWidget

原型:publicvoid bindAppWidgetId(int appWidgetId, ComponentName provider)

实现:

通过appWidgetId检索mAppWidgetIds: AppWidgetService.AppWidgetId中的AppWidgetId对象;

通过provider检索mInstalledProviders:AppWidgetService.Provider中的Provider对象;

通过AppWidgetId的provider和Provider的instances,把Provider对象和AppWidgetId对象关联起来;

向AppWidgetProvider发送Enable和Update广播。

 

4.5 获取指定的AppWidgetProviderInfo

原型:publicAppWidgetProviderInfo getAppWidgetInfo(int appWidgetId)

实现:

通过appWidgetId检索mAppWidgetIds: AppWidgetService.AppWidgetId中的AppWidgetId对象;

把AppWidgetId对象provider里的info返回。

 

4.6 获取RemoteViews

原型:publicRemoteViews getAppWidgetViews(int appWidgetId)

实现:

通过appWidgetId检索mAppWidgetIds: AppWidgetService.AppWidgetId中的AppWidgetId对象;

把AppWidgetId对象里的views:RemoteViews返回。

 

4.7 提供RemoteViews

原型:publicvoid updateAppWidgetProvider(ComponentName provider, RemoteViews views)

通过参数<provider>检索mInstalledProviders:AppWidgetService.Provider中的Provider对象;

通过Provider对象的instances得到ArrayList<AppWidgetId>,也就是所有与该Provider关联的对象;

对ArrayList<AppWidgetId>里所有的对象,通过host的callback接口,通知AppWidgetHost,用RemoteViews更新AppWidgetHostView。

 

相关文章

 

通过这一系列的相关文章,可获得与本文关联的信息:

 

AndroidAppWidget框架

    简要描述AppWidget系统框架,并对这里的组成元素做简要的阐述。

Android中选取并绑定AppWidget

    描述由Launcher作为AppWidgetHost发起,Settings中AppWidgetPickActivity实现的选取并绑定AppWidgetProvider的过程。

Android中AppWidget的分析与应用:AppWidgetProvider

    以SettingsAppWidgetProvider为例,通过对AppWidgetProvider内部实现机制的描述,使读者深刻理解开发AppWidgetProvider所要注意的地方。

Android中Launcher对于AppWidget处理的分析:AppWidgetHost角色

    以Launcher为例,通过它作为AppWidgetHost角色所参与AppWidget实现过程的描述,使读者理解AppWidgetHost如何实现。

Android中RemoteViews的实现

    结合AppWidget的应用场景,分析Android中RemoteViews的内部具体实现。

Android AppWidget的核心之AppWidgetService

    AppWidgetService提供的服务,以及内部实现。

 

你可能感兴趣的:(android,appWidgetId,AppWidgetHost)