#### (1)壁紙服務的驗證與啟動
bindWallpaperComponentLocked()方法將會啟動由ComponentName所指定的WallpaperService,并向WMS申請用于添加壁紙窗口的窗口令牌。不過在此之前,bindWallpaperComponentLocked()會對ComponentName所描述的Service進行一系列的驗證,以確保它是一個壁紙服務。而這一系列的驗證過程體現了一個Android服務可以被當作壁紙必要的條件。
**WallpaperManagerService.java-->WallpaperManagerService.setWallpaperComponentLocked()**
```
boolean bindWallpaperComponentLocked(ComponentNamecomponentName, boolean force,
boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
......
try {
/* 當componentName為null時表示使用默認壁紙。
這里會將componentName參數改為默認壁紙的componentName */
if(componentName == null) {
/* 首先會嘗試從com.android.internal.R.string.default_wallpaper_component
中獲取默認壁紙的componentName。這個值的設置位于res/values/config.xml中,
開發者可以通過修改這個值設置默認壁紙*/
String defaultComponent = mContext.getString(
com.android.internal.R.string.default_wallpaper_component);
if (defaultComponent != null) {
componentName = ComponentName.unflattenFromString(defaultComponent);
}
/* 倘若在上述的資源文件中沒有指定一個默認壁紙,即default_wallpaper_component的
值被設置為@null),則使用ImageWallpaper代替默認壁紙。ImageWallpaper就是前文
所述的靜態壁紙 */
if (componentName == null) {
componentName = IMAGE_WALLPAPER;
}
}
/* 接下來WallpaperMangerService會嘗試從PackageManager中嘗試獲取ComponentName所
指定的Service的描述信息,獲取此信息的目的在于確認該Service是一個符合要求的壁紙服務 */
intserviceUserId = wallpaper.userId;
ServiceInfo si = mIPackageManager.getServiceInfo(componentName,
PackageManager.GET_META_DATA |
PackageManager.GET_PERMISSIONS,serviceUserId);
/* **① 第一個檢查,要求這個Service必須聲明其訪問權限為BIND_WALLPAPER。**這個簽名級的系
統權限這是為了防止壁紙服務被第三方應用程序啟動而產生混亂 */
if(!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) {
if (fromUser) {
throw new SecurityException(msg);
}
return false;
}
WallpaperInfo wi = null;
/* **② 第二個檢查,要求這個Service必須可以用來處理**
** android.service.wallpaper.WallpaperService這個Action。**
其檢查方式是從PackageManager中查詢所有可以處理
android.service.wallpaper.WallpaperService的服務,然后檢查即將啟動的服務
是否在PackageManager的查詢結果之中 */
Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
if(componentName != null && !componentName.equals(IMAGE_WALLPAPER)) {
// 獲取所有可以處理android.service.wallpaper.WallpaperService的服務信息
List<ResolveInfo> ris =
mIPackageManager.queryIntentServices(intent,
intent.resolveTypeIfNeeded(mContext.getContentResolver()),
PackageManager.GET_META_DATA, serviceUserId);
/* **③ 第三個檢查,要求這個Service必須在其meta-data中提供關于壁紙的描述信息。**如果
即將啟動的服務位于查詢結果之中,便可以確定這是一個壁紙服務。此時會創建一
個WallpaperInfo的實例以解析并存儲此壁紙服務的描述信息。壁紙服務的描述信息包含
了壁紙的開發者、縮略圖、簡單的描述文字以及用于對此壁紙進行參數設置的Activity的
名字等。壁紙開發者可以在AndroidManifest.xml中將一個包含了上述信息的xml文件設
置在名為android.service.wallpaper的meta-data中以提供這些信息 */
for (int i=0; i<ris.size(); i++) {
ServiceInfo rsi = ris.get(i).serviceInfo;
if (rsi.name.equals(si.name) &&
rsi.packageName.equals(si.packageName)){
try {
wi = newWallpaperInfo(mContext, ris.get(i));
} catch (XmlPullParserException e) {......}
break;
}
}
if (wi == null) {
/* wi為null表示即將啟動的服務沒有位于查詢結果之中,或者沒有提供必須的meta-data。
此時返回false表示綁定失敗 */
return false;
}
}
......
}
......
}
```
可見WallpaperManagerService要求被啟動的目標Service必須滿足以下三個條件:
- 該服務必須要以android.permission.BIND\_WALLPAPER作為其訪問權限。壁紙雖然是一個標準的Android服務,但是通過其他途徑(如第三方應用程序)啟動壁紙所在的服務是沒有意義的。因此Android要求作為壁紙的Service必須使用這個簽名級的系統權限進行訪問限制,以免被意外的應用程序啟動。
- 該服務必須被聲明為可以處理android.service.wallpaper.WallpaperService這個Action。WallpaperManagerService會使用這個Action對此服務進行綁定。
- 該服務必須在其AndroidManifest.xml中提供一個名為android.service.wallpaper的meta-data,用于提供動態壁紙的開發者、縮略圖與描述文字。
一旦目標服務滿足了上述條件,WallpaperManagerService就會著手進行目標服務的啟動與綁定。
參考setWallpaperComponentLocked()方法的后續代碼:
**WallpaperManagerService.java-->WallpaperManagerService.setWallpaperComponentLocked()**
```
boolean bindWallpaperComponentLocked(ComponentNamecomponentName, boolean force,
boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) {
...... // 檢查服務是否符合要求的代碼
/*
**① 創建一個WallpaperConnection。
**它不僅實現了ServiceConnection接口用于監聽和WallpaperService之間的連接狀態,
同時還實現了IWallpaperConnection.Stub,也就是說它支持跨進程通信。
在服務綁定成功后的WallpaperConnection.onServiceConnected()方法調用中,
WallpaperConnection的實例會被發送給WallpaperService,使其作為WallpaperService
向WallpaperManagerService進行通信的橋梁 */
WallpaperConnection newConn = new WallpaperConnection(wi, wallpaper);
// 為啟動壁紙服務準備Intent
intent.setComponent(componentName);
intent.putExtra(Intent.EXTRA_CLIENT_LABEL,
com.android.internal.R.string.wallpaper_binding_label);
intent.putExtra(Intent.EXTRA_CLIENT_INTENT,PendingIntent.getActivityAsUser(
mContext, 0,
Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
0, null, new UserHandle(serviceUserId)));
/* **② 啟動制定的壁紙服務。**當服務啟動完成后,剩下的啟動流程會在
WallpaperConnection.onServiceConnected()中繼續 */
if(!mContext.bindService(intent,
newConn,Context.BIND_AUTO_CREATE, serviceUserId)) {
}
// **③ 新的的壁紙服務啟動成功后,便通過detachWallpaperLocked()銷毀舊有的壁紙服務**
if(wallpaper.userId == mCurrentUserId && mLastWallpaper != null) {
detachWallpaperLocked(mLastWallpaper);
}
// **④ 將新的壁紙服務的運行信息保存到WallpaperData中**
wallpaper.wallpaperComponent = componentName;
wallpaper.connection = newConn;
/* 設置wallpaper.lastDiedTime。這個成員變量與其說描述壁紙的死亡時間戳,不如說是
描述其啟動的時間戳。它用來在壁紙服務意外斷開時(即壁紙服務非正常停止)檢查此壁紙服務
的存活時間。當存活時間小于一個特定的時長時將會認為這個壁紙的軟件質量不可靠
從而選擇使用默認壁紙,而不是重啟這個壁紙服務 */
wallpaper.lastDiedTime = SystemClock.uptimeMillis();
newConn.mReply = reply;
/* **④ 最后向WMS申請注冊一個WALLPAPER類型的窗口令牌。**這個令牌會在onServiceConnected()
之后被傳遞給WallpaperService用于作為后者添加窗口的通行證 */
try{
if (wallpaper.userId == mCurrentUserId) {
mIWindowManager.addWindowToken(newConn.mToken,
WindowManager.LayoutParams.TYPE_WALLPAPER);
mLastWallpaper = wallpaper;
}
} catch (RemoteException e) {}
} catch(RemoteException e) {}
returntrue;
}
```
bindWallpaperComponentLocked()主要做了如下幾件事情:
- 創建WallpaperConnection。由于實現了ServiceConnection接口,因此它將負責監聽WallpaperManagerService與壁紙服務之間的連接狀態。另外由于繼承了IWallpaperConnection.Stub,因此它具有跨進程通信的能力。在壁紙服務綁定成功后,WallpaperConnection實例會被傳遞給壁紙服務作為壁紙服務與WallpaperManagerService進行通信的橋梁。
- 啟動壁紙服務。通過Context.bindService()方法完成。可見啟動壁紙服務與啟動一個普通的服務沒有什么區別。
- 終止舊有的壁紙服務。
- 將屬于當前壁紙的WallpaperConnection實例、componentName機器啟動時間戳保存到WallpaperData中。
- 向WMS注冊WALLPAPER類型的窗口令牌。這個窗口令牌保存在WallpaperConnection.mToken中,并隨著WallpaperConnection的創建而創建。
僅僅將指定的壁紙服務啟動起來尚無法使得壁紙得以顯示,因為新啟動起來的壁紙服務由于沒有可用的窗口令牌而導致其無法添加窗口。WallpaperManagerService必須通過某種方法將窗口令牌交給壁紙服務才行。所以壁紙顯示的后半部分的流程將在WallpaperConnection.onServiceConnected()回調中繼續。同其他服務一樣,WallpaperManagerService會在這個回調之中獲得一個Binder對象。因此在進行onServiceConnected()方法的討論之前,必須了解WallpaperManagerService在這個回調中將會得到一個什么樣的Binder對象。
現在把分析目標轉移到WallpaperService中。和普通服務一樣,WallpaperService的啟動也會經歷onCreate()、onBind()這樣的生命周期回調。為了了解WallpaperManagerService可以從onServiceConnected()獲取怎樣的Binder對象,需要看下WallpaperService.onBind()的實現:
**WallpaperService.java-->WallpaperService.onBind()**
```
public final IBinder onBind(Intent intent) {
/*onBind()新建了一個IWallpaperServiceWrapper實例,并將
其返回給WallpaperManagerService */
returnnew IWallpaperServiceWrapper(this);
}
```
IWallpaperServiceWrapper類繼承自IWallpaperService.Stub。它保存了WallpaperService的實例,同時也實現了唯一的一個接口attach()。很顯然,當這個Binder對象返回給WallpaperManagerService之后,后者定會調用這個唯一的接口attach()以傳遞顯示壁紙所必須的包括窗口令牌在內的一系列的參數。
#### (2)向壁紙服務傳遞創建窗口所需的信息
重新回到WallpaperManagerService,當WallpaperService創建了IWallpaperServiceWrapper實例并返回后,WallpaperManagerService將會在WallpaperConnection.onServiceConnected()中收到回調。參考其實現:
**WallpaperManagerService.java-->WallpaperConnection.onServiceConnected()**
```
public void onServiceConnected(ComponentName name,IBinder service) {
synchronized (mLock) {
if (mWallpaper.connection == this) {
// 更新壁紙的啟動時間戳
mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
// **① 將WallpaperService傳回的IWallpaperService接口保存為mService**
mService = IWallpaperService.Stub.asInterface(service);
/* **② 綁定壁紙服務。**attachServiceLocked()會調用IWallpaperService.attach()
方法以將壁紙服務創建窗口所需的信息傳遞過去 */
attachServiceLocked(this, mWallpaper);
// **③ 保存當前壁紙的運行狀態到文件系統中,以便在系統重啟或發生用戶切換時可以恢復**
saveSettingsLocked(mWallpaper);
}
}
}
進一步地,attachServiceLocked()方法會調用IWallpaperService.attach()方法,將創建壁紙窗口所需的信息發送給壁紙服務。
[WallpaperManagerService.java-->WallpaperManagerService.attachServiceCLocked()]
void attachServiceLocked(WallpaperConnection conn,WallpaperData wallpaper) {
try {
/* 調用IWallpaperService的唯一接口attach(),將創建壁紙窗口所需要的參數傳遞
給WallpaperService */
conn.mService.attach(conn, conn.mToken,
WindowManager.LayoutParams.TYPE_WALLPAPER, false,
wallpaper.width, wallpaper.height);
} catch(RemoteException e) {......}
}
```
attach()方法的參數很多,它們的意義如下:
- conn即WallpaperConnection,WallpaperService將通過它向WallpaperManagerService進行通信。WallpaperConnection繼承自IWallpaperConnection,只提供了兩個接口的定義,即attachEngine()以及engineShown()。雖說WallpaperManager是WallpaperManagerService向外界提供的標準接口,但是這里仍然選擇使用WallpaperConnection實現這兩個接口的原因是由于attachEngine()以及engineShown()是只有WallpaperService才需要用到而且是它與 WallpaperManagerService之間比較底層且私密的交流,將它們的實現放在通用的接口WallpaperManager中顯然并不合適。這兩個接口中比較重要的當屬attachEngine()了。如前文所述,Engine類是實現壁紙的核心所在,而WallpaperService只是一個用于承載壁紙的運行的容器而已。因此相對于WallpaperService,Engine是WallpaperManagerService更加關心的對象。所以當WallpaperService完成了Engine對象的創建之后,就會通過attachEngine()方法將Engine對象的引用交給WallpaperManagerService。
- conn.mToken就是在bindWallpaperComponent()方法中向WMS注冊過的窗口令牌。是WallpaperService有權添加壁紙窗口的憑證。
- WindowManager.LayoutParams.TYPE\_WALLPAPER指明了WallpaperService需要添加TYPE\_WALLPAPER類型的窗口。讀者可能會質疑這個參數的意義:壁紙除了是TYPE\_WALLPAPER類型以外難道還有其他的可能么?的確在實際的壁紙顯示中WallpaperService必然需要使用TYPE\_WALLPAPER類型添加窗口。但是有一個例外,即壁紙預覽。在LivePicker應用中選擇一個動態壁紙時,首先會使得用戶對選定的壁紙進行預覽。這一預覽并不是真的將壁紙設置給了WallpaperManagerService,而是LivePicker應用自行啟動了對應的壁紙服務,并要求壁紙服務使用TYPE\_APPLICATION\_MEDIA\_OVERLAY類型創建窗口。這樣一來,壁紙服務所創建的窗口將會以子窗口的形式襯在LivePicker的窗口之下,從而實現了動態壁紙的預覽。
- false的參數名是isPreview. 用以指示啟動壁紙服務的意圖。當被實際用作壁紙時取值為false,而作為預覽時則為true。僅當LivePicker對壁紙進行預覽時才會使用true作為isPreview的取值。壁紙服務可以根據這一參數的取值對自己的行為作出調整。
當WallpaperManagerService向WallpaperService提供了用于創建壁紙窗口的足夠的信息之后,WallpaperService便可以開始著手進行Engine對象的創建了。
#### (3)Engine的創建
調用IWallpaperService.attach()是WallpaperManagerService在壁紙服務啟動后第一次與壁紙服務進行聯系。參考其實現:
```
[WallpaperService.java-->IWallpaperServiceWrapper.attach()]
public void attach(IWallpaperConnection conn,IBinder windowToken,
intwindowType, boolean isPreview, int reqWidth, int reqHeight) {
// 使用WallpaperManagerService提供的參數構造一個IWallpaperEngineWarapper實例
newIWallpaperEngineWrapper(mTarget, conn, windowToken,
windowType, isPreview, reqWidth, reqHeight);
}
```
顧名思義,在attach()方法中所創建的IWallpaperEngineWrapper將會創建并封裝Engine實例。IWallpaperEngineWrapper繼承自IWallpaperEngine.Stub,因此它也支持跨Binder調用。在隨后的代碼分析中可知,它將會被傳遞給WallpaperManagerService,作為WallpaperManagerService與Engine進行通信的橋梁。
另外需要注意的是,attach()方法的實現非常奇怪,它直接創建一個實例但是并沒有將這個實例賦值給某一個成員變量,在attach()方法結束時豈不是會被垃圾回收?不難想到,在IWallpaperEngineWrapper的構造函數中一定有些動作可以使得這個實例不被釋放。參考其實現:
**WallpaperService.java-->IWallpaperEngineWrapper.IWallpaperEngineWrapper()**
```
IWallpaperEngineWrapper(WallpaperService context,
IWallpaperConnection conn, IBinder windowToken,
intwindowType, boolean isPreview, int reqWidth, int reqHeight) {
/* 創建一個HandlerCaller。
HandlerCaller是Handler的一個封裝,而它與Handler的區別是額外提供了
一個executeOrSendMessage()方法。當開發者在HandlerCaller所在的線程
執行此方法時會使得消息的處理函數立刻得到執行,在其他線程中執行此方法的效果
則與Handler.sendMessage()別無二致。除非閱讀代碼時遇到這個方法,讀者
只需要將其理解為Handler即可。
注意意通過其構造函數的參數可知HandlerCaller保存了IWallpaperEngineWrapper的實例 */
mCaller= new HandlerCaller(context,
mCallbackLooper != null
? mCallbackLooper : context.getMainLooper(),
this);
// 將WallpaperManagerService所提供的參數保存下來
mConnection = conn; // conn即是WallpaperManagerService中的WallpaperConnection
mWindowToken = windowToken;
mWindowType = windowType;
mIsPreview = isPreview;
mReqWidth = reqWidth;
mReqHeight = reqHeight;
// 發送DO_ATTACH消息。后續的流程轉到DO_ATTACH消息的處理中進行
Messagemsg = mCaller.obtainMessage(DO_ATTACH);
mCaller.sendMessage(msg);
}
```
注意 在這里貌似并沒有保存新建的IWallpaperEngineWrapper實例,它豈不是有可能在DO\_ATTACH消息執行前就被Java的垃圾回收機制回收了?其實不是這樣。HandlerCaller的構造函數以及最后的sendMessage()操作使得這個IWallpaperEngineWrapper的實例得以堅持到DO\_ATTACH消息可以得到處理的時刻。sendMessage()方法的調用使得Message被目標線程的MessageQueue引用,并且對應的Handler的被Message引用,而這個Handler是HandlerCaller的內部類,因此在Handler中有一個隱式的指向HandlerCaller的引用,最后在HandlerCaller中又存在著IWallpaperEngineWrapper的引用。因此IWallpaperEngineWrapper間接地被HandlerCaller所在線程的MessageQueue所引用著,因此在完成DO\_ATTACH消息的處理之前,IWallpaperEngineWrapper并不會被回收。雖然這是建立在對Java引用以及Handler工作原理的深刻理解之上所完成的精妙實現,但是它確實已經接近危險的邊緣了。
在這里所創建的mCaller具有十分重要的地位。它是一個重要的線程調度器,所有壁紙相關的操作都會以消息的形式發送給mCaller,然后在IWallpaperEngineWrapper的executeMessage()方法中得到處理,從而這些操作轉移到mCaller所在的線程上進行(如壁紙繪制、事件處理等)。可以說mCaller的線程就是壁紙的工作線程。默認情況下這個mCaller運行在壁紙服務的主線程上即context.getMainLooper()。不過當WallpaperService.mCallbackLooper不為null時會運行在mCallbackLooper所在的線程。mCaller運行在壁紙服務的主線程上聽起來十分合理,然而提供手段以允許其運行在其他線程的做法卻有些意外。其實這是為了滿足一種特殊的需求,以ImageWallper壁紙服務為例,它是SystemUI的一部分而SystemUI的主線程主要用來作為狀態欄、導航欄的管理與繪制的場所,換句話說其主線程的工作已經比較繁重了。因此ImageWallpaper可以通過這一手段將壁紙的工作轉移到另外一個線程中進行。不過因為這一機制可能帶來同步上的問題,因此在Android 4.4及后續版本中被廢除了。
接下來分析DO\_ATTACH消息的處理:
**WallpaperService.java-->IWallpaperEngineWrapper.executeMessage()**
```
public void executeMessage(Message message) {
switch(message.what) {
caseDO_ATTACH: {
try {
/* **① 把IWallpaperEngineWrapper實例傳遞給WallpaperConnection進行保存。**
至此這個實例便名花有主,再也不用擔心被回收了,而且WallpaperManagerService
還可以通過它與實際的Engine進行通信 */
mConnection.attachEngine(this);
} catch (RemoteException e) {}
/* **② 通過onCreateEngine()方法創建一個Engine。**
onCreateEngine()是定義在WallpaperService中的一個抽象方法。
WallpaperService的實現者需要根據自己的需要返回一個自定義的Engine的子類 */
Engine engine = onCreateEngine();
mEngine = engine;
/* **③ 將新建的Engine添加到WallpaperService.mActiveEngines列表中。**
讀者可能會比較奇怪,為什么是列表?難道一個Wallpaper可能會有多個Engine么?
這個奇怪之處還是壁紙預覽所引入的。當壁紙A已經被設置為當前壁紙之時,系統中會存
在一個它所對應的WallpaperService,以及在其內部會存在一個Engine。
此時當LivePicker或其他壁紙管理工具預覽壁紙A時,它所對應的WallpaperService
仍然只有一個,但是在其內部會變成兩個Engine。
這一現象更能說明,WallpaperService僅僅是提供壁紙運行的場所,而Engine才是真正
的壁紙的實現 */
mActiveEngines.add(engine);
// **④ 最后engine.attach()將會完成窗口的創建、第一幀的繪制等工作**
engine.attach(this);
return;
}
}
}
```
正如前文所述,作為擁有跨Binder調用的IWallpaperEngineWrapper通過attachEngine()方法將自己傳遞給了WallpaperConnection,后者將其保存在WallpaperConnection.mEngine成員之中。從此之后,WallpaperManagerService便可以通過WallpaperConnection.mEngine與壁紙服務進程中的IWallpaperEngineWrapper進行通信,而IWallpaperEngineWrapper進一步將來自WallpaperManagerService中的請求或設置轉發給Engine對象,從而實現了WallpaperManagerService對壁紙的控制。
到目前為止,WallpaperManagerService與壁紙服務之間已經出現了三個用于跨Binder通信的對象。它們分別是:
- IWallpaperService,實現在壁紙服務進程之中,它所提供的唯一的方法attach()用于在壁紙服務啟動后接收窗口創建所需的信息,或者說為了完成壁紙的初始化工作。除此之外IWallpaperService不負責任何功能,WallpaperManagerService對壁紙進行的請求與設置都交由在attach()的過程中所創建的IWallpaperEngineWrapper實例完成。
- WallpaperConnection,實現在WallpaperManagerService中,并通過IWallpaperService.attach()方法傳遞給了IWallpaperEngineWrapper。壁紙服務通過WallpaperConnection的attachEngine()方法將IWallpaperEngineWrapper實例傳遞給WallpaperManagerService進行保存。另外壁紙服務還通過它的engineShown()方法將壁紙顯示完成的事件通知給WallpaperManagerService。
- IWallpaperEngineWrapper,實現在壁紙進程中。Engine實例是壁紙實現的核心所在。作為Engine實例的封裝者,它是WallpaperManagerService對Engine進行請求或設置的唯一接口。
總體來說,IWallpaperService與WallpaperConnection主要服務于壁紙的創建階段,而IWallpaperEngineWrapper則用于在壁紙的運行階段對Engine進行操作與設置。
說明 按照常規的思想來推斷,WallpaperManagerService與WallpaperService之間應該僅僅需要IWallpaperService提供接口對壁紙進行操作與設置。為什么要增加一個IWallpaperEngineWrapper呢?這得從WallpaperService與Engine之間的關系說起。IWallpaperService在WallpaperManagerService看來表示的是WallpaperService,而IWallpaperEngineWrapper則表示的是Engine。WallpaperService是Engine運行的容器,因此它所提供的唯一的方法attach()用來在WallpaperService中創建新的Engine實例(由創建一個IWallpaperEngineWrapper實例來完成)。Engine則是壁紙的具體實現,因此IWallpaperEngineWrapper所提供的方法用來對壁紙進行操作與設置。從這個意義上來講IWallpaperService與IWallpaperEngineWrapper的同時存在是合理的。另外,將IWallpaperService與IWallpaperEngineWrapper分開還有著簡化實現的意義。從DO\_ATTACH消息的處理過程可知,WallpaperService中可以同時運行多個Engine實例。而WallpaperManagerService或LivePicker所關心的只是某一個Engine,而不是WallpaperService中的所有Engine,因此相對于使用IWallpaperService的接口時必須在參數中指明所需要操作的Engine,直接操作IWallpaperEngineWrapper更加簡潔直接。
Engine創建完畢之后會通過Engine.attach()方法完成Engine的初始化工作。參考其代碼:
**WallpaperService.java-->Engine.attach()**
```
void attach(IWallpaperEngineWrapper wrapper) {
......
// 保存必要的信息
mIWallpaperEngine = wrapper;
mCaller= wrapper.mCaller;
mConnection = wrapper.mConnection;
mWindowToken = wrapper.mWindowToken;
/* **① mSurfaceHolder是一個BaseSurfaceHolder類型的內部類的實例。**
Engine對其進行了簡單的定制。開發者可以通過mSurfaceHolder定制所需要的Surface類型 */
mSurfaceHolder.setSizeFromLayout();
mInitializing = true;
// 獲取WindowSession,用于與WMS進行通信
mSession= WindowManagerGlobal.getWindowSession(getMainLooper());
//mWindow是IWindow的實現,窗口創建之后它將用于接收來自WMS的回調
mWindow.setSession(mSession);
//Engine需要監聽屏幕狀態。這是為了保證在屏幕關閉之后,動態壁紙可以停止動畫的渲染以節省電量
mScreenOn =
((PowerManager)getSystemService(Context.POWER_SERVICE)).isScreenOn();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(mReceiver, filter);
/* **② 調用Engine.onCreate()。**
Engine的子類往往需要重寫此方法以修改mSurfaceHolder的屬性,如像素格式,尺寸等。
注意此時尚未創建窗口,在這里所設置的SurfaceHolder的屬性將會在創建窗口時生效 */
onCreate(mSurfaceHolder);
mInitializing = false;
mReportedVisible = false;
/* **③ 最后updateSurface將會根據SurfaceHolder的屬性創建窗口以及Surface,并進行**
** 壁紙的第一次繪制** */
updateSurface(false, false, false);
}
```
Engine.attach()方法執行的結束標志著壁紙啟動工作的完成,至此在最后的updateSurface()方法結束之后新的壁紙便顯示出來了。
#### (4)壁紙的創建流程
可見,壁紙的創建過程比較復雜。在這個過程中存在著多個Binder對象之間的互相調用。因此有必要對此過程進行一個簡單的整理:
- 首先,壁紙管理程序(如LivePicker)調用IWallpaperManager.setWallpaperComponent()要求WallpaperManagerService設置指定的壁紙
- WallpaperManagerService通過調用bindWallpaperComponentLocked()將給定的壁紙服務啟動起來。同時舊有的壁紙服務會被終止。
- WallpaperManagerService成功連接壁紙服務后,調用壁紙服務的attach()方法將窗口令牌等參數交給壁紙服務。
- 壁紙服務響應attach()的調用,創建一個Engine。
- Engine的updateSurface()方法將會創建壁紙窗口及Surface,并進行壁紙的繪制。
而在這個過程中,WallpaperManagerService中存在如下重要的數據結構:
- WallpaperInfo,存儲了動態壁紙的開發者、縮略圖與描述信息。這個數據結構創建于WallpaperManagerService.bindWallpaperComponentLocked()方法,其內容來自于壁紙所在應用程序的AndroidManifest.xml中名為android.service.wallpaper的meta-data。
- WallpaperConnection,它不僅僅是壁紙服務與WallpaperManagerService進行通信的渠道,它同時也保存了與壁紙服務相關的重要的運行時信息,如IWallpaperService、IWallpaperEngineWrapper、WallpaperInfo以及用于創建窗口所需的窗口令牌。WallpaperConnection創建于WallpaperManagerService.bindWallpaperComponentLocked()方法。
- WallpaperData,它保存了一個壁紙在WallpaperManagerService中可能用到的所有信息,包括壁紙服務的ComponentName,WallpaperConnection,壁紙服務的啟動時間等。WallpaperData被保存在一個名為mWallpaperMap的SparseArray中,而且設備中每一個用戶都會擁有一個固定的WallpaperData實例。當前用戶進行壁紙切換時會更新WallpaperData的內容,而不是新建一個WallpaperData實例。另外,WallpaperData中還保存了與靜態壁紙相關的一些信息,關于靜態壁紙的內容將在8.3節進行介紹。
壁紙的創建過程同時體現了壁紙服務與WallpaperManagerService之間的關系,如圖8-1所示。
:-: 
圖 8 - 1 壁紙服務與WallpaperManagerService之間的關系
- 前言
- 推薦序
- 第1章 開發環境部署
- 1.1獲取Android源代碼
- 1.2Android的編譯
- 1.3在IDE中導入Android源代碼
- 1.3.1將Android源代碼導入Eclipse
- 1.3.2將Android源代碼導入SourceInsight
- 1.4調試Android源代碼
- 1.4.1使用Eclipse調試Android Java源代碼
- 1.4.2使用gdb調試Android C/C 源代碼
- 1.5本章小結
- 第2章 深入理解Java Binder和MessageQueue
- 2.1概述
- 2.2Java層中的Binder分析
- 2.2.1Binder架構總覽
- 2.2.2初始化Java層Binder框架
- 2.2.3窺一斑,可見全豹乎
- 2.2.4理解AIDL
- 2.2.5Java層Binder架構總結
- 2.3心系兩界的MessageQueue
- 2.3.1MessageQueue的創建
- 2.3.2提取消息
- 2.3.3nativePollOnce函數分析
- 2.3.4MessageQueue總結
- 2.4本章小結
- 第3章 深入理解AudioService
- 3.1概述
- 3.2音量管理
- 3.2.1音量鍵的處理流程
- 3.2.2通用的音量設置函數setStreamVolume()
- 3.2.3靜音控制
- 3.2.4音量控制小結
- 3.3音頻外設的管理
- 3.3.1 WiredAccessoryObserver 設備狀態的監控
- 3.3.2AudioService的外設狀態管理
- 3.3.3音頻外設管理小結
- 3.4AudioFocus機制的實現
- 3.4.1AudioFocus簡單的例子
- 3.4.2AudioFocus實現原理簡介
- 3.4.3申請AudioFocus
- 3.4.4釋放AudioFocus
- 3.4.5AudioFocus小結
- 3.5AudioService的其他功能
- 3.6本章小結
- 第4章 深入理解WindowManager-Service
- 4.1初識WindowManagerService
- 4.1.1一個從命令行啟動的動畫窗口
- 4.1.2WMS的構成
- 4.1.3初識WMS的小結
- 4.2WMS的窗口管理結構
- 4.2.1理解WindowToken
- 4.2.2理解WindowState
- 4.2.3理解DisplayContent
- 4.3理解窗口的顯示次序
- 4.3.1主序、子序和窗口類型
- 4.3.2通過主序與子序確定窗口的次序
- 4.3.3更新顯示次序到Surface
- 4.3.4關于顯示次序的小結
- 4.4窗口的布局
- 4.4.1從relayoutWindow()開始
- 4.4.2布局操作的外圍代碼分析
- 4.4.3初探performLayoutAndPlaceSurfacesLockedInner()
- 4.4.4布局的前期處理
- 4.4.5布局DisplayContent
- 4.4.6布局的階段
- 4.5WMS的動畫系統
- 4.5.1Android動畫原理簡介
- 4.5.2WMS的動畫系統框架
- 4.5.3WindowAnimator分析
- 4.5.4深入理解窗口動畫
- 4.5.5交替運行的布局系統與動畫系統
- 4.5.6動畫系統總結
- 4.6本章小結
- 第5章 深入理解Android輸入系統
- 5.1初識Android輸入系統
- 5.1.1getevent與sendevent工具
- 5.1.2Android輸入系統簡介
- 5.1.3IMS的構成
- 5.2原始事件的讀取與加工
- 5.2.1基礎知識:INotify與Epoll
- 5.2.2 InputReader的總體流程
- 5.2.3 深入理解EventHub
- 5.2.4 深入理解InputReader
- 5.2.5原始事件的讀取與加工總結
- 5.3輸入事件的派發
- 5.3.1通用事件派發流程
- 5.3.2按鍵事件的派發
- 5.3.3DispatcherPolicy與InputFilter
- 5.3.4輸入事件的派發總結
- 5.4輸入事件的發送、接收與反饋
- 5.4.1深入理解InputChannel
- 5.4.2連接InputDispatcher和窗口
- 5.4.3事件的發送
- 5.4.4事件的接收
- 5.4.5事件的反饋與發送循環
- 5.4.6輸入事件的發送、接收與反饋總結
- 5.5關于輸入系統的其他重要話題
- 5.5.1輸入事件ANR的產生
- 5.5.2 焦點窗口的確定
- 5.5.3以軟件方式模擬用戶操作
- 5.6本章小結
- 第6章 深入理解控件系統
- 6.1 初識Android的控件系統
- 6.1.1 另一種創建窗口的方法
- 6.1.2 控件系統的組成
- 6.2 深入理解WindowManager
- 6.2.1 WindowManager的創建與體系結構
- 6.2.2 通過WindowManagerGlobal添加窗口
- 6.2.3 更新窗口的布局
- 6.2.4 刪除窗口
- 6.2.5 WindowManager的總結
- 6.3 深入理解ViewRootImpl
- 6.3.1 ViewRootImpl的創建及其重要的成員
- 6.3.2 控件系統的心跳:performTraversals()
- 6.3.3 ViewRootImpl總結
- 6.4 深入理解控件樹的繪制
- 6.4.1 理解Canvas
- 6.4.2 View.invalidate()與臟區域
- 6.4.3 開始繪制
- 6.4.4 軟件繪制的原理
- 6.4.5 硬件加速繪制的原理
- 6.4.6 使用繪圖緩存
- 6.4.7 控件動畫
- 6.4.8 繪制控件樹的總結
- 6.5 深入理解輸入事件的派發
- 6.5.1 觸摸模式
- 6.5.2 控件焦點
- 6.5.3 輸入事件派發的綜述
- 6.5.4 按鍵事件的派發
- 6.5.5 觸摸事件的派發
- 6.5.6 輸入事件派發的總結
- 6.6 Activity與控件系統
- 6.6.1 理解PhoneWindow
- 6.6.2 Activity窗口的創建與顯示
- 6.7 本章小結
- 第7章 深入理解SystemUI
- 7.1 初識SystemUI
- 7.1.1 SystemUIService的啟動
- 7.1.2 狀態欄與導航欄的創建
- 7.1.3 理解IStatusBarService
- 7.1.4 SystemUI的體系結構
- 7.2 深入理解狀態欄
- 7.2.1 狀態欄窗口的創建與控件樹結構
- 7.2.2 通知信息的管理與顯示
- 7.2.3 系統狀態圖標區的管理與顯示
- 7.2.4 狀態欄總結
- 7.3 深入理解導航欄
- 7.3.1 導航欄的創建
- 7.3.2 虛擬按鍵的工作原理
- 7.3.3 SearchPanel
- 7.3.4 關于導航欄的其他話題
- 7.3.5 導航欄總結
- 7.4 禁用狀態欄與導航欄的功能
- 7.4.1 如何禁用狀態欄與導航欄的功能
- 7.4.2 StatusBarManagerService對禁用標記的維護
- 7.4.3 狀態欄與導航欄對禁用標記的響應
- 7.5 理解SystemUIVisibility
- 7.5.1 SystemUIVisibility在系統中的漫游過程
- 7.5.2 SystemUIVisibility發揮作用
- 7.5.3 SystemUIVisibility總結
- 7.6 本章小結
- 第8章 深入理解Android壁紙
- 8.1 初識Android壁紙
- 8.2深入理解動態壁紙
- 8.2.1啟動動態壁紙的方法
- 8.2.2壁紙服務的啟動原理
- 8.2.3 理解UpdateSurface()方法
- 8.2.4 壁紙的銷毀
- 8.2.5 理解Engine的回調
- 8.3 深入理解靜態壁紙-ImageWallpaper
- 8.3.1 獲取用作靜態壁紙的位圖
- 8.3.2 靜態壁紙位圖的設置
- 8.3.3 連接靜態壁紙的設置與獲取-WallpaperObserver
- 8.4 WMS對壁紙窗口的特殊處理
- 8.4.1 壁紙窗口Z序的確定
- 8.4.2 壁紙窗口的可見性
- 8.4.3 壁紙窗口的動畫
- 8.4.4 壁紙窗口總結
- 8.5 本章小結