SystemUI是系统启动中第一个用户肉眼可见的应用,系统为用户提供的系统级别的信息显示与交互的一套UI组件,其功能包罗万象,比如开机后看到的锁屏界面,充电时充电界面,状态栏,导航栏,多任务栏等,都是与Android手机用户息息相关的功能。
尽管从表现形式上看,SystemUI和普通的Android APP有较大的差别,但其本质和普通APP并没有什么差别,也是以apk的形式存在,也是通过Android的4大组件中的Activity、Service、BroadcastReceiver来接受外界的请求并执行相关的操作,只不过它们所接受的请求主要来自各个系统服务而已。不同于一般的 app,它不可卸载也不可以被第三方应用替换。
/system/priv-app/SystemUI/
先找到 framework/base/service/java/com/android/server/SystemServer.java 文件,里面有个main()方法,main 方法如下:
public static void main(String[] args){
new SystemServer().run()
}
main 方法里启动了 run() 方法,而在 run 方法中调用了 startBootstrapServices()
方法和 startOtherServices()
方法,在 startOtherServices()
里 mActivityManagerService.systemReady
创建线程去执行startSystemUi(context)
,这里将启动 SystemUI。具体方法如下:
static final void startSystemUi(Context context, WindowManagerService windowManager) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.systemui",
"com.android.systemui.SystemUIService"));
intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
//Slog.d(TAG, "Starting service: " + intent);
context.startServiceAsUser(intent, UserHandle.SYSTEM);
windowManager.onSystemUiStarted();
}
然后我们进入设置启动 systemui 程序的 SystemUIService
文件里,该文件在framework/base/packages/SystemUI/src/com/android/systemui/SystemUIService.java.我们看该文件的onCreate()
方法。方法如下:
37 public void onCreate() {
38 super.onCreate();
39 ((SystemUIApplication) getApplication()).startServicesIfNeeded();
40
41 // For debugging RescueParty
42 if (Build.IS_DEBUGGABLE && SystemProperties.getBoolean("debug.crash_sysui", false)) {
43 throw new RuntimeException();
44 }
45
46 if (Build.IS_DEBUGGABLE) {
47 // b/71353150 - looking for leaked binder proxies
48 BinderInternal.nSetBinderProxyCountEnabled(true);
49 BinderInternal.nSetBinderProxyCountWatermarks(1000,900);
50 BinderInternal.setBinderProxyCountCallback(
51 new BinderInternal.BinderProxyLimitListener() {
52 @Override
53 public void onLimitReached(int uid) {
54 Slog.w(SystemUIApplication.TAG,
55 "uid " + uid + " sent too many Binder proxies to uid "
56 + Process.myUid());
57 }
58 }, Dependency.get(Dependency.MAIN_HANDLER));
59 }
60 }
可以看到有一句((SystemUIApplication) getApplication()).startServicesIfNeeded()
,这句很关键,我们再进入 startServicesIfNeeded()
,看看具体是如何启动系统服务的。该方法如下:
144 private void startServicesIfNeeded(String[] services) {
145 if (mServicesStarted) {
146 return;
147 }
148 mServices = new SystemUI[services.length];
149
150 if (!mBootCompleted) {
151 // check to see if maybe it was already completed long before we began
152 // see ActivityManagerService.finishBooting()
153 if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
154 mBootCompleted = true;
155 if (DEBUG) Log.v(TAG, "BOOT_COMPLETED was already sent");
156 }
157 }
158
159 Log.v(TAG, "Starting SystemUI services for user " +
160 Process.myUserHandle().getIdentifier() + ".");
161 TimingsTraceLog log = new TimingsTraceLog("SystemUIBootTiming",
162 Trace.TRACE_TAG_APP);
163 log.traceBegin("StartServices");
164 final int N = services.length;
165 for (int i = 0; i < N; i++) {
166 String clsName = services[i];
167 if (DEBUG) Log.d(TAG, "loading: " + clsName);
168 log.traceBegin("StartServices" + clsName);
169 long ti = System.currentTimeMillis();
170 Class cls;
171 try {
172 cls = Class.forName(clsName);
173 mServices[i] = (SystemUI) cls.newInstance();
174 } catch(ClassNotFoundException ex){
175 throw new RuntimeException(ex);
176 } catch (IllegalAccessException ex) {
177 throw new RuntimeException(ex);
178 } catch (InstantiationException ex) {
179 throw new RuntimeException(ex);
180 }
181
182 mServices[i].mContext = this;
183 mServices[i].mComponents = mComponents;
184 if (DEBUG) Log.d(TAG, "running: " + mServices[i]);
185 mServices[i].start();
186 log.traceEnd();
187
188 // Warn if initialization of component takes too long
189 ti = System.currentTimeMillis() - ti;
190 if (ti > 1000) {
191 Log.w(TAG, "Initialization of " + cls.getName() + " took " + ti + " ms");
192 }
193 if (mBootCompleted) {
194 mServices[i].onBootCompleted();
195 }
196 }
197 log.traceEnd();
198 Dependency.get(PluginManager.class).addPluginListener(
199 new PluginListener<OverlayPlugin>() {
200 private ArraySet<OverlayPlugin> mOverlays;
201
202 @Override
203 public void onPluginConnected(OverlayPlugin plugin, Context pluginContext) {
204 StatusBar statusBar = getComponent(StatusBar.class);
205 if (statusBar != null) {
206 plugin.setup(statusBar.getStatusBarWindow(),
207 statusBar.getNavigationBarView());
208 }
209 // Lazy init.
210 if (mOverlays == null) mOverlays = new ArraySet<>();
211 if (plugin.holdStatusBarOpen()) {
212 mOverlays.add(plugin);
213 Dependency.get(StatusBarWindowManager.class).setStateListener(b ->
214 mOverlays.forEach(o -> o.setCollapseDesired(b)));
215 Dependency.get(StatusBarWindowManager.class).setForcePluginOpen(
216 mOverlays.size() != 0);
217
218 }
219 }
220
221 @Override
222 public void onPluginDisconnected(OverlayPlugin plugin) {
223 mOverlays.remove(plugin);
224 Dependency.get(StatusBarWindowManager.class).setForcePluginOpen(
225 mOverlays.size() != 0);
226 }
227 }, OverlayPlugin.class, true /* Allow multiple plugins */);
228
229 mServicesStarted = true;
230 }
其中有一个 for 循环,循环里第一句就是将 service[i] 赋值给 clsName, 那么service里存的是什么呢?找到 service[i] 的赋值,是拿到每个和 SystemUI 相关的类的反射,存到了 service[] 里,然后赋值给cl,紧接着将通过反射将其转化为具体类的对象,存到了mService[i]数组里,最后对象调 start()
方法启动相关类的服务,启动完成后,回调 onBootCompleted( )
方法。
mService[i] 里的值不同时,调用的 start() 方法也不相同,这里我们以S ystemBars 的 start()
为例,所以mService[i].start() 先认为是 SystemBars.start()
.
SystemBars.java
位于 /frameworks/base/packages/SystemUI/src/com/android/systemui/SystemBars.java
,找到 start()
方法:
39 @Override
40 public void start() {
41 if (DEBUG) Log.d(TAG, "start");
42 createStatusBarFromConfig();
43 }
可以看到方法中调用了CreateStatusBarFromConfig()
该方法如下:
52 private void createStatusBarFromConfig() {
53 if (DEBUG) Log.d(TAG, "createStatusBarFromConfig");
54 final String clsName = mContext.getString(R.string.config_statusBarComponent);
55 if (clsName == null || clsName.length() == 0) {
56 throw andLog("No status bar component configured", null);
57 }
58 Class<?> cls = null;
59 try {
60 cls = mContext.getClassLoader().loadClass(clsName);
61 } catch (Throwable t) {
62 throw andLog("Error loading status bar component: " + clsName, t);
63 }
64 try {
65 mStatusBar = (SystemUI) cls.newInstance();
66 } catch (Throwable t) {
67 throw andLog("Error creating status bar component: " + clsName, t);
68 }
69 mStatusBar.mContext = mContext;
70 mStatusBar.mComponents = mComponents;
71 mStatusBar.start();
72 if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName());
73 }
从中可以知道,该方法中先读取 value/config.xml
文件中 config_statusBarComponent
的值,这里为:com.android.systemui.statusbar.phone.StatusBar
,然后通过反射得到了 StatusBar
对象,最后的 mStartus.start()
也就等于 StatusBar.start()
,进入该方法,会发现,里面调用了super.start()
,也就是先执行了父类的 start()
,其父类为 /frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
,该类的start()方法较多,就不放出来了,我们看重点,找到里面有调用一个 createAndAddWindows()
,该方法为抽象方法,则会调用它的子类的方法,这里也就是 /frameworks/base/packages/SystemUI/src/com/android/systemui/SystemBars.java
的 createAndAddWindows()
方法,如下:
2865 public void createAndAddWindows() {
2866 addStatusBarWindow();
2867 }
2868
2869 private void addStatusBarWindow() {
2870 makeStatusBarView();
2871 mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class);
2872 mRemoteInputManager.setUpWithPresenter(this, mEntryManager, this,
2873 new RemoteInputController.Delegate() {
2874 public void setRemoteInputActive(NotificationData.Entry entry,
2875 boolean remoteInputActive) {
2876 mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
2877 entry.row.notifyHeightChanged(true /* needsAnimation */);
2878 updateFooter();
2879 }
2880 public void lockScrollTo(NotificationData.Entry entry) {
2881 mStackScroller.lockScrollTo(entry.row);
2882 }
2883 public void requestDisallowLongPressAndDismiss() {
2884 mStackScroller.requestDisallowLongPress();
2885 mStackScroller.requestDisallowDismiss();
2886 }
2887 });
2888 mRemoteInputManager.getController().addCallback(mStatusBarWindowManager);
2889 mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight());
2890 }
createAndAddWindows()
里只调用了 addStaBarWindow()
方法,而在该方法里,调用了makeStartusBarView
,看名字就知道该方法关键,意为构建statusBar
视图。
790 // ================================================================================
791 // Constructing the view
792 // ================================================================================
793 protected void makeStatusBarView() {
794 final Context context = mContext;
795 updateDisplaySize(); // populates mDisplayMetrics
796 updateResources();
797 updateTheme();
798
799 inflateStatusBarWindow(context);
800 mStatusBarWindow.setService(this);
801 mStatusBarWindow.setOnTouchListener(getStatusBarWindowTouchListener());
802
803 // TODO: Deal with the ugliness that comes from having some of the statusbar broken out
804 // into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
805 mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel);
806 mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller);
807 mZenController.addCallback(this);
...
该方法很长,省略后边的,里面有inflateStatusBarWindow()
,进入该方法,可以看到,这么一句:
1206 protected void inflateStatusBarWindow(Context context) {
1207 mStatusBarWindow = (StatusBarWindowView) View.inflate(context,
1208 R.layout.super_status_bar, null);
1209 }
然后,我们通过(/frameworks/base/packages/SystemUI/res/layout/super_status_bar.xml)
super_status_bar.xml 的分析 SystemBars 的大致视图构成了,super_status_bar.xml 代码如下:
20<!-- This is the combined status bar / notification panel window. -->
21<com.android.systemui.statusbar.phone.StatusBarWindowView
22 xmlns:android="http://schemas.android.com/apk/res/android"
23 xmlns:sysui="http://schemas.android.com/apk/res-auto"
24 android:layout_width="match_parent"
25 android:layout_height="match_parent"
26 android:fitsSystemWindows="true">
27
28 <com.android.systemui.statusbar.BackDropView
29 android:id="@+id/backdrop"
30 android:layout_width="match_parent"
31 android:layout_height="match_parent"
32 android:visibility="gone"
33 sysui:ignoreRightInset="true"
34 >
35 <ImageView android:id="@+id/backdrop_back"
36 android:layout_width="match_parent"
37 android:scaleType="centerCrop"
38 android:layout_height="match_parent" />
39 <ImageView android:id="@+id/backdrop_front"
40 android:layout_width="match_parent"
41 android:layout_height="match_parent"
42 android:scaleType="centerCrop"
43 android:visibility="invisible" />
44 </com.android.systemui.statusbar.BackDropView>
45
46 <com.android.systemui.statusbar.ScrimView
47 android:id="@+id/scrim_behind"
48 android:layout_width="match_parent"
49 android:layout_height="match_parent"
50 android:importantForAccessibility="no"
51 sysui:ignoreRightInset="true"
52 />
53
54 <FrameLayout
55 android:id="@+id/status_bar_container"
56 android:layout_width="match_parent"
57 android:layout_height="wrap_content" />
58
59 <ViewStub android:id="@+id/fullscreen_user_switcher_stub"
60 android:layout="@layout/car_fullscreen_user_switcher"
61 android:layout_width="match_parent"
62 android:layout_height="match_parent"/>
63
64 <include layout="@layout/status_bar_expanded"
65 android:layout_width="match_parent"
66 android:layout_height="match_parent"
67 android:visibility="invisible" />
68
69 <include layout="@layout/brightness_mirror" />
70
71 <com.android.systemui.statusbar.ScrimView
72 android:id="@+id/scrim_in_front"
73 android:layout_width="match_parent"
74 android:layout_height="match_parent"
75 android:importantForAccessibility="no"
76 sysui:ignoreRightInset="true"
77 />
78
79</com.android.systemui.statusbar.phone.StatusBarWindowView>
super_status_bar.xml
super_status_bar.xml
中 include
了一个名称为 brightness_mirror
的布局文件,这里的 brightness_mirror
便是系统状态栏的布局文件。
super_status_bar.xml
中 include
了一个名称为 status_bar_expanded
的布局文件,status_bar_expanded
便是下拉的通知窗口的布局文件。
SystemUI启动流程图:
出处:https://my.oschina.net/u/920274/blog/3007114