Android 7.0 SystemUI 之启动和状态栏和导航栏简介
一、SystemUI 是什么
首先SystemUI 是一个系统应用,apk路径位于/system/priv-app
源码路径位于:/framework/base/packages/SystemUI
它负责的功能如下:
- 状态栏信息的展示:比如电量信息,时间,wifi状态等
- 通知栏消息
- 壁纸管理
- 截图功能
- 近期任务栏显示,比如长按home键显示最近使用的app
- 录制屏幕功能
- 截图服务
以下是7.0 SystemUI 的代码截图
二、SystemUI 的启动
SystemUI 是在SystemServer里的AMS实例的systemReady方法里调用startSystemUi方法启动
SystemServer路径:/base/services/java/com/android/server/SystemServer.java
mActivityManagerService.systemReady(new Runnable() { ...... static final void startSystemUi(Context context) { Intent intent = new Intent(); intent.setComponent(new ComponentName("com.android.systemui", "com.android.systemui.SystemUIService")); intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING); context.startServiceAsUser(intent, UserHandle.SYSTEM); } ......
在这个方法里启动一个SystemUIService服务.
public class SystemUIService extends Service { @Override public void onCreate() { super.onCreate(); ((SystemUIApplication) getApplication()).startServicesIfNeeded(); } ......
在onCreate方法中会调用SystemUIApplication的startServicesIfNeeded方法,这个方法会调用 startServicesIfNeeded(SERVICES)方法启动一系列服务(并不是真正的服务,都继承自SystemUI),可以这么说SystemUI就是一个容器,里面装有负责不同功能的模块。
public class SystemUIApplication extends Application { ...... private final Class<?>[] SERVICES = new Class[] { com.android.systemui.tuner.TunerService.class, com.android.systemui.keyguard.KeyguardViewMediator.class, com.android.systemui.recents.Recents.class, com.android.systemui.volume.VolumeUI.class, Divider.class, com.android.systemui.statusbar.SystemBars.class, com.android.systemui.usb.StorageNotification.class, com.android.systemui.power.PowerUI.class, com.android.systemui.media.RingtonePlayer.class, com.android.systemui.keyboard.KeyboardUI.class, com.android.systemui.tv.pip.PipUI.class, com.android.systemui.shortcut.ShortcutKeyDispatcher.class }public void startServicesIfNeeded() { startServicesIfNeeded(SERVICES) } ......
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
startServicesIfNeeded方法会遍历services这个数组,依次调用service的start方法启动服务
private void startServicesIfNeeded(Class<?>[] services) { if (mServicesStarted) { return; } if (!mBootCompleted) { if ("1".equals(SystemProperties.get("sys.boot_completed"))) { mBootCompleted = true; if (DEBUG) Log.v(TAG, "BOOT_COMPLETED was already sent"); } } final int N = services.length; for (int i=0; i<N; i++) { Class<?> cl = services[i]; if (DEBUG) Log.d(TAG, "loading: " + cl); try { Object newService = SystemUIFactory.getInstance().createInstance(cl); mServices[i] = (SystemUI) ((newService == null) ? cl.newInstance() : newService); } catch (IllegalAccessException ex) { throw new RuntimeException(ex); } catch (InstantiationException ex) { throw new RuntimeException(ex); } mServices[i].start(); if (mBootCompleted) { mServices[i].onBootCompleted(); } } mServicesStarted = true; }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
这里以com.android.systemui.statusbar.SystemBars.class为例,讲解一下
三、状态栏和导航栏 的启动
SystemBars的start方法会创建一个ServiceMonitor(服务监听者),会进入到ServiceMonitor的start方法
public class SystemBars extends SystemUI implements ServiceMonitor.Callbacks { ...... public void start() { mServiceMonitor = new ServiceMonitor(TAG, DEBUG, mContext, Settings.Secure.BAR_SERVICE_COMPONENT, this); mServiceMonitor.start(); } ...... }
在ServiceMonitor的start方法启动
public class ServiceMonitor { ...... public void start() { ...... mHandler.sendEmptyMessage(MSG_START_SERVICE); } ...... }
在Handler里处理这个MSG_START_SERVICE
public class ServiceMonitor { ...... private final Handler mHandler = new Handler() { public void handleMessage(Message msg) { switch(msg.what) { case MSG_START_SERVICE: startService(); break; case MSG_CONTINUE_START_SERVICE: continueStartService(); break; case MSG_STOP_SERVICE: stopService(); break; case MSG_PACKAGE_INTENT: packageIntent((Intent)msg.obj); break; case MSG_CHECK_BOUND: checkBound(); break; case MSG_SERVICE_DISCONNECTED: serviceDisconnected((ComponentName)msg.obj); break; } } }; ...... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
startService方法如下
public class ServiceMonitor { ...... private void startService() { mServiceName = getComponentNameFromSetting(); if (mServiceName == null) { mBound = false; mCallbacks.onNoService(); } else { long delay = mCallbacks.onServiceStartAttempt(); mHandler.sendEmptyMessageDelayed(MSG_CONTINUE_START_SERVICE, delay); } } ...... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
这里对mServiceName是否为空进行判断,总之无论如何它最终都会启动这个服务。
回调SystemBars的onNoService里创建StatusBar
public class SystemBars extends SystemUI implements ServiceMonitor.Callbacks { ...... @Override public void onNoService() { if (DEBUG) Log.d(TAG, "onNoService"); createStatusBarFromConfig(); } ......
private void createStatusBarFromConfig() { final String clsName = mContext.getString(R.string.config_statusBarComponent); Class<?> cls = null; try { cls = mContext.getClassLoader().loadClass(clsName); } catch (Throwable t) { throw andLog("Error loading status bar component: " + clsName, t); } try { mStatusBar = (BaseStatusBar) cls.newInstance(); } catch (Throwable t) { throw andLog("Error creating status bar component: " + clsName, t); } mStatusBar.mContext = mContext; mStatusBar.mComponents = mComponents; mStatusBar.start(); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
在createStatusBarFromConfig方法里会获取一个config_statusBarComponent的字符串值,这个值就是PhoneStatusBar的clasName
所以这里的mStatusBar是PhoneStatusBar实例,启动了PhoneStatusBar
PhoneStatusBar的start方法
public class PhoneStatusBar extends BaseStatusBar implements DemoMode, DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,HeadsUpManager.OnHeadsUpChangedListener { ...... public void start() { ...... super.start(); ...... addNavigationBar(); ...... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
它会回调父类BaseStatusBar 的start方法
3.1、 super.start()
public abstract class BaseStatusBar extends SystemUI implements CommandQueue.Callbacks, ActivatableNotificationView.OnActivatedListener, ExpandableNotificationRow.ExpansionLogger, NotificationData.Environment, ExpandableNotificationRow.OnExpandClickListener, OnGutsClosedListener { ...... public void start() { ...... mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); ...... mCommandQueue = new CommandQueue(this); int[] switches = new int[9]; ArrayList<IBinder> binders = new ArrayList<IBinder>(); ArrayList<String> iconSlots = new ArrayList<>(); ArrayList<StatusBarIcon> icons = new ArrayList<>(); Rect fullscreenStackBounds = new Rect(); Rect dockedStackBounds = new Rect(); try { mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders, fullscreenStackBounds, dockedStackBounds); } catch (RemoteException ex) { } createAndAddWindows(); ...... } ...... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
BaseStatusBar进行一些设置,获取了IStatusBarService实例并注册一些信息到IStatusBarService中,IStatusBarService是一个系统服务,BaseStatusBar将自己注册到IStatusBarService之中,IStatusBarService会把操作状态栏和导航栏的请求转发给BaseStatusBar
为了保证SystemUI意外退出后不会发生信息丢失,IStatusBarService保存了所有需要状态栏与导航栏进行显示或处理的信息副本。 在注册时将一个继承自IStatusBar.Stub的CommandQueue的实例注册到IStatusBarService以建立通信,并将信息副本取回。
public class CommandQueue extends IStatusBar.Stub {
IStatusBarService的真身是StatusBarManagerService
路径:./services/core/java/com/android/server/statusbar/StatusBarManagerService.java
它的注册方法做一些数据的初始化
public class StatusBarManagerService extends IStatusBarService.Stub { ...... public void registerStatusBar(IStatusBar bar, List<String> iconSlots, List<StatusBarIcon> iconList, int switches[], List<IBinder> binders, Rect fullscreenStackBounds, Rect dockedStackBounds) { enforceStatusBarService(); mBar = bar; synchronized (mIcons) { for (String slot : mIcons.keySet()) { iconSlots.add(slot); iconList.add(mIcons.get(slot)); } } synchronized (mLock) { switches[0] = gatherDisableActionsLocked(mCurrentUserId, 1); switches[1] = mSystemUiVisibility; switches[2] = mMenuVisible ? 1 : 0; switches[3] = mImeWindowVis; switches[4] = mImeBackDisposition; switches[5] = mShowImeSwitcher ? 1 : 0; switches[6] = gatherDisableActionsLocked(mCurrentUserId, 2); switches[7] = mFullscreenStackSysUiVisibility; switches[8] = mDockedStackSysUiVisibility; binders.add(mImeToken); fullscreenStackBounds.set(mFullscreenStackBounds); dockedStackBounds.set(mDockedStackBounds); } } ...... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
这几者的关系如下
回到PhoneStatusBar中, 父类BaseStatusBar中的createAndAddWindows为抽象方法,由子类实现,看下PhoneStatusBar的
createAndAddWindows
@Override public void createAndAddWindows() { addStatusBarWindow(); }
方法实现如下
private void addStatusBarWindow() { makeStatusBarView(); mStatusBarWindowManager = new StatusBarWindowManager(mContext); mRemoteInputController = new RemoteInputController(mStatusBarWindowManager, mHeadsUpManager); mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight()); }
看下makeStatusBarView方法
makeStatusBarView的方法里调用 inflateStatusBarWindow(context)加载布局
protected void inflateStatusBarWindow(Context context) { mStatusBarWindow = (StatusBarWindowView) View.inflate(context, R.layout.super_status_bar, null); }
这里介绍下布局
状态栏布局介绍
整个状态栏的父布局是R.layout.super_status_bar,对应的是StatusBarWindowView这个自定义布局.
<com.android.systemui.statusbar.phone.StatusBarWindowView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:sysui="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true"> ...... <include layout="@layout/status_bar" android:layout_width="match_parent" android:layout_height="@dimen/status_bar_height" /> <include layout="@layout/brightness_mirror" /> <ViewStub android:id="@+id/fullscreen_user_switcher_stub" android:layout="@layout/car_fullscreen_user_switcher" android:layout_width="match_parent" android:layout_height="match_parent"/> <include layout="@layout/status_bar_expanded" android:layout_width="match_parent" android:layout_height="match_parent" android:visibility="gone" /></com.android.systemui.statusbar.phone.StatusBarWindowView>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
我这里以主要的布局层次做介绍,结合图片分析会更加清楚
StatusBarWindowView里有几个主要的布局
- layout/status_bar
- layout/brightness_mirror
- layout/status_bar_expanded
如下图
1.layout/status_bar
这个是正常状态下(未下拉的状态栏图标区域)
这个布局对应的是PhoneStatusBarView
<com.android.systemui.statusbar.phone.PhoneStatusBarView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" android:id="@+id/status_bar" android:background="@drawable/system_bar_background" android:orientation="vertical" android:focusable="false" android:descendantFocusability="afterDescendants" > ...... <LinearLayout android:id="@+id/status_bar_contents" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingStart="6dp" android:paddingEnd="8dp" android:orientation="horizontal" > <com.android.systemui.statusbar.AlphaOptimizedFrameLayout android:id="@+id/notification_icon_area" android:layout_width="0dip" android:layout_height="match_parent" android:layout_weight="1" android:orientation="horizontal" /> <com.android.keyguard.AlphaOptimizedLinearLayout android:id="@+id/system_icon_area" android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="horizontal" > <include layout="@layout/system_icons" /> <com.android.systemui.statusbar.policy.Clock android:id="@+id/clock" android:textAppearance="@style/TextAppearance.StatusBar.Clock" android:layout_width="wrap_content" android:layout_height="match_parent" android:singleLine="true" android:paddingStart="@dimen/status_bar_clock_starting_padding" android:paddingEnd="@dimen/status_bar_clock_end_padding" android:gravity="center_vertical|start" /> </com.android.keyguard.AlphaOptimizedLinearLayout> </LinearLayout></com.android.systemui.statusbar.phone.PhoneStatusBarView>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
以下是细节图,连线表示层次结构
其中,状态栏的区域分为以下几种
- 通知栏图标,在状态栏的最左侧显示通知信息,比如来了一个短信,那么就会弹出一个短信图标
- 时间信息,显示一个时间,比如上午9:58
- 信号图标,显示手机信号,wifi信号等
- 电量图标,显示当前电量状态
- 状态图标,wifi,蓝牙等开关状态
2.@layout/brightness_mirror
这个布局就是中间那个调整亮度的seekBar.没啥好介绍的.
3.@layout/status_bar_expanded
这个布局是下拉时的状态栏的布局
<com.android.systemui.statusbar.phone.NotificationPanelView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui" android:id="@+id/notification_panel" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/transparent" > <include layout="@layout/keyguard_status_view" android:layout_height="wrap_content" android:visibility="gone" /> <com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="@integer/notification_panel_layout_gravity" android:id="@+id/notification_container_parent" android:clipToPadding="false" android:clipChildren="false"> <com.android.systemui.AutoReinflateContainer android:id="@+id/qs_auto_reinflate_container" android:layout="@layout/qs_panel" android:layout_width="@dimen/notification_panel_width" android:layout_height="match_parent" android:layout_gravity="@integer/notification_panel_layout_gravity" android:clipToPadding="false" android:clipChildren="false" /> <com.android.systemui.statusbar.stack.NotificationStackScrollLayout android:id="@+id/notification_stack_scroller" android:layout_width="@dimen/notification_panel_width" android:layout_height="match_parent" android:layout_gravity="@integer/notification_panel_layout_gravity" android:layout_marginBottom="@dimen/close_handle_underlap" /> <ViewStub android:id="@+id/keyguard_user_switcher" android:layout="@layout/keyguard_user_switcher" android:layout_height="match_parent" android:layout_width="match_parent" /> <include layout="@layout/keyguard_status_bar" android:visibility="invisible" /> </com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer> <include layout="@layout/keyguard_bottom_area" android:visibility="gone" /></com.android.systemui.statusbar.phone.NotificationPanelView>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
细节图
创建完布局后,就会添加窗口到WindowManager里,这样状态栏就创建完成了.接下来会回到3.2 addNavigationBar()的步骤中.
3.2、addNavigationBar
这个方法是添加底部的导航栏的,就是那些home键,back键所在的区域.
public class PhoneStatusBar extends BaseStatusBar implements DemoMode, DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,HeadsUpManager.OnHeadsUpChangedListener { ...... protected void addNavigationBar() { if (mNavigationBarView == null) return; prepareNavigationBarView(); mWindowManager.addView(mNavigationBarView, getNavigationBarLayoutParams()); } ...... }
在这个方法里先初始化导航栏,然后把导航栏添加到窗口中.
prepareNavigationBarView()
public class PhoneStatusBar extends BaseStatusBar implements DemoMode, DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,HeadsUpManager.OnHeadsUpChangedListener { ...... private void prepareNavigationBarView() { mNavigationBarView.reorient(); ButtonDispatcher recentsButton = mNavigationBarView.getRecentsButton(); recentsButton.setOnClickListener(mRecentsClickListener); recentsButton.setOnTouchListener(mRecentsPreloadOnTouchListener); recentsButton.setLongClickable(true); recentsButton.setOnLongClickListener(mRecentsLongClickListener); ButtonDispatcher backButton = mNavigationBarView.getBackButton(); backButton.setLongClickable(true); backButton.setOnLongClickListener(mLongPressBackListener); ButtonDispatcher homeButton = mNavigationBarView.getHomeButton(); homeButton.setOnTouchListener(mHomeActionListener); homeButton.setOnLongClickListener(mLongPressHomeListener); mAssistManager.onConfigurationChanged(); } ...... }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
四、结束
关于SystemUI的状态栏和导航栏就介绍完了,讲的很浅显,只是从整体上梳理了下流程.
原文地址: http://blog.csdn.net/qq_31530015/article/details/53507968