前言html
轉載請聲明,轉自【http://www.javashuo.com/article/p-edrjpwxy-e.html】,謝謝!java
SystemUI是系統啓動中第一個用戶肉眼可見的應用,其功能一應俱全,好比開機後看到的鎖屏界面,充電時充電界面,狀態欄,導航欄,多任務欄等,都是與Android手機用戶息息相關的功能。因此不止SystemUI開發者,普通的應用開發者也頗有必要去了解一下SystemUI。本系列文章會基於Android P和Android Q來介紹SystemUI的各個方面,本篇做爲本系列第一篇,主要介紹了SystemUI的啓動流程,以及主要功能簡介。android
本文的主要內容以下:數組
1、SystemUI簡介app
SystemUI,顧名思義是系統爲用戶提供的系統級別的信息顯示與交互的一套UI組件,因此其功能一應俱全。好比鎖屏、狀態欄、底部導航欄、最近使用App列表等,大部分功能相互獨立,按需啓動,後文會繼續列出更多功能。在系統源碼中,其位置爲:frameworks/base/package/SystemUI。儘管從表現形式上看,SystemUI和普通的Android APP有較大的差異,但其本質和普通APP並無什麼差異,也是以apk的形式存在,以下圖所示:ide
以當前測試機爲例,其就預置在系統指定的目錄下。也是經過Android的4大組件中的Activity、Service、BroadcastReceiver來接受外界的請求並執行相關的操做,只不過它們所接受的請求主要來自各個系統服務而已。函數
2、Lambda表達式簡介oop
因爲後面有個流程中用到了Lambda表達式,爲了後面便於講解,這裏我們先簡單介紹一下它,並簡單演示其使用方法,這裏不作深刻探討,有興趣的能夠自行研究。post
Lambda表達式是一個匿名函數,即沒有函數名的函數,是基於數學中的λ演算得名。在java中,從java8開始引入,使用它來設計代碼會更加簡潔。下面在Android項目中舉兩個例子來直觀感覺一下Lambda語法的使用。學習
如下是一個很常見的設置點擊事件的例子,先看看不用Lambda表達式時的狀況:
1 mTextView.setOnClickListener(new View.OnClickListener() { 2 @Override 3 public void onClick(View v) { 4 Log.i("songzheweiwang", "test lambda"); 5 } 6 });
在採用Lambda表達式後,就是下面這種狀況:
1 mTextView.setOnClickListener(onClickListener -> { 2 Log.i("songzheweiwang", "test lambda"); 3 });
其中「onClickListener」是隨意取的一個字符串,咱們取名的時候便於識別就能夠了。可見整個代碼簡潔了不少,閱讀起來也很是簡單。
另外再看一個更加明顯的例子,不使用Lambda表達式時是這樣:
1 Runnable runnable = new Runnable() { 2 @Override 3 public void run() { 4 Log.i("songzheweiwang", "test lambda"); 5 } 6 };
使用Lambda表達式後,就成了這樣:
1 Runnable runnable2 = () -> Log.i("songzheweiwang", "test lambda");
如上的「->」符號能夠讀做「go to」。使用Lambda表達式來代替匿名的內部類,確實是很是的方便,可是使用的時候須要注意java的版本號,前面說了,是在java8中才引入的,不然在編譯時會報以下的錯誤:
如上內容參考【Lambda表達式_百度百科】
3、SystemUI的啓動時機
在【【乘風破浪】Android系統啓動篇】中,我介紹過Android系統的大體流程,在第6步中講到了SystemServer進程的啓動。SystemServer進程啓動時,會執行下面的代碼:
1 //=========SystemServer.java========= 2 public static void main(String[] args) { 3 new SystemServer().run(); 4 } 5 private void run() { 6 ...... 7 //建立消息Looper 8 Looper.prepareMainLooper(); 9 // 加載動態庫libandroid_servers.so,初始化native服務 10 System.loadLibrary("android_servers"); 11 ...... 12 //初始化系統context 13 createSystemContext(); 14 //建立SystemServiceManager 15 mSystemServiceManager = new SystemServiceManager(mSystemContext); 16 ...... 17 //啓動引導服務,如AMS等 18 startBootstrapServices(); 19 //啓動核心服務 20 startCoreServices(); 21 //啓動其它服務,如WMS,SystemUI等 22 startOtherServices(); 23 .... 24 }
在執行完第1八、20行的代碼後,會啓動引導服務和一些核心服務,如AMS等,而後第22行中就會啓動其餘服務,其中SystemUI就在其中。
1 //======SystemServer.java====== 2 private void startOtherServices() { 3 ...... 4 // We now tell the activity manager it is okay to run third party 5 // code. It will call back into us once it has gotten to the state 6 // where third party code can really run (but before it has actually 7 // started launching the initial applications), for us to complete our 8 // initialization. 9 mActivityManagerService.systemReady(() -> { 10 ...... 11 traceBeginAndSlog("StartSystemUI"); 12 try { 13 startSystemUi(context, windowManagerF); 14 } catch (Throwable e) { 15 reportWtf("starting System UI", e); 16 } 17 traceEnd(); 18 ...... 19 }, BOOT_TIMINGS_TRACE_LOG); 20 ...... 21 }
在第9行中,前面講過AMS先啓動了,mActivityManagerService調用systemReady方法,這裏就用到了前面介紹過的Lambda表達式,systemReady方法的源碼以下:
1 public void systemReady(final Runnable goingCallback, BootTimingsTraceLog traceLog) { 2 ...... 3 if (goingCallback != null) { 4 goingCallback.run(); 5 } 6 ...... 7 }
這裏參照前面介紹的Lambda表達式的使用方法就容易理解了,實際上就是執行Runnable的回調而已,這裏其實就等同於以下代碼:
1 mActivityManagerService.systemReady(new Runnable(){ 2 new Runnable() { 3 @Override 4 public void run() { 5 //Lambda表達式中的回調代碼 6 } 7 } 8 },BOOT_TIMINGS_TRACE_LOG);
實際上在Lambda表達式還未引入前,即早期的代碼中就是這樣寫法。
當一切就緒後,回調開始執行,就開始執行第13行的startSystemUI方法了。該方法的源碼以下:
1 static final void startSystemUi(Context context, WindowManagerService windowManager) { 2 Intent intent = new Intent(); 3 intent.setComponent(new ComponentName("com.android.systemui", 4 "com.android.systemui.SystemUIService")); 5 intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING); 6 //Slog.d(TAG, "Starting service: " + intent); 7 context.startServiceAsUser(intent, UserHandle.SYSTEM); 8 windowManager.onSystemUiStarted(); 9 }
第三、4行中給出了包名和類名,這樣就開始啓動SystemUI了。從這段代碼能夠看到,SystemUI是經過Service來啓動的,並且是以系統的身份來啓動它的。
4、Service啓動流程淺析
上節中startSystemUI方法中開始啓動SystemUIService,Service的啓動流程比較複雜,這裏不作詳細分析,僅簡單介紹一下其中和本節息息相關的關鍵流程。
上節代碼第7行startSystemUI方法的調用者看起來是Context類型的context,Context是一個抽象類,實際執行者實際上是ContextImpl。調用流程會經過Binder方式從ContextImpl跳轉到AMS中,再經過Binder方式跳轉到ActivityThread中的內部類ApplicationThread中的scheduleCreateService方法。在該方法中會發送給Handler H來處理,Handler H的實例化是使用的主線程的Looper,因此其回調方法handleMessage就是在主線程中執行的,此時會在該方法中調用handleCreateService方法,我們從這個方法開始看。
1 private void handleCreateService(CreateServiceData data) { 2 ...... 3 Service service = null; 4 try { 5 ...... 6 service = packageInfo.getAppFactory() 7 .instantiateService(cl, data.info.name, data.intent); 8 } catch (Exception e) { 9 ...... 10 } 11 try { 12 ...... 13 ContextImpl context = ContextImpl.createAppContext(this, packageInfo); 14 context.setOuterContext(service); 15 Application app = packageInfo.makeApplication(false, mInstrumentation); 16 service.attach(context, this, data.info.name, data.token, app, 17 ActivityManager.getService()); 18 service.onCreate(); 19 ...... 20 } catch (Exception e) { 21 ...... 22 } 23 }
第6行建立了service的實例,第13行建立上下文,第15行建立Application,並在其中執行了Application的onCreate方法,第18行執行了service的onCreate方法。這裏進入到第15行的makeApplication方法。下面截取了關鍵代碼:
1 public Application makeApplication(boolean forceDefaultAppClass, 2 Instrumentation instrumentation) { 3 ...... 4 Application app = null; 5 String appClass = mApplicationInfo.className; 6 if (forceDefaultAppClass || (appClass == null)) { 7 appClass = "android.app.Application"; 8 } 9 try { 10 ...... 11 ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this); 12 app = mActivityThread.mInstrumentation.newApplication( 13 cl, appClass, appContext); 14 appContext.setOuterContext(app); 15 } catch (Exception e) { 16 ...... 17 } 18 mActivityThread.mAllApplications.add(app); 19 ...... 20 if (instrumentation != null) { 21 try { 22 instrumentation.callApplicationOnCreate(app); 23 } catch (Exception e) { 24 ...... 25 } 26 } 27 ...... 28 return app; 29 }
由於是初始啓動,因此會走到第7行。第12行的newApplication源碼以下:
1 public Application newApplication(ClassLoader cl, String className, Context context) 2 throws InstantiationException, IllegalAccessException, 3 ClassNotFoundException { 4 Application app = getFactory(context.getPackageName()) 5 .instantiateApplication(cl, className); 6 app.attach(context); 7 return app; 8 }
繼續追蹤instantiateApplication方法:
1 public @NonNull Application instantiateApplication(@NonNull ClassLoader cl, 2 @NonNull String className) 3 throws InstantiationException, IllegalAccessException, ClassNotFoundException { 4 return (Application) cl.loadClass(className).newInstance(); 5 }
這裏就經過類加載器的形式建立了Application的實例。可見前面的makeApplication方法第12行的做用就是建立Application實例了,而後走到該方法的第22行,進入該方法:
1 public void callApplicationOnCreate(Application app) { 2 app.onCreate(); 3 }
該方法中Application執行了onCreate方法。
到這裏service的大體啓動流程就明瞭了,這裏我們須要記住一個執行順序(由於我看過很多資料容易在這裏犯錯,說是Application會比Service先實例化,經過這個流程咱們能夠看到這種說法是錯誤的,因此這裏着重提出來):
(1)實例Service;
(2)實例Application;
(3)Application實例執行onCreate方法;
(4)Service實例執行onCrate方法。
5、SystemUIApplication中onCreate方法處理邏輯
上一節咱們分析了,會先執行Application的onCreate方法,在執行Service的onCreate方法,這裏先分析SystemUIApplication中onCreate方法的執行邏輯。
1 //============SystemUIApplication.java======== 2 private SystemUI[] mServices; 3 @Override 4 public void onCreate() { 5 super.onCreate(); 6 ...... 7 //設置主題 8 setTheme(R.style.Theme_SystemUI); 9 ...... 10 if (Process.myUserHandle().equals(UserHandle.SYSTEM)) { 11 IntentFilter bootCompletedFilter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); 12 bootCompletedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 13 registerReceiver(new BroadcastReceiver() { 14 @Override 15 public void onReceive(Context context, Intent intent) { 16 if (mBootCompleted) return; 17 18 if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received"); 19 unregisterReceiver(this); 20 mBootCompleted = true; 21 if (mServicesStarted) { 22 final int N = mServices.length; 23 for (int i = 0; i < N; i++) { 24 mServices[i].onBootCompleted(); 25 } 26 } 27 } 28 }, bootCompletedFilter); 29 30 IntentFilter localeChangedFilter = new IntentFilter(Intent.ACTION_LOCALE_CHANGED); 31 registerReceiver(new BroadcastReceiver() { 32 @Override 33 public void onReceive(Context context, Intent intent) { 34 if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) { 35 if (!mBootCompleted) return; 36 // Update names of SystemUi notification channels 37 NotificationChannels.createAll(context); 38 } 39 } 40 }, localeChangedFilter); 41 } else { 42 ...... 43 startSecondaryUserServicesIfNeeded(); 44 } 45 }
這裏說一下第9行的if-else邏輯,咱們知道Linux是多用戶操做系統,因此這個if-else語句就是判斷是系統用戶,仍是切換到了其它用戶。SystemUI大多數功能對全部用戶都是同樣的,只有少部分功能會由於不一樣的用戶而表現不同,好比通知、多任務功能等,這裏後面會再講到。若是是系統用戶就會走if中的流程,這裏註冊了兩個廣播接收器,用於監聽Intent.ACTION_BOOT_COMPLETED和Intent.ACTION_LOCALE_CHANGED。
Intent.ACTION_BOOT_COMPLETED是監聽開機啓動,這裏分析的Android9.0的系統源碼,當前系統中使用的是FBE加密方式(讀者請自行查閱FBE加密方式,這裏不作詳細介紹),這種方式下,要等到系統啓動並鎖屏界面解鎖後,在進入到桌面過程當中,系統纔會發送發送該廣播,因此接收該廣播的處理邏輯會比較延後。經過第15行和第18行能夠看到,該廣播只會處理一次,就會反註冊該廣播,之後就不會再接收了。在這個邏輯當中,第20行到第25行,判斷mServicesStarted變量,該變量表示SystemUIService是否已經啓動了,實際上因爲該廣播接收的時機比較延後,會在SystemUIService啓動完後才接收到該廣播,因此這裏面的代碼會在此時執行。第23行的mService[]數組存儲的是SystemUI的子服務,當整個系統啓動完成後,這裏面的每一個子服務都會執行onBootCompleted()方法,讓各個子服務知道系統啓動完成了,要作本身該作的事情了。mService[]的賦值以及它存儲的SytemUI子服務,下一節會詳細講解,這裏咱們只須要知道,這個過程發生在SystemUIService的啓動階段便可。
Intent.ACTION_LOCALE_CHANGED廣播是用於監聽設備當前區域設置已更改時發出的廣播,簡單來講就是修改語言時發出的廣播(暫時不知道其它動做是否也會發送該廣播)。
第42行就是在當前用戶不是系統用戶時的狀況,即切換用戶後的場景,該動做發生時系統是已經啓動了的,不會再觸發Intent.ACTION_BOOT_COMPLETED廣播。這裏看一看它的執行過程:
1 void startSecondaryUserServicesIfNeeded() { 2 String[] names = 3 getResources().getStringArray(R.array.config_systemUIServiceComponentsPerUser); 4 startServicesIfNeeded(names); 5 }
第2行和第4行其實就是啓動資源文件指定的功能,以下所示:
1 <string-array name="config_systemUIServiceComponentsPerUser" translatable="false"> 2 <item>com.android.systemui.Dependency</item> 3 <item>com.android.systemui.util.NotificationChannels</item> 4 <item>com.android.systemui.recents.Recents</item> 5 </string-array>
能夠看到包含了通知(第3行)和多任務(第4行),這幾個功能會因用戶不一樣而異,第2行是什麼功能暫時不清楚,讀者能夠本身查閱。另外咱們會發現,實際上這幾個子服務,在下一節的 config_systemUIServiceComponents數組資源中也都是包含的,也就正好對應了前面說的,切換到我的用戶後這幾個功能會因用戶不一樣而表現不一樣,須要從新加載一次。startSecondaryUserServicesIfNeeded方法的處理邏輯,在下一節會詳細講到,這裏我們只須要清楚這一塊的功能便可。
6、SystemUIService中onCreate方法處理邏輯
如前文所述,SystemUI經過「com.android.systemui.SystemUIService」這個服務來啓動,在Application的onCreate方法執行完後,就會執行本身的onCreate方法。下面看看SystemUIService啓動過程當中作了哪些工做:
1 public class SystemUIService extends Service { 2 @Override 3 public void onCreate() { 4 super.onCreate(); 5 ((SystemUIApplication) getApplication()).startServicesIfNeeded(); 6 ...... 7 } 8 ...... 9 }
該類中關鍵代碼是第5行代碼,其它的沒有什麼重要邏輯,繼續追蹤startServicesIfNeeded()方法:
1 //===========SystemUIApplication========== 2 public void startServicesIfNeeded() { 3 String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents); 4 startServicesIfNeeded(names); 5 }
第3行在資源文件中對應的數組以下所示,每一項都對應了一個子服務(這裏並非表示它們是Service,而是指某項功能模塊),實際上在Android O及之前的版本中,這些類都是以數組的形式保存在代碼中的。
1 <string-array name="config_systemUIServiceComponents" translatable="false"> 2 <item>com.android.systemui.Dependency</item> 3 <item>com.android.systemui.util.NotificationChannels</item> 4 <item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item> 5 <item>com.android.systemui.keyguard.KeyguardViewMediator</item> 6 <item>com.android.systemui.recents.Recents</item> 7 <item>com.android.systemui.volume.VolumeUI</item> 8 <item>com.android.systemui.stackdivider.Divider</item> 9 <item>com.android.systemui.SystemBars</item> 10 <item>com.android.systemui.usb.StorageNotification</item> 11 <item>com.android.systemui.power.PowerUI</item> 12 <item>com.android.systemui.media.RingtonePlayer</item> 13 <item>com.android.systemui.keyboard.KeyboardUI</item> 14 <item>com.android.systemui.pip.PipUI</item> 15 <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item> 16 <item>@string/config_systemUIVendorServiceComponent</item> 17 <item>com.android.systemui.util.leak.GarbageMonitor$Service</item> 18 <item>com.android.systemui.LatencyTester</item> 19 <item>com.android.systemui.globalactions.GlobalActionsComponent</item> 20 <item>com.android.systemui.ScreenDecorations</item> 21 <item>com.android.systemui.fingerprint.FingerprintDialogImpl</item> 22 <item>com.android.systemui.SliceBroadcastRelayHandler</item> 23 </string-array>
在Android Q上將第21行修改成了
1 <item>com.android.systemui.biometrics.BiometricDialogImpl</item>
就是將指紋識別功能改爲了生物識別功能,在Android Q上開始,除了指紋識別外,還增長了人臉識別。在原來的基礎上另外再添加了3條:
1 <item>com.android.systemui.SizeCompatModeActivityController</item> 2 <item>com.android.systemui.statusbar.notification.InstantAppNotifier</item> 3 <item>com.android.systemui.theme.ThemeOverlayController</item>
打開這每個類後,會發現它們都繼承自SystemUI類,SystemUI類是一個抽象類,提供了以下接口:
1 public abstract class SystemUI implements SysUiServiceProvider { 2 ...... 3 public abstract void start(); 4 5 protected void onConfigurationChanged(Configuration newConfig) { 6 } 7 8 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 9 } 10 11 protected void onBootCompleted() { 12 } 13 ...... 14 } 15 }
startServicesIfNeeded(names)方法源碼以下:
1 private SystemUI[] mServices; 2 private void startServicesIfNeeded(String[] services) { 3 ...... 4 mServices = new SystemUI[services.length]; 5 ...... 6 final int N = services.length; 7 for (int i = 0; i < N; i++) { 8 String clsName = services[i]; 9 Class cls; 10 try { 11 cls = Class.forName(clsName); 12 mServices[i] = (SystemUI) cls.newInstance(); 13 } catch (ClassNotFoundException ex) { 14 throw new RuntimeException(ex); 15 } catch (IllegalAccessException ex) { 16 throw new RuntimeException(ex); 17 } catch (InstantiationException ex) { 18 throw new RuntimeException(ex); 19 } 20 ...... 21 mServices[i].mContext = this; 22 mServices[i].mComponents = mComponents; 23 ...... 24 mServices[i].start(); 25 ...... 26 if (mBootCompleted) { 27 mServices[i].onBootCompleted(); 28 } 29 } 30 }
實際上就是經過反射的方式將前面的各個子服務類實例化,並執行這些對象中的start()方法,來啓動這些服務。這樣整個SystemUI就算啓動了,上述邏輯仍是比較簡單的。
咱們須要注意的是,這裏使用了模板模式。SystemUI是一個基類,其中定義了4個抽象或空方法,做爲模板指定了子類的行爲模式。資源文件中定義的衆多子服務類都是SystemUI的子類,既然都繼承自SystemUI類,那麼這些子類就有一些共同的行爲模式,在某些階段應該有什麼表現,只是具體如何表現因不一樣子類而異。好比說,在上述代碼中第24行和27行分別規定了SystemUI子類們在啓動時要執行start()方法,系統啓動後要執行onBootCompleted()方法,因此在這些子類中都重寫了這兩個方法,到必定的階段都會以回調的方式執行,可是具體要在這些方法中幹什麼,子類們本身說了算。這就是典型的模板模式使用,至於具體介紹和使用模板模式,這裏不展開講,讀者能夠自行查資料,該模式在Android系統中使用仍是很常見的,讀者最好能好好掌握。
到這裏爲止,SystemUI的啓動流程就介紹完了,這裏概括起來就是執行了以下幾個階段:
(1)系統啓動就緒後,SystemServer進程下達啓動SystemUIService的命令;
(2)SystemUI的SystemUiApplication中執行onCreate方法,註冊系統啓動廣播和區域設置更改廣播。
(3)SystemUI的SystemUIService中執行onCreate方法,啓動公共用戶的各項服務,各子服務執行onStart()回調方法。
(4)系統啓動後,第二步註冊的廣播會接收到系統啓動廣播,而後各個子服務執行onBootCompleted()回調方法。
(5)在切換都我的用戶時,再次加載因人而異的子服務功能。
7、SystemUI包含的功能模塊
上一節中經過數組的形式列出了SystemUI的子服務類,這些類都分別表示什麼功能呢?下面我簡單介紹其中幾項,讀者能夠根據名稱來對號入座。至於更詳細的介紹,有須要的話會專門寫一篇文章來作介紹。
(1)Status bars(狀態欄)
(2)Navigation bars(導航欄)
(3)Notification(通知)
(4)Keyguard(鎖屏)
(5)Quick settings(快速設置)
(6)Recent task panel(最近任務面板)
(7)VolumeUI(音量UI)
(8)Screenshot(截屏)
(9)PowerUI(電量UI)
(10)RingtonePlayer(鈴聲播放器)
(11)StackDivider(分屏)
(12)PipUI(畫中畫UI)
(13)Biometrics(生物識別解鎖功能,如指紋解鎖、人臉解鎖、虹膜解鎖等)
結語
SystemUI的啓動流程就介紹到這裏,因爲講得還算比較詳細,因此涉及的內容及細節很多,必定會有些描述不許確或者不妥的地方,若是發現,請讀者不吝賜教。另外因爲篇幅有限,有些地方仍是僅提到或者簡單介紹而已,好比FBE加密,模板模式,Lambda表達式等,在平時系統開發中都會常常碰到,讀者均可以繼續拓展深刻學習。