android壁纸服务,android壁纸服务流程浅析
由于最近工作需要了解android的壁紙機制,當時急切地想在網上找點資料來了解WallpaperManager.setResource()之后的流程,但網上僅有一點不全的東西,其它的全是粘貼復制那點不全的內容,真是捉急。今天自己來寫下關于設置壁紙的流程,希望后來者不用像本人一樣找不到能用的資料。
假設調用WallpaperManager.setResource()方法來設置壁紙(還有wallpaperManager的其它方法也可以設置壁紙,但流程是一樣的),
public void setResource(int resid) throws IOException {
if (sGlobals.mService == null) {
Log.w(TAG, "WallpaperService not running");
return;
}
try {
Resources resources = mContext.getResources();
/* Set the wallpaper to the default values */
ParcelFileDescriptor fd = sGlobals.mService.setWallpaper(
"res:" + resources.getResourceName(resid));
if (fd != null) {
FileOutputStream fos = null;
try {
fos = new ParcelFileDescriptor.AutoCloseOutputStream(fd);
setWallpaper(resources.openRawResource(resid), fos);
} finally {
if (fos != null) {
fos.close();
}
}
}
} catch (RemoteException e) {
}
}
代碼里牽涉到sGlobals.mService,這個mService是WallpaperManagerService的實例對象,它是通過下面這兩句話來初始化的。
IBinder b = ServiceManager.getService(Context.WALLPAPER_SERVICE);
mService = IWallpaperManager.Stub.asInterface(b);
可以去查看WallpaperManagerService的代碼,發現WallpaperManagerService正是實現了IWallpaperManager.Stub,而ServiceManager中正是以關鍵字Context.WALLPAPER_SERVICE保存的WallpaperManagerService實例,由以上兩點可以得知mService正是WallpaperManagerService的實例對象。WallpaperManagerService的setWallpaper其實主要作用是取得一個ParcelFileDescriptor對象,這個對象指向了/data/system/user/0/wallpaper這個文件,接著根據ParcelFileDescriptor生成文件輸出流fos,再調用resources.openRawResource(resid)獲得源壁紙的文件輸入流,傳入WallpaperManager的下一個方法,setWallpaper(InputStream,FileOutputStream)。
而在這個方法中主要代碼為
while ((amt=data.read(buffer)) > 0) {
fos.write(buffer, 0, amt);
if (mSaveBakFlag && null != fos_bak) {
fos_bak.write(buffer, 0, amt);
}
}
到了這里可能有讀者納悶了,這不是文件的復制嗎?對,WallpaperManager.setResource方法到最后就是進行文件復制而已,把源壁紙圖片復制到之前曾經提到過的
/data/system/user/0/wallpaper這個文件中(可能不同的廠商做的定制化rom文件路徑會有不一樣,但大體相同),那接下來如何在桌面繪制所設壁紙呢?這就與之前提到的WallpaperManagerService和另一個新類ImageWallpaper有關。(如何有讀者對ParcelFileDescriptor類不大理解,可以不求甚解把它當成一個類似于File的類就可以了)
WallpaperManagerService中含有一個WallpaperObserver內部類,這個內部類繼承自FileObserver,FileObserver主要用于對文件進行監聽,如果被監聽的文件被重寫了或刪除或其它操作它都能監聽并觸發相應的方法。
public WallpaperObserver(WallpaperData wallpaper) {
super(getWallpaperDir(wallpaper.userId).getAbsolutePath(),
CLOSE_WRITE | DELETE | DELETE_SELF);
mWallpaperDir = getWallpaperDir(wallpaper.userId);
mWallpaper = wallpaper;
mWallpaperFile = new File(mWallpaperDir, WALLPAPER);
}
WallpaperObserver在構造方法中指明了它要監聽的文件及對文件的操作,
super(getWallpaperDir(wallpaper.userId).getAbsolutePath(),CLOSE_WRITE | DELETE | DELETE_SELF);
getWallpaperDir(wallpaper.userId).getAbsolutePath()是它是監聽的文件路徑/data/system/user/0/,這個路徑包含wallpaperManager復制的wallpaper文件,所以一旦通過wallpaperManager設置壁紙,文件監聽器WallpaperObserver就會監聽到關執行相應方法。
public void onEvent(int event, String path) {
if (path == null) {
return;
}
synchronized (mLock) {
// changing the wallpaper means we'll need to back up the new one
long origId = Binder.clearCallingIdentity();
BackupManager bm = new BackupManager(mContext);
bm.dataChanged();
Binder.restoreCallingIdentity(origId);
File changedFile = new File(mWallpaperDir, path);
if (mWallpaperFile.equals(changedFile)) {
notifyCallbacksLocked(mWallpaper);
if (mWallpaper.wallpaperComponent == null || event != CLOSE_WRITE
|| mWallpaper.imageWallpaperPending) {
if (event == CLOSE_WRITE) {
mWallpaper.imageWallpaperPending = false;
}
bindWallpaperComponentLocked(mWallpaper.imageWallpaperComponent, true,
false, mWallpaper);
saveSettingsLocked(mWallpaper);
}
}
}
}
在bindWallpaperComponentLocked方法中傳入的第一個參數是mWallpaer.imageWallpaperComponent,這個Component指向ImageWallpaper,這個方法太過復雜,關鍵代碼在于
mContext.bindService(intent, newConn, Context.BIND_AUTO_CREATE, serviceUserId)這句話的執行,
intent:Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
intent.setComponent(componentName);? componentName即是之前傳入的imageWallpaperComponent,表明這個intent最終會被解析定位到ImageWallpaper這個類中,ImageWallpaper繼承了WallpaperService。接下來看newConn的代碼
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mLock) {
if (mWallpaper.connection == this) {
mWallpaper.lastDiedTime = SystemClock.uptimeMillis();
mService = IWallpaperService.Stub.asInterface(service);
attachServiceLocked(this, mWallpaper);
// XXX should probably do saveSettingsLocked() later
// when we have an engine, but I'm not sure about
// locking there and anyway we always need to be able to
// recover if there is something wrong.
saveSettingsLocked(mWallpaper);
}
}
}
注意這句話,mService = IWallpaperService.Stub.asInterface(service),而wallpaperService中的內部類IWallpaperServiceWrapper實現了IWallpaperService.stub,
class IWallpaperServiceWrapper extends IWallpaperService.Stub
所以根據之前的intent和IWallpaperService.Stub可知最終bindService會得到一個WallpaperService中的內部類IWallpaerServiceWrapper的引用(注意到但WallpaperService是一個abstract類,而ImageWallpaper是WallpaerService的子類,所以最終是得到的是ImageWallpaper中IWallpaerServiceWrapper的引用),看上面的onServiceConnected方法,主要執行兩個方法。attachServiceLocked?????????? saveSettingsLocked。
void attachServiceLocked(WallpaperConnection conn, WallpaperData wallpaper) {
try {
conn.mService.attach(conn, conn.mToken,
WindowManager.LayoutParams.TYPE_WALLPAPER, false,
wallpaper.width, wallpaper.height);
} catch (RemoteException e) {
Slog.w(TAG, "Failed attaching wallpaper; clearing", e);
if (!wallpaper.wallpaperUpdating) {
bindWallpaperComponentLocked(null, false, false, wallpaper);
}
}
}
attachServiceLocked方法將執行mService中的attach方法,轉到WallpaperService查看attach方法,發現此方法將生成一個IWallpaperEngineWrapper對象(之前說bindService將定位到ImageWallpaper,怎么現在去WallpaperService去查看代碼呢?因為ImageWallaper中沒有attach方法,自然只能去其父類中查找相關的方法),在
IWallpaperEngineWrapper構造方法中將發送一個message
Message msg = mCaller.obtainMessage(DO_ATTACH);
mCaller.sendMessage(msg);
在message的處理代碼中將創建繪圖引擎并調用?engine.attach(this),繼續追查engine.attach方法(注意ImageWallpaper中有內部類繼承Engine類),發現在些方法中會調用Engine.onCreate(mSurfaceHolder),查看ImageWallpaper中的onCreate(mSurfaceHolder),接下來有一系列的邏輯來進行壁紙的繪制工作。
至些,文章里邊提到了四個類,每個類都已經聯系起來了。接著看ImageWallaper的繪圖工作吧。
在onCreate(SurfaceHolder)方法中執行updateSurfaceSize方法,用于更新surface的大小,在updateSurfaceSize方法中調用WallpaperManager.isUseSingleWallPaper()方法確定是繪制單屏壁紙還是寬屏壁紙,updateSurfaceSize方法執行完后,onSurfaceChanged這個回調函數自動執行,在這個回調函數中再執行drawFrameLocked方法,在drawFrameLocked中就是真正繪制地方,在drawFrameLocked方法中將首先獲取設置好的壁紙圖片,再由是否執行硬件加速來決定是用opengl繪圖還是用canvas繪圖。至此,設置壁紙的整個流程已然介紹完畢。 ????? 由于本人對于繪圖這一塊還不是很熟悉,所以在ImageWallpaper的繪圖工作介紹的不是很詳細,如有哪位清楚的歡迎補充。
總結
以上是生活随笔為你收集整理的android壁纸服务,android壁纸服务流程浅析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java常用算法分析和实现 amp,Ja
- 下一篇: AutoCAD软件应用