SystemUI的實(shí)現(xiàn)
以StatusBar為例,來分析下Android系統(tǒng)具體是如何實(shí)現(xiàn)它們的。 相關(guān)代碼分為兩部分,即:
Service部分
代碼路徑:frameworks/base/services/java/com/android/server。
應(yīng)用部分
代碼路徑:frameworks/base/packages/SystemUI。
下面來看看SystemUI的“目錄”:
…
android:allowBackup="false"android:hardwareAccelerated="true"android:label="@string/app_ label" android:icon="@*android:drawable/platlogo"android:supportsRtl="true" > /*SystemUIService是我們分析的重點(diǎn),狀態(tài)欄等系統(tǒng)UI實(shí)現(xiàn)都是在這里完成的*/ android:process=":screenshot" android:exported="false" />/*由此可知,SystemUI提供了 截屏操作。有興趣的讀者可以自己研究下是如何實(shí)現(xiàn)的*/ 不過這里啟動(dòng)的是LoadAverageService,而不是SystemUIService*/ … 通過AndroidManfiest我們知道SystemUIService是整個(gè)系統(tǒng)UI的“載體”,所以接下來將根據(jù)這一線索來把整個(gè)代碼流程“串”起來。和其他很多系統(tǒng)服務(wù)一樣,SystemUIService也是在SystemServer中啟動(dòng)的。具體而言,SystemServer會(huì)在適當(dāng)?shù)臅r(shí)機(jī)通知ActivityManagerService“系統(tǒng)已經(jīng)就緒(systemReady),可以進(jìn)一步運(yùn)行第三方模塊了”——這其中就包括將由startServiceAsUser啟動(dòng)的SystemUIService。 SystemUIService繼承了標(biāo)準(zhǔn)的Service組件,因而必須重載onCreate接口: /*frameworks/base/packages/systemui/src/com/android/systemui/SystemUIService.java*/ public void onCreate() {… IWindowManager wm = WindowManagerGlobal.getWindowManagerService();//獲取WMS服務(wù) try { SERVICES[0] = wm.hasSystemNavBar()? R.string.config_systemBarComponent : R.string.config_statusBarComponent;//是StatusBar還是SystemBar? } catch (RemoteException e) { Slog.w(TAG, "Failing checking whether status bar can hide", e); } final int N = SERVICES.length; mServices = new SystemUI[N]; for (int i=0; i Class cl = chooseClass(SERVICES[i]); Slog.d(TAG, "loading: " + cl); try { mServices[i] = (SystemUI)cl.newInstance(); } … mServices[i].mContext = this; Slog.d(TAG, "running: " + mServices[i]); mServices[i].start();//mServices中的每個(gè)元素都繼承自SystemUI } } SERVICES是一個(gè)object數(shù)組,它的初始值如下所示: final Object[] SERVICES = new Object[] { 0, // system bar or status bar, filled in below. com.android.systemui.power.PowerUI.class, com.android.systemui.media.RingtonePlayer.class, com.android.systemui.settings.SettingsUI.class, }; 其中,SERVICES[0]在初始化時(shí)沒有賦值。它將根據(jù)hasSystemNavBar的執(zhí)行結(jié)果來決定是用systemBar還是statusBar。上面這段代碼首先取出SERVICES數(shù)組中的class名,然后分別實(shí)例化它們,最后調(diào)用start接口統(tǒng)一啟動(dòng)。因此,每一個(gè)系統(tǒng)ui元素(包括statusBar,PowerUI等)都必須繼承自SystemUI這個(gè)抽象類,并重載其中的start方法。 函數(shù)hasSystemNavBar做了哪些判斷來對(duì)statusBar和systemBar進(jìn)行取舍呢?WindowManager的真正實(shí)現(xiàn)體是WindowManagerService。 /*frameworks/base/services/java/com/android/server/wm/WindowManagerService.java*/ public boolean hasSystemNavBar() { return mPolicy.hasSystemNavBar(); } **Policy是Android中定義UI行為的一個(gè)“規(guī)范”。**比如有沒有Navigation Bar,WindowLayer如何排布等。以PhoneWindowManager為例,它判斷當(dāng)前系統(tǒng)是否需要導(dǎo)航條。 我們假設(shè)設(shè)備的分辨率是800*480,屏幕密度為ldpi: /*frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java*/ int shortSizeDp = shortSize*DisplayMetrics.DENSITY_DEFAULT/ density; /*在這個(gè)場(chǎng)景中,shortSize=480,DENSITY_DEFAULT=160,density =120, 所以最終 shortSizeDp = 640*/ if (shortSizeDp < 600) {//在這個(gè)場(chǎng)景中不成立 mHasSystemNavBar = false; mNavigationBarCanMove = true; } else if (shortSizeDp < 720) {/*本場(chǎng)景屬于這一分支*/ mHasSystemNavBar = false; mNavigationBarCanMove = false; } if (!mHasSystemNavBar) {//進(jìn)一步判斷是否有Navigation Bar … } else { mHasNavigationBar = false; } 所以在這個(gè)場(chǎng)景中,經(jīng)過上面的判決后mHasSystemNavBar為false。換句話說,對(duì)于分辨率800*480且密度為ldpi的屏幕,它的SERVICES[0]對(duì)應(yīng)的class類名是R.string.config_statusBar Component即“com.android.systemui.statusbar.phone.PhoneStatusBar”。下面以PhoneStatusBar為例來看看它的創(chuàng)建過程及具體樣式: /*frameworks/base/packages/systemui/src/com/android/systemui/statusbar/phone/ PhoneStatusBar.java*/ public void start() { mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE)) .getDefaultDisplay();/*mDisplay記錄了當(dāng)前默認(rèn)顯示屏的大小,密度等等信息*/ … super.start();// 關(guān)鍵語句,下面我們會(huì)重點(diǎn)介紹 addNavigationBar();/*不是所有Phone都需要Navigation Bar。比如設(shè)備本身已經(jīng)配備了物理按 鍵,這種情況下如果一直在屏幕上顯示導(dǎo)航條反而是一種累贅*/ … } PhoneStatusBar的“父類”是BaseStatusBar,很多框架性的操作都是在這里面完成的(但UI界面的具體描述還是會(huì)通過回調(diào)PhoneStatusBar中的方法來確定): /*frameworks/base/packages/systemui/src/com/android/systemui/statusbar/BaseStatusBar.java*/ public void start() {… mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); // Connect in to the status bar manager service StatusBarIconList iconList = new StatusBarIconList();//狀態(tài)欄圖標(biāo)列表 ArrayList ArrayList mCommandQueue = new CommandQueue(this, iconList); int[] switches = new int[7]; ArrayList try { mBarService.registerStatusBar(mCommandQueue,iconList,notificationKeys, notifications,switches, binders); /*經(jīng)過一系列對(duì)象的創(chuàng)建與初始化后,開始向 StatusBarService進(jìn)行注冊(cè)。這里涉及跨進(jìn)程操作,因而傳遞的 參數(shù)都是繼承自Parcelable的*/ } catch (RemoteException ex) { // If the system process isn't there we're doomed anyway. } createAndAddWindows(); /*這是真正將Status Bar顯示出來的地方*/ … } 好不容易快到“水落石出”的時(shí)候了,但是上面這段代碼卻又殺出一個(gè)“程咬金”——StatusBarService。 既然SystemUI這個(gè)應(yīng)用程序中已經(jīng)有StatusBar了,為什么又需要StatusBarService? 先來看看StatusBarService是在哪里啟動(dòng)的。 /*frameworks/base/services/java/com/android/server/SystemServer.java*/ try { Slog.i(TAG, "Status Bar"); statusBar = new StatusBarManagerService(context, wm); /*確實(shí)在這里。而且具體的實(shí)現(xiàn)類叫做StatusBarManagerService*/ ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar); } catch (Throwable e) { reportWtf("starting StatusBarManagerService", e); } 現(xiàn)在可以進(jìn)一步分析StatusBarManagerService的實(shí)現(xiàn)了。針對(duì)上面BaseStatusBar中調(diào)用的注冊(cè)操作: public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList,List notificationKeys,List int switches[], List enforceStatusBarService(); mBar = bar; synchronized (mIcons) { iconList.copyFrom(mIcons); /*復(fù)制Icon列表,注意方向是從StatusBarManager-> BaseStatusBar*/ } synchronized (mNotifications) { for (Map.Entry { notificationKeys.add(e.getKey()); notifications.add(e.getValue());/*和Icon列表類似,方向也是從StatusBarManager 到BaseStatusBar*/ } } … } 由上面這段代碼可以看出,registerStatusBar有兩個(gè)作用: 其一,為新啟動(dòng)的SystemUI應(yīng)用中的StatusBar賦予當(dāng)前系統(tǒng)的真實(shí)值(比如有多少需要顯示的圖標(biāo))。其二,通過成員變量mBar記錄下IStatusBar對(duì)象——它在SystemUI中對(duì)應(yīng)的是CommandQueue。 我們?cè)倩氐紹aseStatusBar。向StatusBarManagerService注冊(cè)完成后,它會(huì)執(zhí)行如下語句。 createAndAddWindows(); BaseStatusBar中的這個(gè)方法是抽象的,因而其子類PhoneStatusBar必須要重載它: /*frameworks/base/packages/systemui/src/com/android/systemui/statusbar/phone/Ph- oneStatusBar.java*/ public void createAndAddWindows() { addStatusBarWindow(); } private void addStatusBarWindow() { final int height = getStatusBarHeight();/*首先獲取StatusBar的高度。默認(rèn)的高度值是通 過com.android.internal.R.dimen.status_bar_height來指定的,因而 開發(fā)人員如果需要更改StatusBar高度的話,可以考慮修改這個(gè)值*/ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, /*寬度是MATCH_PARENT*/ height, //高度值是可定制的 WindowManager.LayoutParams.TYPE_STATUS_BAR, /*指定窗口類型*/ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, /*設(shè)置flag, 下面還會(huì)加上硬件加速屬性*/ PixelFormat.TRANSLUCENT/*半透明的*/); lp.flags |=windowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; lp.gravity = getStatusBarGravity();/*設(shè)置Gravity屬性,默認(rèn)值為Gravity.TOP |Gravity.FILL_HORIZONTAL,所以StatusBar是在屏幕上方*/ lp.setTitle("StatusBar"); //標(biāo)題 lp.packageName = mContext.getPackageName(); makeStatusBarView(); //下面會(huì)詳細(xì)介紹 mWindowManager.addView(mStatusBarWindow, lp); /*將一切就緒的mStatusBarWindow加入 WindowManager中。請(qǐng)參見本書顯示系統(tǒng)章節(jié)的講解*/ } 從makeStatusBarView這個(gè)函數(shù)名可以推斷出,StatusBarView會(huì)被創(chuàng)建并且初始化。先來了解下兩個(gè)重要的變量。 mStatusBarWindow 這是一個(gè)StatusBarWindowView類對(duì)象,同時(shí)我們通過addView傳給WindowManager的也是這個(gè)變量——說明它很可能包含了StatusBarView。 mStatusBarView 這就是makeStatusBarView需要操作的對(duì)象。 /*frameworks/base/packages/systemui/src/com/android/systemui/statusbar/phone/Ph-one StatusBar.java*/ protected PhoneStatusBarView makeStatusBarView() {… mStatusBarWindow = (StatusBarWindowView) View.inflate(context, R.layout.super_ status_bar, null); mStatusBarWindow.mService = this; //mService其實(shí)指的是PhoneStatusBar mStatusBarWindow.setOnTouchListener(new View.OnTouchListener() {//設(shè)置觸摸事件 @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) {//支持下拉手勢(shì) if (mExpandedVisible) { animateCollapsePanels ();//通知欄的“下拉展開”需要?jiǎng)赢嬓Ч蝗粫?huì)很突兀 } } return mStatusBarWindow.onTouchEvent(event); }}); mStatusBarView =(PhoneStatusBarView)mStatusBarWindow.findViewById(R.id.status_ bar); mStatusBarView.setBar(this); /*狀態(tài)欄出場(chǎng)了*/ … mNotificationPanel = (NotificationPanelView) mStatusBarWindow. findViewById(R.id. notification_panel); mNotificationPanel.setStatusBar(this); /*通知欄也很關(guān)鍵,只不過它只有在下拉后才會(huì)出現(xiàn)*/ /*從下面開始將利用mStatusBarView為PhoneStatusBar中的眾多內(nèi)部變量賦值*/ … try { boolean showNav = mWindowManagerService.hasNavigationBar();/*決定是否需要導(dǎo)航條*/ if (showNav) { mNavigationBarView = (NavigationBarView) View.inflate(context, R.layout. navigation_ bar, null); /*Navigation Bar對(duì)應(yīng)的layout。有興趣的讀者可以自己看一下*/ … } } catch (RemoteException ex) { /*Android中的不少代碼在捕捉異常時(shí),很常見的一種處理就是“聽天由命”…*/ } /*接下來通過findViewById從mStatusBarView中獲取StatusIcons、NotificationIcons、 ClearButton等一系列按鍵。我們將會(huì)在StatusBar布局文件中做統(tǒng)一分析。這里暫時(shí)略過*/ … /*最后動(dòng)態(tài)注冊(cè)需要接收的廣播,比如系統(tǒng)設(shè)置改變,屏幕關(guān)閉等*/ IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); … context.registerReceiver(mBroadcastReceiver, filter);… return mStatusBarView;//注意最終返回值是mStatusBarWindow的子View } 變量mStatuBarWindow來源于super_status_bar布局。它本質(zhì)上還是一個(gè)FrameLayout,包含的元素也很簡(jiǎn)單,就是status_bar和status_bar_expanded兩個(gè)布局,前者對(duì)應(yīng)的是狀態(tài)欄mStatusBarView,后者其實(shí)就是通知欄mNotificationPanel。為status_bar中的眾多元素(按鍵、背景等)進(jìn)行初始化。 最終的返回值是mStatusBarView。然后利用WindowManager的addView接口將mStatus- BarWindow(注意:不是mStatusBarView)添加進(jìn)窗口系統(tǒng)中。接下來的主動(dòng)權(quán)就轉(zhuǎn)交給WindowManager。 這樣我們就把StatusBar,Notification和NavigationBar的調(diào)用流程“串”起來了。 Android壁紙資源——WallpaperService 除了前面講解的狀態(tài)欄、通知欄外,壁紙也屬于SystemUI管理的一個(gè)重點(diǎn)。在Android系統(tǒng)中,用戶可以從設(shè)備內(nèi)部或者外存儲(chǔ)器(比如SD卡中)中選取圖片資源作為壁紙。另外,系統(tǒng)還支持動(dòng)態(tài)壁紙的顯示。 壁紙管理系統(tǒng)主要包括以下幾個(gè)方面。 WallpaperManagerService(WPMS) 它是壁紙機(jī)制的“大總管”,靜態(tài)、動(dòng)態(tài)壁紙都是在這里統(tǒng)一調(diào)度的。 WallpaperService(WPS) WPS繼承了標(biāo)準(zhǔn)的Service組件,因而它一定會(huì)實(shí)現(xiàn)onCreate、onDestroy、onBind等一系列方法。此外它還包含了一個(gè)重要的嵌套類engine,我們?cè)诤竺鏁?huì)做詳細(xì)講解。WPS是靜態(tài)、動(dòng)態(tài)壁紙的基類,代表了作為“壁紙”所應(yīng)該具有的一切屬性。 ImageWallpaper(IWP) 從名稱可以看出,它是靜態(tài)壁紙的實(shí)現(xiàn)類,而且一定是繼承自上面的WPS。 WallPaperManagerService WPMS既然是基于AIDL實(shí)現(xiàn)的,我們來看看它的接口描述: /*frameworks/base/core/java/android/app/IWallpaperManager.aidl*/ interface IWallpaperManager { ParcelFileDescriptor setWallpaper(String name); /*設(shè)置壁紙*/ void setWallpaperComponent(in ComponentName name); /*設(shè)置動(dòng)態(tài)壁紙*/ ParcelFileDescriptor getWallpaper(IWallpaperManagerCallback cb,out Bundle outParams); WallpaperInfo getWallpaperInfo(); … } 從上面的接口定義可以看出,WPMS的工作并不復(fù)雜——它提供了全局的壁紙注冊(cè)、取消和查詢功能,并在接收到事件時(shí)進(jìn)行合理分配。 和其他系統(tǒng)服務(wù)一樣,WPMS是在SystemServer.java中啟動(dòng)并注冊(cè)進(jìn)ServiceManager中的,如下所示: /*frameworks/base/services/java/com/android/server/SystemServer.java*/ try { Slog.i(TAG, "Wallpaper Service"); if (!headless) { wallpaper = new WallpaperManagerService(context); ServiceManager.addService(Context.WALLPAPER_SERVICE, wallpaper); } } catch (Throwable e) { reportWtf("starting Wallpaper Service", e); } 接下來的一個(gè)問題是:既然系統(tǒng)同時(shí)支持靜態(tài)壁紙和動(dòng)態(tài)壁紙,而且每種類型中還包含了N個(gè)實(shí)例(比如原生態(tài)系統(tǒng)就自帶多個(gè)動(dòng)態(tài)壁紙供用戶選擇),那么系統(tǒng)在顯示時(shí)是如何選擇的呢?我們很自然地會(huì)想到,在WPMS啟動(dòng)時(shí)它應(yīng)該會(huì)去讀取某個(gè)“配置文件”,這個(gè)文件記錄了用戶最近一次的選擇: public WallpaperManagerService(Context context) { … loadSettingsLocked(UserHandle.USER_OWNER);//加載配置 } 當(dāng)WPMS構(gòu)造時(shí),它調(diào)用了loadSettingsLocked: private void loadSettingsLocked(int userId) {//這里傳進(jìn)來的userId=0 … try { stream = new FileInputStream(file); XmlPullParser parser = Xml.newPullParser(); parser.setInput(stream, null); int type; do { type = parser.next(); if (type == XmlPullParser.START_TAG) { String tag = parser.getName(); if ("wp".equals(tag)) {… wallpaper.name = parser.getAttributeValue(null, "name"); String comp = parser.getAttributeValue(null, "component"); … } } } while (type != XmlPullParser.END_DOCUMENT); success = true; } … } 上面這段代碼會(huì)按照寫入時(shí)的格式將wallpaper的配置信息讀出來,并保存在WallpaperData結(jié)構(gòu)中——專門用于描述壁紙信息的通用數(shù)據(jù)結(jié)構(gòu)。不過這時(shí)壁紙還沒有真正顯示出來,而是要等到系統(tǒng)進(jìn)入Ready狀態(tài)(此時(shí)系統(tǒng)會(huì)回調(diào)SystemReady接口)后才會(huì)通知具體的壁紙程序進(jìn)行繪制: public void systemReady() { WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_OWNER); switchWallpaper(wallpaper, null); … } 接著進(jìn)入Wallpaper的具體處理中: void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) { synchronized (mLock) {… try { ComponentName cname = wallpaper.wallpaperComponent != null ? wallpaper.wallpaperComponent : wallpaper.nextWallpaperComponent; if (bindWallpaperComponentLocked(cname, true, false, wallpaper,reply)) { return; } } … } 系統(tǒng)開機(jī)后,wallpaper.wallpaperComponent為空(除非上一次用戶選擇了其他方式);而wallpaper.nextWallpaperComponent則在loadSettingsLocked中被設(shè)置為wallpaper.imageWallpaper Component,即我們前面提到的ImageWallpaper這個(gè)Service。所以當(dāng)調(diào)用bindWallpaperComponentLocked時(shí),傳入的cname就代表了ImageWallpaper。從bindWallpaperComponentLocked的函數(shù)名稱可以看出,它將會(huì)以bindService的方式來啟動(dòng)目標(biāo)壁紙Service(所以后期如果確認(rèn)已經(jīng)不再使用這個(gè)Service,還要主動(dòng)執(zhí)行unbind,然后這個(gè)壁紙服務(wù)就會(huì)自動(dòng)銷毀)。 WPMS啟動(dòng)后就可以接收客戶端的請(qǐng)求了,因?yàn)樗鼘儆趯?shí)名的BinderServer,意味著所有人都可以自由地使用它所提供的服務(wù)。比如我們既可以在系統(tǒng)自帶的Launcher應(yīng)用程序中選擇壁紙,也完全可以自己編寫一個(gè)更改壁紙的應(yīng)用程序。 下面我們以設(shè)置壁紙這一場(chǎng)景為例來分析WPMS的內(nèi)部實(shí)現(xiàn): /*frameworks/base/services/java/com/android/server/WallpaperManagerService.java*/ public ParcelFileDescriptor setWallpaper(String name) { checkPermission(android.Manifest.permission.SET_WALLPAPER); synchronized (mLock) { int userId = UserHandle.getCallingUserId(); WallpaperData wallpaper = mWallpaperMap.get(userId); … final long ident = Binder.clearCallingIdentity(); try { ParcelFileDescriptor pfd = updateWallpaperBitmapLocked(name, wallpaper); … return pfd; } finally { Binder.restoreCallingIdentity(ident); } } } 首先系統(tǒng)會(huì)做下權(quán)限檢查,所以提供壁紙?jiān)O(shè)置功能的應(yīng)用程序一定要在AndroidManifest.xml中顯式寫上如下權(quán)限聲明: 變量wallpaper是從mWallpaperMap取出來的,代表UserId為0時(shí)的壁紙——如果不為空就進(jìn)入以下函數(shù): ParcelFileDescriptor updateWallpaperBitmapLocked(String name, WallpaperData wallpaper) { if (name == null) name = ""; try { File dir = getWallpaperDir(wallpaper.userId);//wallpaper的路徑 if (!dir.exists()) {//指定的路徑不存在,需要?jiǎng)?chuàng)建 dir.mkdir(); FileUtils.setPermissions(dir.getPath(), FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, -1, -1); } File file = new File(dir, WALLPAPER); ParcelFileDescriptor=ParcelFileDescriptor.open(file, MODE_CREATE|MODE_READ_WRITE); if (!SELinux.restorecon(file)) { return null; } wallpaper.name = name; return fd; } catch (FileNotFoundException e) { Slog.w(TAG, "Error setting wallpaper", e); } return null; } 上面getWallpaperDir將得到一個(gè)WALLPAPER_BASE_DIR+“/”+userId的路徑,其中WALLPAPER_BASE_DIR默認(rèn)值是"/data/system/users"。 ImageWallpaper 前面講過,當(dāng)WPMS開機(jī)啟動(dòng)時(shí),默認(rèn)情況下會(huì)選擇ImageWallpaper這個(gè)壁紙實(shí)現(xiàn),并且以bindService的方式來啟動(dòng)它。在bindService中,WPMS同時(shí)傳入名為newConn的Binder對(duì)象(WallpaperConnection)來使ImageWallpaper(其他WallpaperService也是一樣的)可以訪問到WPMS。而ImageWallpaper則響應(yīng)onBind返回一個(gè)IWallpaperServiceWrapper的Binder對(duì)象,如圖所示。 我們來看看當(dāng)綁定成功后WPMS中的操作: public void onServiceConnected(ComponentName name, IBinder service) { synchronized (mLock) { if (mWallpaper.connection == this) {… attachServiceLocked(this, mWallpaper); … saveSettingsLocked(mWallpaper); } } } WPMS除了要保存當(dāng)前所選的壁紙外,還要調(diào)用attachServiceLocked(間接調(diào)用Iwallpaper ServiceWrapper.attach)來執(zhí)行實(shí)際的工作。 WPS這邊的attach函數(shù)將生成一個(gè)IWallpaperEngineWrapper對(duì)象并給它發(fā)送一個(gè)DO_ATTACH,這個(gè)消息最終由IWallpaperEngineWrapper. executeMessage來處理: public void executeMessage(Message message) { switch (message.what) { case DO_ATTACH: { try { mConnection.attachEngine(this); } catch (RemoteException e) { Log.w(TAG, "Wallpaper host disappeared", e); return; } Engine engine = onCreateEngine(); mEngine = engine; mActiveEngines.add(engine); engine.attach(this); return; } 上述代碼段通過onCreateEngine生成了一個(gè)壁紙引擎——這也是各壁紙應(yīng)用間最核心的差異。所以系統(tǒng)要求每一個(gè)WallpaperService實(shí)例必須要重載onCreateEngine來實(shí)現(xiàn)自己的engine。在ImageWallpaper中,它將產(chǎn)生一個(gè)DrawableEngine——這個(gè)engine隨后會(huì)被加入mActiveEngines的全局list中,然后調(diào)用它提供的attach接口。如下所示: /*frameworks/base/core/java/android/service/wallpaper/WallpaperService.java*/ public class Engine {… void attach(IWallpaperEngineWrapper wrapper) {… mSession = WindowManagerGlobal.getWindowSession(); mWindow.setSession(mSession); … IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_SCREEN_ON); filter.addAction(Intent.ACTION_SCREEN_OFF); registerReceiver(mReceiver, filter); … updateSurface(false, false, false);//更新Surface }… Engine內(nèi)部首先需要進(jìn)行各重要變量的初始化,然后注冊(cè)監(jiān)聽屏幕的開/關(guān)事件,最后調(diào)用updateSurface。 相關(guān)閱讀
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。