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的外部接口
AppWidgetService通過IAppWidgetService這個標準的AIDL方式向外界提供服務。
AppWidgetHost提供了Host端操作所需要的方法,而這些方式實際是通過AIDL方式調用IAppWidgetService實現的;AppWidgetManager提供了Host端和Provider端操作所需要的方法,而這些方式也是通過AIDL方式調用IAppWidgetService實現的。
二、AppWidgetService的內部數據結構——靜態分析
下圖是AppWidgetService中的數據結構。
圖二、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, mHosts和mAppWidgetIds中。
下面是系統中只有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, mHosts和mAppWidgetIds對於處理這些機制至關重要。
本章結合典型場景來看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。
相關文章
通過這一系列的相關文章,可獲得與本文關聯的信息:
簡要描述AppWidget系統框架,並對這裏的組成元素做簡要的闡述。
描述由Launcher作爲AppWidgetHost發起,Settings中AppWidgetPickActivity實現的選取並綁定AppWidgetProvider的過程。
Android中AppWidget的分析與應用:AppWidgetProvider
以SettingsAppWidgetProvider爲例,通過對AppWidgetProvider內部實現機制的描述,使讀者深刻理解開發AppWidgetProvider所要注意的地方。
Android中Launcher對於AppWidget處理的分析:AppWidgetHost角色
以Launcher爲例,通過它作爲AppWidgetHost角色所參與AppWidget實現過程的描述,使讀者理解AppWidgetHost如何實現。
結合AppWidget的應用場景,分析Android中RemoteViews的內部具體實現。
Android AppWidget的核心之AppWidgetService
AppWidgetService提供的服務,以及內部實現。