SystemUI概览

SystemUI属于系统级的apk,位置在frameworks\base\packages\SystemUI,主要功能有:

  • 状态栏信息显示,比如电池,wifi信号,3G/4G等icon显示
  • 通知面板,比如系统消息,第三方应用消息
  • 近期任务栏显示面板,比如长按近期任务快捷键,显示近期使用的应用
  • 截图服务
  • 壁纸服务
  • ……

SystemUI的启动流程

SystemServer启动后,会在Main Thread启动ActivityManagerService,当ActivityManagerService systemReady后,会去启动SystemUIService。
SystemServer路径:frameworks/base/services/java/com/android/server/SystemServer.java

mActivityManagerService.systemReady(new Runnable() {@Overridepublic void run() {Slog.i(TAG, "Making services ready");......Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartSystemUI");try {startSystemUi(context);} catch (Throwable e) {reportWtf("starting System UI", e);}Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);......}            });

在这个方法里启动一个SystemUIService服务

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);
}

通过startServiceAsUser,SystemUIService就启动了,即SystemUI进程开机启动。

public class SystemUIService extends Service {@Overridepublic void onCreate() {super.onCreate();((SystemUIApplication) getApplication()).startServicesIfNeeded();}......

在SystemUIService的onCreate方法中会调用SystemUIApplication的startServicesIfNeeded方法,这个方法会调用 startServicesIfNeeded(SERVICES)方法启动一系列服务(并不是真正的service,都继承自SystemUI)

public class SystemUIApplication extends Application {....../*** The classes of the stuff to start.*/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,com.android.systemui.VendorServices.class};......public void startServicesIfNeeded() {startServicesIfNeeded(SERVICES);}
}

所有SERVICES统一继承了SystemUI类:

public abstract class SystemUI {......public abstract void start();protected void onConfigurationChanged(Configuration newConfig) {}public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {}protected void onBootCompleted() {}......
}

startServicesIfNeeded方法会遍历SERVICES 这个数组,依次调用service的start方法启动服务。

private void startServicesIfNeeded(Class<?>[] services) {......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].mContext = this;mServices[i].mComponents = mComponents;if (DEBUG) Log.d(TAG, "running: " + mServices[i]);mServices[i].start();if (mBootCompleted) {mServices[i].onBootCompleted();}}......
}

状态栏

状态栏(SystemBars)service是SystemUI中最重要的service,代码量最多,最复杂的,界面结构也复杂。根据前面的内容可知,启动SystemBars是通过调用start()方法,如下图:

    public void start() {if (DEBUG) Log.d(TAG, "start");mServiceMonitor = new ServiceMonitor(TAG, DEBUG,mContext, Settings.Secure.BAR_SERVICE_COMPONENT, this);mServiceMonitor.start();  // will call onNoService if no remote service is found}

这里实质是回调到到SystemBars的onNoService()方法,最后是调用SystemBars的createStatusBarFromConfig()方法:

    private void createStatusBarFromConfig() {......String clsName = mContext.getString(R.string.config_statusBarComponent);......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.start();......}

上图可以看到,从string资源文件里面读取class name,通过java的映射机制实例化对象,然后调用start()方法启动,class name的值如下图:

    <!-- Component to be used as the status bar service.  Must implement the IStatusBarinterface.  This name is in the ComponentName flattened format (package/class)  --><string name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.PhoneStatusBar</string>

该配置文件在SystemUI/res/values/config.xml中。所以实质是PhoneStatusBar调用了start()方法。
SystemBars模块的初始化过程主要涉及的类有:

SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
SystemUI/src/com/android/systemui/statusbar/CommandQueue.java

PhoneStatusBar的父类是BaseStatusBar继承于SystemUI,SystemBars调用PhoneStatusBar中的start()方法,类关系图如下:

PhoneStatusBar的start()

public void start() {......super.start(); // calls createAndAddWindows()......addNavigationBar();......
}

如上图,调用父类中的start()方法,即BaseStatsuBar中的start()方法。然后调用addNavigationBar()方法实例化导航条。状态栏的布局层次结构如下图:

继续看BaseStatsuBar中的方法。

public void start() {......mBarService = IStatusBarService.Stub.asInterface(ServiceManager.getService(Context.STATUS_BAR_SERVICE));......// Connect in to the status bar manager servicemCommandQueue = 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) {// If the system process isn't there we're doomed anyway.}createAndAddWindows();......// Set up the initial icon stateint N = iconSlots.size();int viewIndex = 0;for (int i=0; i < N; i++) {setIcon(iconSlots.get(i), icons.get(i));}// Set up the initial notification state.try {mNotificationListener.registerAsSystemService(mContext,new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),UserHandle.USER_ALL);} catch (RemoteException e) {Log.e(TAG, "Unable to register notification listener", e);}......
}

如上面BaseStatsuBar中的start()方法,实例化一些对象,此处的对象都是“空值”,然后通过IStatusBarService的实例mBarService对象注册到StatusBarManagerService。

mCommandQueue是CommandQueue的实例,在StatusBarManagerService的远程回调,实现StatusBarManagerService和SystemUI的通信。

然后调用createAndAddWindows()方法,该方法初始化status bar,notification,quick settings等的View控件。

在这里,还需要注意NotificationListenerService的实例mNotificationListener的registerAsSystemService()方法,该方法主要实现StatusBarManagerService和SystemUI的notification的控制通道,也就是说,StatusBarManagerService收到notification变化时,通过此通道通知SystemUI显示notification的变化。

通知显示过程

一个APP需要显示notification首先需要实例化一个NotificationManager的对象,然后调用NotificationManager的方法notify()方法把创建好的Notification对象作为参数传进去。

public void notify(int id, Notification notification){notify(null, id, notification);
}
public void notify(String tag, int id, Notification notification){notifyAsUser(tag, id, notification, new UserHandle(UserHandle.myUserId()));
}
public void notifyAsUser(String tag, int id, Notification notification, UserHandle user){......INotificationManager service = getService();......try {service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,copy, idOut, user.getIdentifier());......} catch (RemoteException e) {throw e.rethrowFromSystemServer();}
}

上图中可以看到一个service的对象调用了enqueueNotificationWithTag()方法,该方法实质是远程调用NotificationManagerService中的enqueueNotificationWithTag()方法,该方法又直接调用enqueueNotificationInternal(),该方法如下:

void enqueueNotificationInternal(final String pkg, final String opPkg, final int callingUid,final int callingPid, final String tag, final int id, final Notification notification,int[] idOut, int incomingUserId) {......final StatusBarNotification n = new StatusBarNotification(pkg, opPkg, id, tag, callingUid, callingPid, 0, notification,user);......final NotificationRecord r = new NotificationRecord(getContext(), n);mHandler.post(new EnqueueNotificationRunnable(userId, r));......
}

这里会把NotificationManager传递过来的Notification对象进行很多处理,比如变换成NotificationRecord,实质就是把Notification缓存下来。在上图的这个过程,还有一些其它的处理逻辑,在这里就不详细说明。最后把这个NotificationRecord传递给EnqueueNotificationRunnable线程来处理:

    private class EnqueueNotificationRunnable implements Runnable {private final NotificationRecord r;private final int userId;EnqueueNotificationRunnable(int userId, NotificationRecord r) {this.userId = userId;this.r = r;};@Overridepublic void run() {synchronized (mNotificationList) {final StatusBarNotification n = r.sbn;......if (notification.getSmallIcon() != null) {StatusBarNotification oldSbn = (old != null) ? old.sbn : null;mListeners.notifyPostedLocked(n, oldSbn);} else {......}buzzBeepBlinkLocked(r);}}}

代码的末尾调用了buzzBeepBlinkLocked()方法,该方法主要处理Notification的声音和震动的逻辑。mListeners调用了notifyPostedLocked()方法,此方法最终会执行到如下图的代码:

private void notifyPosted(final ManagedServiceInfo info,final StatusBarNotification sbn, NotificationRankingUpdate rankingUpdate) {final INotificationListener listener = (INotificationListener)info.service;StatusBarNotificationHolder sbnHolder = new StatusBarNotificationHolder(sbn);try {listener.onNotificationPosted(sbnHolder, rankingUpdate);} catch (RemoteException ex) {Log.e(TAG, "unable to notify listener (posted): " + listener, ex);}
}

info.service返回一个INotificationListener的实例对象,该对象在上文中的mNotificationListener.registerAsSystemService()方法进行设置,所以listener.onNotificationPosted()方法实质是远程回调SystemUI中的方法:

private final NotificationListenerService mNotificationListener =new NotificationListenerService() {......@Overridepublic void onNotificationPosted(final StatusBarNotification sbn,final RankingMap rankingMap) {......if (sbn != null) {mHandler.post(new Runnable() {@Overridepublic void run() {......if (isUpdate) {updateNotification(sbn, rankingMap);} else {addNotification(sbn, rankingMap, null /* oldEntry */);}}});}}
}

代码运行又回到了BaseStatusBar.java类中,从APP调用NotificationManager的notify()方法到BaseStatusBar的addNotification()或updateNotification()方法,经历了一个复杂的过程。就不再往下详情说明Notification到达SystemUI的处理过程了,之后有机会我们继续分析。

锁屏

锁屏(Keyguard)service在SystemUI是一个比较特殊的模块,特殊在于SystemUI启动的service只是一个信息传递者,也就是KeyguardViewMediator,并没有做锁屏或解屏的实质操作。在这里,涉及到三个比较关键的类是:

SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java

KeyguardViewMediator和KeyguardService在源码中位于SystemUI中,而KeyguardUpdateMonitor则位于KeyGuard中。在KeyguardViewMediator的初始化中主要做了三件事,如图:

public void start() {synchronized (this) {setupLocked();}putComponent(KeyguardViewMediator.class, this);
}
private void setupLocked() {......mUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);......mStatusBarKeyguardViewManager =SystemUIFactory.getInstance().createStatusBarKeyguardViewManager(mContext,mViewMediatorCallback, mLockPatternUtils);final ContentResolver cr = mContext.getContentResolver();mDeviceInteractive = mPM.isInteractive();......if (soundPath != null) {mLockSoundId = mLockSounds.load(soundPath, 1);}if (soundPath == null || mLockSoundId == 0) {Log.w(TAG, "failed to load lock sound from " + soundPath);}soundPath = Settings.Global.getString(cr, Settings.Global.UNLOCK_SOUND);if (soundPath != null) {mUnlockSoundId = mLockSounds.load(soundPath, 1);}if (soundPath == null || mUnlockSoundId == 0) {Log.w(TAG, "failed to load unlock sound from " + soundPath);}soundPath = Settings.Global.getString(cr, Settings.Global.TRUSTED_SOUND);if (soundPath != null) {mTrustedSoundId = mLockSounds.load(soundPath, 1);}if (soundPath == null || mTrustedSoundId == 0) {Log.w(TAG, "failed to load trusted sound from " + soundPath);}......
}

实例化KeyguardUpdateMonitor的实例mUpdateMonitor,KeyguardUpdateMonitor负责更新已经锁屏界面上的内容(如时间)。当然,KeyguardUpdateMonitor只是一个信息传递者,实际去刷新界面的是StatusBar模块。Keyguard模块通知StatusBar刷新解密是通过KeyguardUpdateMonitorCallback这个类进行远程回调,该类的实例在StatusBar模块启动时通过KeyguardService获取到IKeyguardService的远端实例,通过IKeyguardService远程调用IKeyguardService的addStateMonitorCallback()方法实例化KeyguardUpdateMonitorCallback对象,SystemUI/src/com/android/systemui/keyguard/KeyguardService.java

private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {@Override // Binder interfacepublic void addStateMonitorCallback(IKeyguardStateCallback callback) {checkPermission();mKeyguardViewMediator.addStateMonitorCallback(callback);}......@Override // Binder interfacepublic void onScreenTurnedOn() {Trace.beginSection("KeyguardService.mBinder#onScreenTurningOn");checkPermission();mKeyguardViewMediator.onScreenTurnedOn();Trace.endSection();}......
};

SystemUI启动的Keyguard模块并没有真正的去操作锁屏界面,而是作为一个信息传递者把信息传递给StatusBar模块。
KeyguardService在KeyguardServiceDelegate中绑定调用。
frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java:

public void bindService(Context context) {Intent intent = new Intent();final Resources resources = context.getApplicationContext().getResources();final ComponentName keyguardComponent = ComponentName.unflattenFromString(resources.getString(com.android.internal.R.string.config_keyguardComponent));intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);intent.setComponent(keyguardComponent);boolean isBox = isBox();if (isBox || !context.bindServiceAsUser(intent, mKeyguardConnection,Context.BIND_AUTO_CREATE, mScrimHandler, UserHandle.SYSTEM)) {Log.v(TAG, "*** Keyguard: can't bind to " + keyguardComponent);mKeyguardState.showing = false;mKeyguardState.showingAndNotOccluded = false;mKeyguardState.secure = false;synchronized (mKeyguardState) {// TODO: Fix synchronisation model in this class. The other state in this class// is at least self-healing but a race condition here can lead to the scrim being// stuck on keyguard-less devices.mKeyguardState.deviceHasKeyguard = false;hideScrim();}} else {if (DEBUG) Log.v(TAG, "*** Keyguard started");}
}private final ServiceConnection mKeyguardConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {if (DEBUG) Log.v(TAG, "*** Keyguard connected (yay!)");mKeyguardService = new KeyguardServiceWrapper(mContext,IKeyguardService.Stub.asInterface(service), mShowingStateChangedCallback);if (mKeyguardState.systemIsReady) {// If the system is ready, it means keyguard crashed and restarted.mKeyguardService.onSystemReady();if (mKeyguardState.currentUser != UserHandle.USER_NULL) {// There has been a user switch earliermKeyguardService.setCurrentUser(mKeyguardState.currentUser);}// This is used to hide the scrim once keyguard displays.if (mKeyguardState.interactiveState == INTERACTIVE_STATE_AWAKE) {mKeyguardService.onStartedWakingUp();}if (mKeyguardState.screenState == SCREEN_STATE_ON|| mKeyguardState.screenState == SCREEN_STATE_TURNING_ON) {mKeyguardService.onScreenTurningOn(new KeyguardShowDelegate(mDrawnListenerWhenConnect));}if (mKeyguardState.screenState == SCREEN_STATE_ON) {mKeyguardService.onScreenTurnedOn();}mDrawnListenerWhenConnect = null;}if (mKeyguardState.bootCompleted) {mKeyguardService.onBootCompleted();}if (mKeyguardState.occluded) {mKeyguardService.setOccluded(mKeyguardState.occluded, false /* animate */);}}@Overridepublic void onServiceDisconnected(ComponentName name) {if (DEBUG) Log.v(TAG, "*** Keyguard disconnected (boo!)");mKeyguardService = null;}};

com.android.internal.R.string.config_keyguardComponent的默认配置值:

<!-- Keyguard component -->
<string name="config_keyguardComponent" translatable="false">com.android.systemui/com.android.systemui.keyguard.KeyguardService</string>

KeyguardViewMediator启动的流程图如下:

总结

先大概看一遍流程,这个apk比较复杂,之后还会分析。

什么是SystemUI?简单分析相关推荐

  1. SystemUI架构分析

    SystemUI架构分析 SystemUI架构分析 前言 1SystemUI介绍 1SystemUI摘要 2什么是SystemUI 2SystemUI的启动过程 3SystemUI的SERVICES ...

  2. android SystemUI 流程分析

    android4 SystemUI 流程分析 什么是SystemUI? 对于Phone来说SystemUI指的是:StatusBar(状态栏).NavigationBar(导航栏).而对于Tablet ...

  3. android开发中Settings结构简单分析

    Settings界面结构简单分析 Setting是android系统很重要的模块,这个模块并不是很复杂,这部分也一直在看,很多时候都是在看某个具体的选项,比如WLAN,蓝牙这样具体的源码,但是对于主界 ...

  4. R语言splines包构建基于logistic回归的自然样条分析:南非心脏病数据集、非线性:基函数展开和样条分析、你简单分析的不重要特征,可能只是线性不显著、而非线性是显著的

    R语言splines包构建基于logistic回归的自然样条分析:南非心脏病数据集.非线性:基函数展开和样条分析.你简单分析的不重要特征,可能只是线性不显著.而非线性是显著的 目录

  5. [EntLib]微软企业库5.0 学习之路——第七步、Cryptographer加密模块简单分析、自定义加密接口及使用—上篇...

    在完成了后,今天开始介绍企业库中的新模块:Cryptographer(加密模块),这个模块在日常的大多数项目的作用非常重要,例如:网站会员密码.身份证号.网站配置等,通过对信息进行加密可以保证项目数据 ...

  6. FFmpeg资料来源简单分析:libswscale的sws_getContext()

    ===================================================== FFmpeg库函数的源代码的分析文章: [骨架] FFmpeg源码结构图 - 解码 FFmp ...

  7. howdoi 简单分析

    对howdoi的一个简单分析. 曾经看到过下面的这样一段js代码: try{doSth(); } catch (e){ask_url = "https://stackoverflow.com ...

  8. Mac与Phy组成原理的简单分析

    Mac与Phy组成原理的简单分析 2011-12-28 15:30:43 //http://blog.chinaunix.net/uid-20528014-id-3050217.html 本文乃fir ...

  9. python做数据可视化的代码_Python数据可视化正态分布简单分析及实现代码

    Python说来简单也简单,但是也不简单,尤其是再跟高数结合起来的时候... 正态分布(Normaldistribution),也称"常态分布",又名高斯分布(Gaussiandi ...

  10. ASIHTTPRequest源码简单分析

    2019独角兽企业重金招聘Python工程师标准>>> 1.前言 ASIHttprequest 是基于CFNetwork的,由于CFNetwork是比较底层的http库,功能比较少, ...

最新文章

  1. DBUtils 笔记
  2. java ajax datatype_理解jquery ajax中的datatype属性选项值
  3. POSTGRESQL小玩
  4. 机器学习-分类算法-模型选择与调优09
  5. SuperSocket源码解析之开篇
  6. 【渝粤教育】广东开放大学 机械制造基础 形成性考核 (51)
  7. Visual Studio 2017版本15.9现在可用
  8. WebApp 开发中常用的代码片段
  9. SQLite 3.31.0 发布,世界上使用量最大的数据库引擎
  10. 移动硬盘无法休眠了?
  11. PHP新的连接MySQL方法mysqli
  12. java executor 例子_Java中Executor框架的实例
  13. resize2fs调整ext2\ext3\ext4文件系统的大小
  14. 北京今日限行 API数据接口
  15. 2019年小米软件开发《异数》笔试题
  16. c 语言 todo 用法,Tip:iOS开发中关于TODO的用法
  17. stm32串口通信实验
  18. 使用html框架制作一个网站布局
  19. INDEX REBUILD和INDEX REORGANIZE和UPDATE STATISTICS是否涉及Sch-M的案例分析
  20. 找工作经验分享贴——硬件岗求职历程

热门文章

  1. mysql切片库_Database数据库切片模式
  2. 详解 python 的 切片
  3. 移动硬盘如何合并磁盘
  4. 二维曲线 去噪点 c++_二维介孔聚吡咯-氧化石墨烯异质结用于制备无枝晶的锂金属负极...
  5. 至简设计系列_7段数码管显示
  6. Emacs指北(做一个搬运工好累)
  7. 趋势科技笔试记录和补充
  8. HDU 2246 神题?一千多行
  9. 如何手动控制Mac的风扇
  10. 在vue中使用unity3D实现webGL将要呈现的效果