Android 5.1 SystemUI 家的事系列片
第二集SystemUI的启动
什么是SystemUI
SystemUI是什么?我们从2个层面来看;
用户层面,即显示效果
- status bar 和 navigation bar
图一
图中红色框框标示出的即为SystemUI显示的范围,上方的我们称之为status bar即状态栏,其height一般为25dpi,width为屏幕的宽度;下方的为navigation bar即导航栏,其height为48dpi,width为屏幕的宽度;
- expand panel
图二
expand panel即我们常说的下拉面板;如图中红色框框所示;其UI 显示以及下拉,缩回的动画都在SystemUI的实现;
当我们收到notification时,例如插拔sdcard,在expand panel中会有通知显示,其显示的UI也由SystemUI 控制;
开发层面,即源码
SystemUI的源码位于android源码/frameworks/base/package/SystemUI
分析frameworks/base/package/SystemUI/Android.mk文件
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
src/com/android/systemui/EventLogTags.logtags
LOCAL_STATIC_JAVA_LIBRARIES := Keyguard
LOCAL_JAVA_LIBRARIES := telephony-common
LOCAL_PACKAGE_NAME := SystemUI
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
LOCAL_RESOURCE_DIR := \
frameworks/base/packages/Keyguard/res \
$(LOCAL_PATH)/res
LOCAL_AAPT_FLAGS := --auto-add-overlay --extra-packages com.android.keyguard
include $(BUILD_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
其实SystemUI就是一个APP,没有Activity的。用platform key签名,拥有system权限的系统app;
编译后,其apk文件在out/target/product/productname/system/priv-app/SystemUI/SystemUI.apk
- SystemUI的管辖地带说明
1.status bar中显示的例如时钟,SIM卡信号,3G信号,wifi信号,蓝牙,等等,都归SystemUI控制;
2.navigation bar中涉及到3个重要的按钮,back button,home button以及recent app button;这些控制都在SystemUI中;
3.wallpaper,即在图一看到的绿色背景,称之为静态壁纸,归SystemUI管控;但是出去红色框框之外的,壁纸之上的,都是home的管辖地带了;
插播一段小花絮
这里为什么一定要提到SystemUI的管辖地带,因为很多人在解决Android bug时,分不清楚,哪个是SystemUI的,哪个是home的;很容易弄混,导致了分析问题时,连方向都找错了;
例如,点击图一中的Gallery button,无法进入Gallery,有人会认为SystemUI有问题引起的。其实问题有可能在home,但一定不在SystemUI,,这个锅SystemUI不背;
例如,wallpaper不显示了,背景为黑色的,问题应该在SystemUI, 但一定不在home;
SystemUI的架构
分析的起点
分析或者学习一个功能,总的来说先从架构图入手,纵观全局;然后再从流程图推进,逐步了解细节;
那么针对Android app,或者framework层的功能;它的入手也是从架构图开始,但有2个重要的文件,一定是要先看的,一个是Android.mk,一个是AndroidManifest.xml;
Android.mk决定了当前参与此功能的编译code涉及了哪些,编译出来的是个什么鬼;是个jar包,apk还是so库,还是可执行文件;所以会读mk文件,是行走android 源码的基本技能之一;
AndroidManifest.xml在apk中特有,从它可以知道,该app运行的起始activity会是谁,app中涉及到了哪些activity,service,broadcast;APP具备哪些权限,等等;
可以说读懂以上2个文件,是分析Apk的源头;
SystemUI类图
SystemUI 功能类图分析
图三
这个图标识除了SystemUI中管控的七大功能:
- SystemBars
- KerguardViewMediator
- Recents
- VolumeUI
- StorageNotification
- PowerUI
- RingtonePlayer
这些功能具体做了哪些事情,后续分集讲解;
SystemUIService是SystemUI得以运行的总起点,它去找SystemUIApplication将七大功能都运行起来;所以说它们二者开始了SystemUI的启动;具体流程看第三节—SystemUI的启动
SystemUI的启动
startSystemUi
SystemUI是随着Android系统的启动而启动的;正如我们所熟知的,Android系统运行的第一个进程为zygote,然后启动了init进程,接着就是SystemServer了;SystemServer负责将Android系统的各大Service运行起来,例如负责App运行的ActivityManagerService,负责APP安装,解析,卸载的PackageManagerService,负责Window窗口绘画管理的WindowManagerService,等等;SystemUI也是在SystemServer中启动的;
具体code如下:
static final void startSystemUi(Context context) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.systemui", "com.android.systemui.SystemUIService"));
//Slog.d(TAG, "Starting service: " + intent);
context.startServiceAsUser(intent, UserHandle.OWNER);
}
我们看到SystemUI的启动其实就是启动了一个常规的Android四大组件之一Service—- SystemUIService;
startSystemUi又是由谁调用的呢?
mActivityManagerService.systemReady(new Runnable() {
@Override
public void run() {
Slog.i(TAG, "Making services ready");
...
try {
startSystemUi(context);
} catch (Throwable e) {
reportWtf("starting System UI", e);
}
我们看到在AcitivityManagerService运行成功之后,调用了startSystemUi函数;
SystemUI 各大功能的创建
在SystemServer中执行了startSystemUi之后,就会运行到SystemUI中的SystemUIService onCreate函数,那么接下来做了哪些事情呢?
step 1 onCreate
属于系统调用,先调用Application的onCreate
public void onCreate() {
super.onCreate();
// Set the application theme that is inherited by all services. Note that setting the
// application theme in the manifest does only work for activities. Keep this in sync with
// the theme set there.
setTheme(R.style.systemui_theme);
IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (mBootCompleted) return;
if (DEBUG) Log.v(TAG, "BOOT_COMPLETED received");
unregisterReceiver(this);
mBootCompleted = true;
if (mServicesStarted) {
final int N = mServices.length;
for (int i = 0; i < N; i++) {
mServices[i].onBootCompleted();
}
}
}
}, filter);
}
step 1的关键是注册了一个静态广播,防止各大功能的onBootCompleted没有被调用;这样说可能大家有些迷糊,先放着,继续往下看;
step 2 onCreate
也是系统调用,注意step 1 和step 2 都是来自Android 对Service启动的生命周期控制;
public void onCreate() {
super.onCreate();
((SystemUIApplication) getApplication()).startServicesIfNeeded();
}
step 2 很简单,就是回调到SystemUIApplication中的startServicesIfNeeded()函数中
step3 startServicesIfNeeded
public void startServicesIfNeeded() {
if (mServicesStarted) {
return;
}
if (!mBootCompleted) {
// check to see if maybe it was already completed long before we began
// see ActivityManagerService.finishBooting()
if ("1".equals(SystemProperties.get("sys.boot_completed"))) {
mBootCompleted = true;
if (DEBUG) Log.v(TAG, "BOOT_COMPLETED was already sent");
}
}
Log.v(TAG, "Starting SystemUI 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 {
mServices[i] = (SystemUI)cl.newInstance();
} 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();
}
}
mServicesStarted = true;
}
SystemUIApplication当中有2个比较重要的控制变量:
private boolean mServicesStarted;
private boolean mBootCompleted;
初始化的值均为false;
mServicesStarted为true表示SystemUIService运行起来了,该变量的目的是方式new 出多个SystemUI对象;
mBootCompleted为true表示Android系统已经运行完全,具体的指标有系统发出boot completed广播或者sys.boot_completed属性值为1;因为SystemUI中有些功能必须在系统运行完全之后才能执行;
这里相当于将SystemUI旗下的功能,分成了2个阶段初始化,start函数执行在boot completed之前,而onBootCompelted执行在boot completed之后;为什么要分2个阶段初始化?是因为功能需求,在早起的Android 3.X的系统中,SystemUI并没有这样的初始化区分;
功能总结变量:
private final SystemUI[] mServices = new SystemUI[SERVICES.length];
在step 3中,各个功能new出的实例都存放在mServices中;
Class<?> cl = SERVICES[i];
mServices[i] = (SystemUI)cl.newInstance();
SERVICE 列出了,当前有哪些功能:
private final Class<?>[] SERVICES = new Class[] {
com.android.systemui.keyguard.KeyguardViewMediator.class,
com.android.systemui.recent.Recents.class,
com.android.systemui.volume.VolumeUI.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
};
这就是我们在图三种看到7个功能;
回过头来看step 1中注册的静态广播
在step 3 中的最后几步,如果此时mBootCompleted的值为false,也就是说,此时系统还没有运行完全,那么各大功能的onBootcompleted就不会被调用;此时广播就发挥效用了;当系统发出boot commpelted的广播时,在onReceive中就会执行各个功能的onBootCompleted函数了;
严谨的编程思维就是这样的,而且我们可以看到onReceive中,最先执行的是unregisterReceiver,将广播注销;这也是我们经常忽略的部分,值得学习
总结
SystemUIService的开始运行,启动了SystemUI的运作;后续会绘画status bar,navigation bar,expand panel,接收notification等等;后续会陆续分析;
从SystemUI的架构图中,我们看出SystemUI的架构设计得很好;所有的功能都继承自abstract class SystemUI;启动也列举到 SERVICES中,如果我们想要在SystemUI中增加新的功能,很简单,自己写一个继承自SystemUI 的类即可;如果想要删除某个功能,也很简单,在SERVICES中将其移除即可;体现了架构设计的灵活性;