ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
如7.1.1节所述,状态栏与导航栏的启动由其PhoneStatusBar.start()完成。参考其实现: **PhoneStatusBar.java-->PhoneStatusBar.start()** ``` public void start() { ...... // **① 调用父类BaseStatusBar的start()方法进行初始化。** super.start(); // 创建导航栏的窗口 addNavigationBar(); // **② 创建PhoneStatusBarPolicy。**PhoneStatusBarPolicy定义了系统通知图标的设置策略 mIconPolicy = new PhoneStatusBarPolicy(mContext); } ``` 参考BaseStatusBar.start()的实现,这段代码比较长,并且涉及到了本章随后会详细介绍的内容。因此倘若读者阅读起来比较吃力可以仅关注那三个关键步骤。在完成本章的学习之后再回过头来阅读这部分代码便会发现十分简单了。 **BaseStatusBar-->BaseStatusBar.start()** ``` public void start() { /* 由于状态栏的窗口不属于任何一个Activity,所以需要使用第6章所介绍的WindowManager 进行窗口的创建 */ mWindowManager = (WindowManager)mContext .getSystemService(Context.WINDOW_SERVICE); /* 在第4章介绍窗口的布局时曾经提到状态栏的存在对窗口布局有着重要的影响。因此状态栏中 所发生的变化有必要通知给WMS */ mWindowManagerService = WindowManagerGlobal.getWindowManagerService(); ...... /*mProvisioningOberver是一个ContentObserver。 它负责监听Settings.Global.DEVICE_PROVISIONED设置的变化。这一设置表示此设备是否已经 归属于某一个用户。比如当用户打开一个新购买的设备时,初始化设置向导将会引导用户阅读使用条款、 设置帐户等一系列的初始化操作。在初始化设置向导完成之前, Settings.Global.DEVICE_PROVISIONED的值为false,表示这台设备并未归属于某 一个用户。 当设备并未归属于某以用户时,状态栏会禁用一些功能以避免信息的泄露。mProvisioningObserver 即是用来监听设备归属状态的变化,以禁用或启用某些功能 */ mProvisioningObserver.onChange(false); // set up mContext.getContentResolver().registerContentObserver( Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), true, mProvisioningObserver); /* **① 获取IStatusBarService的实例。**IStatusBarService是一个系统服务,由ServerThread 启动并常驻system_server进程中。IStatusBarService为那些对状态栏感兴趣的其他系统服务定 义了一系列API,然而对SystemUI而言,它更像是一个客户端。因为IStatusBarService会将操作 状态栏的请求发送给SystemUI,并由后者完成请求 */ mBarService = IStatusBarService.Stub.asInterface( ServiceManager.getService(Context.STATUS_BAR_SERVICE)); /* 随后BaseStatusBar将自己注册到IStatusBarService之中。以此声明本实例才是状态栏的真正 实现者,IStatusBarService会将其所接受到的请求转发给本实例。 “天有不测风云”,SystemUI难免会因为某些原因使得其意外终止。而状态栏中所显示的信息并不属于状态 栏自己,而是属于其他的应用程序或是其他的系统服务。因此当SystemUI重新启动时,便需要恢复其 终止前所显示的信息以避免信息的丢失。为此,IStatusBarService中保存了所有的需要状态栏进行显 示的信息的副本,并在新的状态栏实例启动后,这些副本将会伴随着注册的过程传递给状态栏并进行显示, 从而避免了信息的丢失。 从代码分析的角度来看,这一从IstatusBarService中取回信息副本的过程正好完整地体现了状态栏 所能显示的信息的类型*/ /*iconList是向IStatusBarService进行注册的参数之一。它保存了用于显示在状态栏的系统状态 区中的状态图标列表。在完成注册之后,IStatusBarService将会在其中填充两个数组,一个字符串 数组用于表示状态的名称,一个StatusBarIcon类型的数组用于存储需要显示的图标资源。 关于系统状态区的工作原理将在7.2.3节介绍*/ StatusBarIconList iconList = new StatusBarIconList(); /*notificationKeys和StatusBarNotification则存储了需要显示在状态栏的通知区中通知信息。 前者存储了一个用Binder表示的通知发送者的ID列表。而notifications则存储了通知列表。二者 通过索引号一一对应。关于通知的工作原理将在7.2.2节介绍 */ ArrayList<IBinder> notificationKeys = newArrayList<IBinder>(); ArrayList<StatusBarNotification> notifications = newArrayList<StatusBarNotification>(); /*mCommandQueue是CommandQueue类的一个实例。CommandQueue继承自IStatusBar.Stub。 因此它是IStatusBar的Bn端。在完成注册后,这一Binder对象的Bp端将会保存在 IStatusBarService之中。因此它是IStatusBarService与BaseStatusBar进行通信的桥梁。 */ mCommandQueue= new CommandQueue(this, iconList); /*switches则存储了一些杂项:禁用功能列表,SystemUIVisiblity,是否在导航栏中显示虚拟的 菜单键,输入法窗口是否可见、输入法窗口是否消费BACK键、是否接入了实体键盘、实体键盘是否被启用。 在后文中将会介绍它们的具体影响 */ int[]switches = new int[7]; ArrayList<IBinder> binders = new ArrayList<IBinder>(); try { // **② 向IStatusBarServie进行注册,并获取所有保存在IStatusBarService中的信息副本** mBarService.registerStatusBar(mCommandQueue, iconList, notificationKeys,notifications, switches, binders); } catch(RemoteException ex) {......} // **③ 创建状态栏与导航栏的窗口。**由于创建状态栏与导航栏的窗口涉及到控件树的创建,因此它由子类 PhoneStatusBar或TabletStatusBar实现,以根据不同的布局方案选择创建不同的窗口与控件树 */ createAndAddWindows(); /*应用来自IStatusBarService中所获取的信息 mCommandQueue已经注册到IStatusBarService中,状态栏与导航栏的窗口与控件树也都创建完毕 因此接下来的任务就是应用从IStatusBarService中所获取的信息 */ disable(switches[0]); // 禁用某些功能 setSystemUiVisibility(switches[1], 0xffffffff); // 设置SystemUIVisibility topAppWindowChanged(switches[2]!= 0); // 设置菜单键的可见性 // 根据输入法窗口的可见性调整导航栏的样式 setImeWindowStatus(binders.get(0), switches[3], switches[4]); // 设置硬件键盘信息。 setHardKeyboardStatus(switches[5] != 0, switches[6] != 0); // 依次向系统状态区添加状态图标 int N = iconList.size(); ...... // 依次向通知栏添加通知 N = notificationKeys.size(); ...... /* 至此,与IStatusBarService的连接已建立,状态栏与导航栏的窗口也已完成创建与显示,并且 保存在IStatusBarService中的信息都已完成了显示或设置。状态栏与导航栏的启动正式完成 */ } ``` 可见,状态栏与导航栏的启动分为如下几个过程: - 获取IStatusBarService,IStatusBarService是运行于system\_server的一个系统服务,它接受操作状态栏/导航栏的请求并将其转发给BaseStatusBar。为了保证SystemUI意外退出后不会发生信息丢失,IStatusBarService保存了所有需要状态栏与导航栏进行显示或处理的信息副本。 - 将一个继承自IStatusBar.Stub的CommandQueue的实例注册到IStatusBarService以建立通信,并将信息副本取回。 - 通过调用子类的createAndAddWindows()方法完成状态栏与导航栏的控件树及窗口的创建与显示。 - 使用从IStatusBarService取回的信息副本。