詳解Android壁紙服務(wù)的啟動(dòng)過(guò)程
壁紙基礎(chǔ)
android中的壁紙分為動(dòng)態(tài)壁紙和靜態(tài)壁紙兩種,兩種類(lèi)型的壁紙都以Service的類(lèi)型運(yùn)行在系統(tǒng)后臺(tái)。
- 靜態(tài)壁紙:僅以圖片的形式進(jìn)行展示對(duì)于靜態(tài)壁紙,可以使用WallpaperManager中的getDrawable()等接口獲取到當(dāng)前的bitmap圖像。
- 動(dòng)態(tài)壁紙:顯示的內(nèi)容為動(dòng)態(tài)的內(nèi)容,同時(shí)可以對(duì)用戶(hù)的操作做出響應(yīng)對(duì)于動(dòng)態(tài)壁紙的實(shí)時(shí)圖像,是沒(méi)辦法通過(guò)android中原生的接口獲取到,需要獲取到動(dòng)態(tài)壁紙的圖像得自己修改源碼。
壁紙實(shí)現(xiàn)時(shí)涉及的幾個(gè)主要的類(lèi):
- WallpaperService及其內(nèi)部類(lèi)Engine:壁紙?jiān)赪allpaperService這個(gè)服務(wù)中運(yùn)行,當(dāng)需要實(shí)現(xiàn)自己的壁紙時(shí),繼承和實(shí)現(xiàn)這個(gè)類(lèi),是首先需要做的。Engine是WallpaperService中的一個(gè)內(nèi)部類(lèi),實(shí)現(xiàn)了壁紙服務(wù)窗口的創(chuàng)建以及Surface的維護(hù),同時(shí)Engine內(nèi)部類(lèi)還提供了onVisibilityChanged(),onCommand()等回調(diào)方法,用于可見(jiàn)狀態(tài)變化和用戶(hù)觸摸事件等。Engine類(lèi)因此也是壁紙實(shí)現(xiàn)的核心類(lèi),實(shí)現(xiàn)和重寫(xiě)其接口在開(kāi)發(fā)中也相當(dāng)重要。
- WallpaperManagerService和WallpaperManager:WallpaperManagerService用于管理壁紙的運(yùn)行與切換,并通過(guò)WallpaperManager對(duì)外界提供操作壁紙的接口。
- WindowMangerService:該類(lèi)用于計(jì)算壁紙窗口的Z序,可見(jiàn)性以及為壁紙窗口應(yīng)用動(dòng)畫(huà)。
壁紙服務(wù)的兩種啟動(dòng)場(chǎng)景
非首次重啟壁紙服務(wù)啟動(dòng)流程
SystemService進(jìn)程啟動(dòng)時(shí),會(huì)啟動(dòng)各種系統(tǒng)服務(wù)。在該類(lèi)的startOtherServices()方法中會(huì)首先拉起
WallpaperManagerService,通過(guò)該類(lèi),WallpaperService后面才得以啟動(dòng)。
if (context.getResources().getBoolean(R.bool.config_enableWallpaperService)) { t.traceBegin("StartWallpaperManagerService"); mSystemServiceManager.startService(WALLPAPER_SERVICE_CLASS); t.traceEnd(); } else { Slog.i(TAG, "Wallpaper service disabled by config"); }
WallpaperManagerService啟動(dòng)之后systemReady()方法中會(huì)通過(guò)loadSettingsLocked()方法加載用戶(hù)設(shè)置過(guò)的壁紙信息,然后監(jiān)聽(tīng)用戶(hù)切換用戶(hù)switchUser(),切換用戶(hù)時(shí),switchWallpaper()會(huì)調(diào)用bindWallpaperComponentLocked()方法拉起對(duì)應(yīng)的壁紙服務(wù)。
手動(dòng)切換時(shí)壁紙服務(wù)的啟動(dòng)流程
手動(dòng)切換壁紙服務(wù)時(shí)需要通過(guò)WallpaperManager.getIWallpaperManager().setWallpaperComponent()方法完成,我們?cè)谶@個(gè)接口中傳入壁紙服務(wù)對(duì)應(yīng)的ComponentName,getIWallpaperManager返回的是WallpaperManagerService的Bp(binder proxy binder代理)端,在WallpaperManagerService端,我們可以查看到setWallpaperComponent的具體實(shí)現(xiàn),
private void setWallpaperComponent(ComponentName name, int userId) { ... /* 首先調(diào)用該方法的時(shí)候回去校驗(yàn)權(quán)限,該權(quán)限定義在frameworks/base/core/res/AndroidManifest.xml, <permission android:name="android.permission.SET_WALLPAPER_COMPONENT" android:protectionLevel="signature|privileged" /> 查看protectionLevel,只有是特權(quán)應(yīng)用或者系統(tǒng)簽名的應(yīng)用才能獲取到這個(gè)系統(tǒng)權(quán)限,所以普通的應(yīng)用是沒(méi)有辦法進(jìn)行壁紙?jiān)O(shè)置的 */ checkPermission(android.Manifest.permission.SET_WALLPAPER_COMPONENT); int which = FLAG_SYSTEM; boolean shouldNotifyColors = false; WallpaperData wallpaper; synchronized (mLock) { Slog.v(TAG, "setWallpaperComponent name=" + name); /* 此處會(huì)先通過(guò)當(dāng)前的用戶(hù)ID獲取到與該用戶(hù)相關(guān)的壁紙信息,WallpaperManagerService支持多用戶(hù)機(jī)制,用戶(hù)的信息在mWallpaperMap中存儲(chǔ),每一個(gè)用戶(hù)對(duì)應(yīng)一個(gè)WallpaperData,WallpaperData存儲(chǔ)壁紙相關(guān)信息 */ wallpaper = mWallpaperMap.get(userId); if (wallpaper == null) { throw new IllegalStateException("Wallpaper not yet initialized for user " + userId); } ... // 在這里真正會(huì)去拉起對(duì)應(yīng)的WallPaperService if (bindWallpaperComponentLocked(name, false, true, wallpaper, null)) { ... }
setWallpaperComponent最終也是通過(guò)bindWallpaperComponentLocked拉起壁紙服務(wù)
壁紙服務(wù)啟動(dòng)過(guò)程
1.校驗(yàn)是否是壁紙服務(wù)
bindWallpaperComponentLocked()方法將會(huì)啟動(dòng)該ComponentName所指定的WallpaperService,在啟動(dòng)的時(shí)候首先會(huì)進(jìn)行校驗(yàn),以確定待拉起的服務(wù)是一個(gè)壁紙服務(wù),
private boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force, boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) { ... int serviceUserId = wallpaper.userId; ServiceInfo si = mIPackageManager.getServiceInfo(componentName, PackageManager.GET_META_DATA | PackageManager.GET_PERMISSIONS, serviceUserId); if (si == null) { // The wallpaper component we're trying to use doesn't exist Slog.w(TAG, "Attempted wallpaper " + componentName + " is unavailable"); return false; } /* 第一個(gè)校驗(yàn): 啟動(dòng)的時(shí)候首先會(huì)校驗(yàn)這個(gè)壁紙服務(wù)是否聲明權(quán)限為BIND_WALLPAPER權(quán)限, 該權(quán)限的定義同樣也在fwk/base/core/res/manifest.xml <permission android:name="android.permission.BIND_WALLPAPER" android:protectionLevel="signature|privileged" /> 該權(quán)限也是系統(tǒng)級(jí)別的,防止三方應(yīng)用切換壁紙, */ if (!android.Manifest.permission.BIND_WALLPAPER.equals(si.permission)) { String msg = "Selected service does not have " + android.Manifest.permission.BIND_WALLPAPER + ": " + componentName; if (fromUser) { throw new SecurityException(msg); } Slog.w(TAG, msg); return false; } WallpaperInfo wi = null; Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE); if (componentName != null && !componentName.equals(mImageWallpaper)) { // Make sure the selected service is actually a wallpaper service. /* 第二個(gè)校驗(yàn): 這個(gè)檢查來(lái)校驗(yàn)服務(wù)是否聲明了android.service.wallpaper.WallpaperService這個(gè)action。如果這個(gè)服務(wù)沒(méi)有聲明這個(gè)action的話那么,ris中就不會(huì)含有這個(gè)component信息, */ List<ResolveInfo> ris = mIPackageManager.queryIntentServices(intent, intent.resolveTypeIfNeeded(mContext.getContentResolver()), PackageManager.GET_META_DATA, serviceUserId).getList(); 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 { /* 第三個(gè)檢查: 獲取名為android.service.wallpaper中的meta-data信息,該meta-data信息中提供了縮略圖,開(kāi)發(fā)者,簡(jiǎn)單的描述等。會(huì)將這些信息轉(zhuǎn)換成WallpaperInfo */ wi = new WallpaperInfo(mContext, ris.get(i)); } catch (XmlPullParserException e) { if (fromUser) { throw new IllegalArgumentException(e); } Slog.w(TAG, e); return false; } catch (IOException e) { if (fromUser) { throw new IllegalArgumentException(e); } Slog.w(TAG, e); return false; } break; } } if (wi == null) { String msg = "Selected service is not a wallpaper: " + componentName; if (fromUser) { throw new SecurityException(msg); } Slog.w(TAG, msg); return false; } } // 當(dāng)壁紙服務(wù)支持在ambient模式下進(jìn)行繪制的時(shí)候,需要檢查是否有AMBIENT_WALLPAPER權(quán)限, if (wi != null && wi.supportsAmbientMode()) { final int hasPrivilege = mIPackageManager.checkPermission( android.Manifest.permission.AMBIENT_WALLPAPER, wi.getPackageName(), serviceUserId); if (hasPrivilege != PackageManager.PERMISSION_GRANTED) { String msg = "Selected service does not have " + android.Manifest.permission.AMBIENT_WALLPAPER + ": " + componentName; if (fromUser) { throw new SecurityException(msg); } Slog.w(TAG, msg); return false; } } // 檢驗(yàn)完畢,這里才會(huì)開(kāi)始bind 壁紙服務(wù),如果校驗(yàn)失敗的話,會(huì)返回false ... }
上面的校驗(yàn)可以看出一共校驗(yàn)了三個(gè)條件:
- 啟動(dòng)的時(shí)候首先會(huì)校驗(yàn)這個(gè)壁紙服務(wù)是否聲明權(quán)限為BIND_WALLPAPER權(quán)限, 該權(quán)限的定義在fwk/base/core/res/manifest.xml中,< permission android:name=“android.permission.BIND_WALLPAPER”
android:protectionLevel=“signature|privileged” />
該權(quán)限也是系統(tǒng)級(jí)別的,防止三方應(yīng)用切換壁紙。 - 這個(gè)檢查來(lái)校驗(yàn)服務(wù)是否聲明了android.service.wallpaper.WallpaperService這個(gè)action。如果這個(gè)服務(wù)沒(méi)有聲明這個(gè)action的話那么,ris中就不會(huì)含有這個(gè)component信息。
- 獲取名為android.service.wallpaper中的meta-data信息,該meta-data信息中提供了縮略圖,開(kāi)發(fā)者,簡(jiǎn)單的描述等。會(huì)將這些信息轉(zhuǎn)換成WallpaperInfo。
2.綁定壁紙服務(wù)
壁紙服務(wù)的校驗(yàn)滿(mǎn)足后,開(kāi)始啟動(dòng)和綁定目標(biāo)服務(wù):
private boolean bindWallpaperComponentLocked(ComponentName componentName, boolean force, boolean fromUser, WallpaperData wallpaper, IRemoteCallback reply) { // 校驗(yàn)服務(wù)是否符合要求結(jié)束后,開(kāi)始著手啟動(dòng)服務(wù) ... //1. 創(chuàng)建一個(gè)WallpaperConnection,該對(duì)象可以監(jiān)聽(tīng)和WallpaperService之間的連接狀態(tài),同時(shí)歸對(duì)象繼承了IWallpaperConnection.Stub,這樣該對(duì)象有擁有了跨進(jìn)程通信的能力,當(dāng)服務(wù)綁定成功后,onServiceConnected()方法調(diào)用中,WallpaperConnection實(shí)力會(huì)被發(fā)送到WallpaperService,該實(shí)例可以用于WallpaperService想WallpaperManagerService進(jìn)行通信的橋梁。 intent.setComponent(componentName); intent.putExtra(Intent.EXTRA_CLIENT_LABEL, com.android.internal.R.string.wallpaper_binding_label); ... /* 2. 這里啟動(dòng)指定的壁紙服務(wù),服務(wù)啟動(dòng)后,壁紙還沒(méi)有辦法進(jìn)行顯示,還需要WallpaperConnection.onServiceConnected中進(jìn)行相應(yīng)的處理*/ if (!mContext.bindServiceAsUser(intent, newConn,Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI| Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE | Context.BIND_INCLUDE_CAPABILITIES,new UserHandle(serviceUserId))) { } /*3. 新的壁紙服務(wù)啟動(dòng)之后,就開(kāi)始銷(xiāo)毀舊服務(wù)*/ if (wallpaper.userId == mCurrentUserId && mLastWallpaper != null && !wallpaper.equals(mFallbackWallpaper)) { detachWallpaperLocked(mLastWallpaper); } /* 4.將新的壁紙服務(wù)信息進(jìn)行保存*/ wallpaper.wallpaperComponent = componentName; wallpaper.connection = newConn; ...
bindWallpaperComponentLocked函數(shù)在拉起壁紙服務(wù)的時(shí)候主要做了下面幾件事情:
- 創(chuàng)建了WallpaperConnection對(duì)象,由于實(shí)現(xiàn)了ServiceConnection接口,所以WallpaperConnection可以用來(lái)監(jiān)聽(tīng)和壁紙服務(wù)的連接狀態(tài),另外由于繼承了IWallpoaperConnection.Stub接口,所以WallpaperConnection具有了跨進(jìn)程通信的能力。
- 啟動(dòng)壁紙服務(wù):這里僅僅是拉起服務(wù),和拉起普通服務(wù)的方式基本一致,拉起方式上則使用了bindServiceAsUser,查看官方注解,該接口增加了校驗(yàn)該用戶(hù)是否能拉起該服務(wù),其余的行為和bindService相同。
- 保存當(dāng)前WallpaperConnection實(shí)例,ConponentName,到WallpaperData中
bindWallpaperComponentLocked()函數(shù)將壁紙服務(wù)拉了起來(lái),但是僅僅將壁紙服務(wù)拉起來(lái)是沒(méi)有辦法顯示圖像的,因?yàn)閱?dòng)的服務(wù)并沒(méi)有窗口令牌,這樣就沒(méi)辦法添加窗口。剩下的這部分顯示的工作在WallpaperConnection的onServiceConnected()方法中進(jìn)行,在該回調(diào)中同樣也能拿到壁紙服務(wù)端服務(wù)端提供的Binder對(duì)象。
WallpaperService在被bind的時(shí)候返回了一個(gè)IWallpaperServiceWrapper對(duì)象,從代碼中可以看到,該對(duì)象中保存了WallpaperService實(shí)例,看了代碼后再去理解這個(gè)對(duì)象的命名(包裝WallpaperService),果然名副其實(shí)。
class IWallpaperServiceWrapper extends IWallpaperService.Stub { private final WallpaperService mTarget; private IWallpaperEngineWrapper mEngineWrapper; public IWallpaperServiceWrapper(WallpaperService context) { mTarget = context; } @Override public void attach(IWallpaperConnection conn, IBinder windowToken, int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding,int displayId) { ... } @Override public void detach() { ... } }
該接口中一共有兩個(gè)接口,attach和detach,attach接口在創(chuàng)建的時(shí)候可以將相關(guān)信息傳遞到壁紙服務(wù)中,對(duì)應(yīng)的,detach接口在服務(wù)銷(xiāo)毀的時(shí)候調(diào)用。
3.引擎的創(chuàng)建和初始化
引擎的創(chuàng)建準(zhǔn)備工作開(kāi)始于onServiceConnected()回調(diào)處,該回調(diào)會(huì)傳遞壁紙服務(wù)需要的窗口令牌和ServiceConnection對(duì)象等。
WallpaperManagerService.java
@Override public void onServiceConnected(ComponentName name, IBinder service) { synchronized (mLock) { if (mWallpaper.connection == this) { 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. if (!mWallpaper.equals(mFallbackWallpaper)) { // 保存當(dāng)前的壁紙信息到文件系統(tǒng)中,這樣重啟的時(shí)候就可以加載之前用戶(hù)設(shè)置過(guò)的壁紙 saveSettingsLocked(mWallpaper.userId); } FgThread.getHandler().removeCallbacks(mResetRunnable); mContext.getMainThreadHandler().removeCallbacks(mTryToRebindRunnable); } } }
onServiceConnected()函數(shù)中,首先將返回的binder對(duì)象進(jìn)行了保存,然后在attachServiceLocked()方法中會(huì)調(diào)用connectLocked()方法,connectLocked()接口中調(diào)用了attach方法傳遞了壁紙服務(wù)所需要的信息。
void connectLocked(WallpaperConnection connection, WallpaperData wallpaper) { ... mWindowManagerInternal.addWindowToken(mToken, TYPE_WALLPAPER, mDisplayId); final DisplayData wpdData = getDisplayDataOrCreate(mDisplayId); try { connection.mService.attach(connection, mToken, TYPE_WALLPAPER, false, wpdData.mWidth, wpdData.mHeight, wpdData.mPadding, mDisplayId); } ... }
attach接口回傳了許多信息,其中
- connection為WallpaperConnection的實(shí)例。WallpaperConnection之所以具有跨進(jìn)程通信的能力是因?yàn)槔^承了IWallpaperConnection.Stub類(lèi),該Stub對(duì)象中比較重要的一個(gè)接口就是attachEngine(),因?yàn)镋ngine實(shí)現(xiàn)才是動(dòng)態(tài)壁紙的核心,WallpaperService會(huì)將創(chuàng)建好的Engine引用通過(guò)attachEngine()回傳給WallpaperManagerService進(jìn)行管理。
- mToken是向WMS注冊(cè)過(guò)的窗口令牌,只有擁有了這個(gè)令牌,WallpaperService才有權(quán)添加壁紙窗口。
傳遞了WallpaperService需要的信息之后,WallPaperService開(kāi)始進(jìn)行引擎的創(chuàng)建。查看WallpaperService中attach()方法的實(shí)現(xiàn),
class IWallpaperServiceWrapper extends IWallpaperService.Stub { ... @Override public void attach(IWallpaperConnection conn, IBinder windowToken, int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding, int displayId) { mEngineWrapper = new IWallpaperEngineWrapper(mTarget, conn, windowToken, windowType, isPreview, reqWidth, reqHeight, padding, displayId); } ... }
attach方法創(chuàng)建了一個(gè)IWallpaperEngineWrapper,顧名思義,該對(duì)象有壁紙服務(wù)創(chuàng)建的引擎的引用,在創(chuàng)建IWallpaperEngineWrapper對(duì)象的時(shí)候,會(huì)發(fā)送DO_ATTACH消息,該消息用于壁紙服務(wù)引擎的創(chuàng)建,
IWallpaperEngineWrapper.java IWallpaperEngineWrapper(WallpaperService context, IWallpaperConnection conn, IBinder windowToken, int windowType, boolean isPreview, int reqWidth, int reqHeight, Rect padding, int displayId) { mCaller = new HandlerCaller(context, context.getMainLooper(), this, true); ... Message msg = mCaller.obtainMessage(DO_ATTACH); mCaller.sendMessage(msg); } ... @Override public void executeMessage(Message message) { switch (message.what) { case DO_ATTACH: { try { // 將IWallpaperEngineWapper對(duì)象傳遞給WallpaperConnection進(jìn)行保存,通過(guò)這個(gè)引用,WallpaperManagerService也可以通過(guò)它與engine進(jìn)行通信 mConnection.attachEngine(this, mDisplayId); } catch (RemoteException e) { Log.w(TAG, "Wallpaper host disappeared", e); return; } // 創(chuàng)建一個(gè)引擎,該方法為抽象方法,需要子類(lèi)根據(jù)自身實(shí)現(xiàn)具體的引擎 Engine engine = onCreateEngine(); mEngine = engine; mActiveEngines.add(engine); // 該方法中會(huì)完成窗口的創(chuàng)建,surface創(chuàng)建等工作。 engine.attach(this); return; }
由于mConnection.attachEngine()方法將IWallpaperEngineWrapper傳遞給了WallpaperManagerService,因此WallpaperManagerService可以轉(zhuǎn)發(fā)相關(guān)的請(qǐng)求和設(shè)置到Engine對(duì)象中,實(shí)現(xiàn)WallpaperManagerService到壁紙的通信。
onCreateEngine方法執(zhí)行后,引擎創(chuàng)建完成,之后通過(guò)engine.attach()方法進(jìn)行引擎相關(guān)的初始化:
void attach(IWallpaperEngineWrapper wrapper) { ... mIWallpaperEngine = wrapper; mCaller = wrapper.mCaller; mConnection = wrapper.mConnection; mWindowToken = wrapper.mWindowToken; mSurfaceHolder.setSizeFromLayout(); mInitializing = true; // 這個(gè)session用于和WMS進(jìn)行通信 mSession = WindowManagerGlobal.getWindowSession(); // mWindow是一個(gè)IWindow對(duì)象,用于接收從WMS發(fā)送過(guò)來(lái)的消息 mWindow.setSession(mSession); mLayout.packageName = getPackageName(); mIWallpaperEngine.mDisplayManager.registerDisplayListener(mDisplayListener, mCaller.getHandler()); mDisplay = mIWallpaperEngine.mDisplay; mDisplayContext = createDisplayContext(mDisplay); mDisplayState = mDisplay.getState(); if (DEBUG) Log.v(TAG, "onCreate(): " + this); // 子類(lèi)可以重寫(xiě)該接口,在該接口中可以修改mSurfaceHolder相關(guān)的屬性,這個(gè)時(shí)候 // 窗口尚未創(chuàng)建。設(shè)置的相關(guān)屬性將在updateSurface中創(chuàng)建窗口時(shí)使用 onCreate(mSurfaceHolder); mInitializing = false; mReportedVisible = false; // updateSurface會(huì)進(jìn)行窗口以及Surface的創(chuàng)建。 updateSurface(false, false, false); }
attach方法執(zhí)行的完成,標(biāo)志著壁紙啟動(dòng)的完成,之后可以調(diào)用壁紙的surface顯示圖像。
壁紙服務(wù)的啟動(dòng)流程總結(jié)
壁紙服務(wù)的啟動(dòng)相比于普通服務(wù)的啟動(dòng)較為復(fù)雜,接下來(lái)用下面的示意圖對(duì)整體的流程進(jìn)行梳理:
壁紙服務(wù)在啟動(dòng)的時(shí)候,大體可以分為兩個(gè)階段,首先就要是拉起對(duì)應(yīng)的服務(wù),拉起服務(wù)后然后將WindowToken等參數(shù)傳遞給引擎進(jìn)行窗口的創(chuàng)建,surface的創(chuàng)建。在WallpaperManagerService和WallpaperService交互的過(guò)程中,主要有下面三個(gè)跨進(jìn)程通信的Binder對(duì)象:
- WallpaperConnection:實(shí)現(xiàn)在WallpaperManagerService中,并通過(guò)IWallpaperService.attach回調(diào)傳遞給了IWallpaperEngineWrapper,通過(guò)WallpaperConnection.attachEngine()方法,WallpaperService將IWallpaperEngineWrapper回傳給了WallpaperManagerService,實(shí)現(xiàn)了雙向的通信。
- IWallpaperService:實(shí)現(xiàn)在WallpaperService中,該對(duì)象提供了attach方法,用于從WallpaperManagerService獲取引擎創(chuàng)建時(shí)需要的WindowToken等信息。
- IWallpaperEngineWrapper:實(shí)現(xiàn)在壁紙服務(wù)進(jìn)程中,同時(shí)引用交給了WallpaperManagerService,該對(duì)象封裝了Engine類(lèi),WallpaperManagerService對(duì)引擎相關(guān)的控制需要通過(guò)該對(duì)象提供的接口實(shí)現(xiàn)。
自己最近因?yàn)樾枰ㄎ灰粋€(gè)開(kāi)機(jī)壁紙服務(wù)啟動(dòng)慢的問(wèn)題,所以熟悉了下壁紙服務(wù)的啟動(dòng)過(guò)程,在此記錄啟動(dòng)流程。定位該問(wèn)題梳理從bind到onServiceConnected和引擎相關(guān)初始化,對(duì)比每一個(gè)階段的時(shí)間,最終確定問(wèn)題的原因。
參考:
- 《深入理解Android卷3》第八章:深入理解android壁紙服務(wù)
- framework-base源碼
到此這篇關(guān)于詳解Android壁紙服務(wù)的啟動(dòng)過(guò)程的文章就介紹到這了,更多相關(guān)Android壁紙內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android ViewDragHelper仿淘寶拖動(dòng)加載效果
這篇文章主要為大家詳細(xì)介紹了Android ViewDragHelper仿淘寶拖動(dòng)加載效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08Android中FoldingLayout折疊布局的用法及實(shí)戰(zhàn)全攻略
這篇文章主要介紹了Android中FoldingLayout折疊布局的用法及實(shí)例,通過(guò)FoldingLayout我們可以制作出炫酷的菜單折疊效果,文中的例子講解得非常詳細(xì),需要的朋友可以參考下2016-02-02Android自定義軟鍵盤(pán)的設(shè)計(jì)與實(shí)現(xiàn)代碼
本篇文章主要介紹了 Android自定義軟鍵盤(pán)的設(shè)計(jì)與實(shí)現(xiàn)代碼,有需要的可以了解一下。2016-11-11Android使用Intent啟動(dòng)其他非系統(tǒng)應(yīng)用程序的方法
這篇文章主要介紹了Android使用Intent啟動(dòng)其他非系統(tǒng)應(yīng)用程序的方法,實(shí)例分析了Intent調(diào)用系統(tǒng)應(yīng)用程序的相關(guān)技巧,需要的朋友可以參考下2015-12-12Android開(kāi)發(fā)中TextView各種常見(jiàn)使用方法小結(jié)
這篇文章主要介紹了Android開(kāi)發(fā)中TextView各種常見(jiàn)使用方法,結(jié)合實(shí)例形式總結(jié)分析了Android開(kāi)發(fā)中TextView各種常見(jiàn)布局與功能實(shí)現(xiàn)技巧,需要的朋友可以參考下2019-04-04一文了解Android?ViewModelScope?如何自動(dòng)取消協(xié)程
這篇文章主要介紹了一文了解Android?ViewModelScope?如何自動(dòng)取消協(xié)程,文章圍繞主題站展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定參考價(jià)值,感興趣的小伙伴可以參考一下2022-07-07詳談Android動(dòng)畫(huà)效果translate、scale、alpha、rotate
下面小編就為大家?guī)?lái)一篇詳談Android動(dòng)畫(huà)效果translate、scale、alpha、rotate。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-01-01Android實(shí)現(xiàn)在TextView文字過(guò)長(zhǎng)時(shí)省略部分或滾動(dòng)顯示的方法
這篇文章主要介紹了Android實(shí)現(xiàn)在TextView文字過(guò)長(zhǎng)時(shí)省略部分或滾動(dòng)顯示的方法,結(jié)合實(shí)例形式分析了Android中TextView控件文字顯示及滾動(dòng)效果相關(guān)操作技巧,需要的朋友可以參考下2016-10-10Android 6.0指紋識(shí)別App開(kāi)發(fā)案例
這篇文章主要為大家詳細(xì)介紹了Android 6.0 指紋識(shí)別App開(kāi)發(fā)案例,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09