SystemUI之启动流程(一)

前言

SystemUI是系统启动中第一个用户肉眼可见的应用,系统为用户提供的系统级别的信息显示与交互的一套UI组件,其功能包罗万象,比如开机后看到的锁屏界面,充电时充电界面,状态栏,导航栏,多任务栏等,都是与Android手机用户息息相关的功能。

SystemUI概览

  • SystemUI属于系统级的apk,位置在frameworks\base\packages\SystemUI,主要功能有:
  • StatusBar:通知消息提示和状态展现
  • NavigationBar:返回,HOME,Recent
  • KeyGuard:锁屏模块可以看做单独的应用,提供基本的手机个人隐私保护
  • Recents:近期应用管理,以堆叠栈的形式展现。
  • Notification Panel:展示系统或应用通知内容。提供快速系统设置开关。
  • VolumeUI:来用展示或控制音量的变化:媒体音量、铃声音量与闹钟音量
  • 截屏界面:长按电源键+音量下键后截屏,用以展示截取的屏幕照片/内容
  • PowerUI:主要处理和Power相关的事件,比如省电模式切换、电池电量变化和开关屏事件等。
  • RingtonePlayer:铃声播放
  • StackDivider:控制管理分屏
  • PipUI:提供对于画中画模式的管理

尽管从表现形式上看,SystemUI和普通的Android APP有较大的差别,但其本质和普通APP并没有什么差别,也是以apk的形式存在,也是通过Android的4大组件中的Activity、Service、BroadcastReceiver来接受外界的请求并执行相关的操作,只不过它们所接受的请求主要来自各个系统服务而已。不同于一般的 app,它不可卸载也不可以被第三方应用替换。

SystemUI APK路径:

/system/priv-app/SystemUI/

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.javacreateAndAddWindows()方法,如下:

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.xmlinclude 了一个名称为 brightness_mirror的布局文件,这里的 brightness_mirror便是系统状态栏的布局文件。
super_status_bar.xmlinclude 了一个名称为 status_bar_expanded 的布局文件,status_bar_expanded 便是下拉的通知窗口的布局文件。

SystemUI启动流程图:

SystemUI之启动流程(一)_第1张图片
SystemUI之启动流程(一)_第2张图片

出处:https://my.oschina.net/u/920274/blog/3007114

你可能感兴趣的:(Android源码分析)