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的外部接口。

AppWidgetService IAppWidgetService AppWidgetManager AppWIdgetHost

圖一、AppWidgetService的外部接口

 

AppWidgetService通過IAppWidgetService這個標準的AIDL方式向外界提供服務。

AppWidgetHost提供了Host端操作所需要的方法,而這些方式實際是通過AIDL方式調用IAppWidgetService實現的;AppWidgetManager提供了Host端和Provider端操作所需要的方法,而這些方式也是通過AIDL方式調用IAppWidgetService實現的。

 

二、AppWidgetService的內部數據結構——靜態分析

 

下圖是AppWidgetService中的數據結構。

 AppWidgetService Host Provider AppWidgetId

圖二、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提供的服務,以及內部實現。

 

發佈了88 篇原創文章 · 獲贊 16 · 訪問量 124萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章