博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android 7.0 SystemUI 之启动和状态栏和导航栏简介
阅读量:6982 次
发布时间:2019-06-27

本文共 14691 字,大约阅读时间需要 48 分钟。

PS:已同步至我的博客

一、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);        //Slog.d(TAG, "Starting service: " + intent);        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); } ......复制代码

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
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; }复制代码

这里以com.android.systemui.statusbar.SystemBars.class为例,讲解一下


三、状态栏和导航栏 的启动

SystemBars的start方法会创建一个ServiceMonitor(服务监听者),会进入到ServiceMonitor的start方法

public class SystemBars extends SystemUI implements ServiceMonitor.Callbacks { ...... public void start() {        //    ServiceMonitor是服务监听者        mServiceMonitor = new ServiceMonitor(TAG, DEBUG, mContext, Settings.Secure.BAR_SERVICE_COMPONENT, this);        mServiceMonitor.start();  // will call onNoService if no remote service is found    } ......    }复制代码

在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;            }        }    };    ......    }复制代码

startService方法如下


public class ServiceMonitor {  ......     private void startService() {    //获取服务组件名称    mServiceName = getComponentNameFromSetting();    //如果为空,回调服务的onNoService方法    if (mServiceName == null) {            mBound = false;            mCallbacks.onNoService();        } else {        //不为空,回调服务的的onServiceStartAttempt方法            long delay = mCallbacks.onServiceStartAttempt();            mHandler.sendEmptyMessageDelayed(MSG_CONTINUE_START_SERVICE, delay);        }    }    ......    }复制代码

这里对mServiceName是否为空进行判断,总之无论如何它最终都会启动这个服务。

回调SystemBars的onNoService里创建StatusBar

public class SystemBars extends SystemUI implements ServiceMonitor.Callbacks {   ...... @Override    public void onNoService() {    if (DEBUG) Log.d(TAG, "onNoService");     //创建StatusBar    createStatusBarFromConfig();  // fallback to using an in-process implementation    }   ......复制代码
private void createStatusBarFromConfig() {        //config_statusBarComponent就是PhoneStatusBar        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 { //创建BaseStatusBar实例 mStatusBar = (BaseStatusBar) cls.newInstance(); } catch (Throwable t) { throw andLog("Error creating status bar component: " + clsName, t); } mStatusBar.mContext = mContext; mStatusBar.mComponents = mComponents; //启动 mStatusBar.start(); }复制代码

在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() {         ......        //调用父类的start方法,在父类BaseStatusBar里调用createAndAddWindows方法        // 3.1        super.start(); // calls createAndAddWindows()         ......        //添加导航栏        // 3.2        addNavigationBar();        ......    }复制代码

它会回调父类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() {    ......    /*实例化IStatusBarService,    随后BaseStatusBar将自己注册到IStatusBarService之中。以此声明本实例才是状态栏的真正        实现者,IStatusBarService会将其所接受到的请求转发给本实例。    IStatusBarService会保存SystemUi的状态信息,避免SystemUi崩溃而造成信息的丢失    */        mBarService = IStatusBarService.Stub.asInterface(                ServiceManager.getService(Context.STATUS_BAR_SERVICE));        ......        //IStatusBarService与BaseStatusBar进行通信的桥梁。        mCommandQueue = new CommandQueue(this);     /*switches则存储了一些杂项:禁用功能列表,SystemUIVisiblity,是否在导航栏中显示虚拟的           菜单键,输入法窗口是否可见、输入法窗口是否消费BACK键、是否接入了实体键盘、实体键盘是否被启用。          */        int[] switches = new int[9];        ArrayList
binders = new ArrayList
(); /*它保存了用于显示在状态栏的系统状态 区中的状态图标列表。在完成注册之后,IStatusBarService将会在其中填充两个数组,一个字符串 数组用于表示状态的名称,一个StatusBarIcon类型的数组用于存储需要显示的图标资源。 */ ArrayList
iconSlots = new ArrayList<>(); ArrayList
icons = new ArrayList<>(); Rect fullscreenStackBounds = new Rect(); Rect dockedStackBounds = new Rect(); //IStatusBarService注册一些信息 try { mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders, fullscreenStackBounds, dockedStackBounds); } catch (RemoteException ex) { } //创建状态栏窗口 createAndAddWindows(); ...... } ...... }复制代码

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
iconSlots, List
iconList, int switches[], List
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); } } ...... }复制代码

这几者的关系如下

回到PhoneStatusBar中, 父类BaseStatusBar中的createAndAddWindows为抽象方法,由子类实现,看下PhoneStatusBar的
createAndAddWindows
@Override    public void createAndAddWindows() {        //添加状态栏窗口        addStatusBarWindow();    }复制代码

方法实现如下

private void addStatusBarWindow() {    //创建控件            makeStatusBarView();    //创建StatusBarWindowManager实例            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这个自定义布局.

......
复制代码

我这里以主要的布局层次做介绍,结合图片分析会更加清楚

StatusBarWindowView里有几个主要的布局

  • layout/status_bar
  • layout/brightness_mirror
  • layout/status_bar_expanded

如下图


1.layout/status_bar

这个是正常状态下(未下拉的状态栏图标区域)

这个布局对应的是PhoneStatusBarView

......
复制代码

以下是细节图,连线表示层次结构


其中,状态栏的区域分为以下几种

  • 通知栏图标,在状态栏的最左侧显示通知信息,比如来了一个短信,那么就会弹出一个短信图标
  • 时间信息,显示一个时间,比如上午9:58
  • 信号图标,显示手机信号,wifi信号等
  • 电量图标,显示当前电量状态
  • 状态图标,wifi,蓝牙等开关状态

2.@layout/brightness_mirror

这个布局就是中间那个调整亮度的seekBar.没啥好介绍的.


3.@layout/status_bar_expanded

这个布局是下拉时的状态栏的布局

复制代码

细节图



创建完布局后,就会添加窗口到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();        //添加到WindowManager中        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);        //home键        ButtonDispatcher homeButton = mNavigationBarView.getHomeButton();        homeButton.setOnTouchListener(mHomeActionListener);        homeButton.setOnLongClickListener(mLongPressHomeListener);        //监听配置改变        mAssistManager.onConfigurationChanged();        }        ......    }复制代码

四、结束

关于SystemUI的状态栏和导航栏就介绍完了,讲的很浅显,只是从整体上梳理了下流程.

转载地址:http://pxtpl.baihongyu.com/

你可能感兴趣的文章
Validform使用入门
查看>>
Jfinal碰到的问题记录
查看>>
Kettle7 java 远程执行Trans/Job
查看>>
Python Socket 编程
查看>>
jenkins ssh发布配置
查看>>
Guava新集合-Multiset
查看>>
Mahout各种推荐器的主要特点(转)
查看>>
精通Spring Boot—— 第二十一篇:Spring Social OAuth 登录简介
查看>>
IIS_FastCGI+php5.3+wincache+memcached+ZendLoader
查看>>
windows mongdb 安装
查看>>
HBase shell 中的十六进制数值表示
查看>>
Redis数据库如何实现读写分离
查看>>
maven 工程依赖war包
查看>>
C# 常用文件操作
查看>>
MySQL绿色版5.7以上安装教程
查看>>
PIC中档单片机汇编指令详解(6)
查看>>
JVM是怎么判断不可用对象的
查看>>
Tornado使用mako 模板总结
查看>>
用python 登录 ssh 与 sftp 通过证书登录系统
查看>>
tpcc的测试
查看>>