做了SystemUI模块将近2年了,期间自己也通过努力将Google原生的界面改为市场上流行的UI效果,今天就系统总结一下
这个模块。
一:SystemUI主体框架启动流程
android设备上电,引导程序引导进入boot(通常是uboot),加载initramfs、kernel镜像,启动kernel后,进入用户态程序。第一个用户空间程序是init, PID固定是1.
init的基本功能有:
而在Zygote中将启动SystemServer组件。
本文将从SystemServer开始分析。
SystemServer 名为系统服务进程,负责启动 Android 系统的关键服务。
其入口是SystemServer.main():
/**
* The main entry point from zygote.
*/
public static void main(String[] args) {
new SystemServer().run();
}
可以看到main()中生成了SystemServer对象并执行了run方法。
SystemServer.run():
private void run() {
......
// Start services.
try {
startBootstrapServices();
startCoreServices();
startOtherServices();
......
} catch (Throwable ex) {
...
throw ex;
}
...
// Loop forever.
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
先看一眼startBootstrapServices();
private void startBootstrapServices() {
......
Installer installer = mSystemServiceManager.startService(Installer.class);
// Activity manager runs the show.
mActivityManagerService = mSystemServiceManager.startService(
ActivityManagerService.Lifecycle.class).getService();
mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
......
}
在startBootstrapServices()中启动了mActivityManagerService。
随后,我们再回头去看startOtherServices():
private void startOtherServices() {
final Context context = mSystemContext;
AccountManagerService accountManager = null;
ContentService contentService = null;
.......
mActivityManagerService.systemReady(new Runnable() {
@Override
public void run() {
......
try {
startSystemUi(context);
} catch (Throwable e) {
reportWtf("starting System UI", e);
}
.......
mActivityManagerService.systemReady创建线程去执行startSystemUi(context),从方法名称可以看出,这里将启动systemUI。
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);
}
通过intent.setComponent(new ComponentName("com.android.systemui",
"com.android.systemui.SystemUIService"));
设置启动systemui程序的SystemUIService
进入SystemUIService:
public class SystemUIService extends Service {
@Override
public void onCreate() {
super.onCreate();
((SystemUIApplication) getApplication()).startServicesIfNeeded();
}
......
onCreate方法中获得SystemUIApplication对象并调用其startServicesIfNeeded方法:
public void startServicesIfNeeded() {
final int N = SERVICES.length;
for (int i=0; i cl = SERVICES[i];
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;
mServices[i].start();//start服务
if (mBootCompleted) {
mServices[i].onBootCompleted();
}
}
mServicesStarted = true;
}
可以看到startServicesIfNeeded()循环start了很多Services。
数组SERVICES的定义如下:
/** * The classes of the stuff to start. */ private final Class>[] SERVICES = new Class[] { com.android.systemui.tuner.TunerService.class,//定制service暂时不清楚干什么的,没用过 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//这个也没用过 };
可以看到子服务包括:TunerService,KeyguardViewMediator,Recents,VolumeUI,SystemBars,StorageNotification,PowerUI
RingtonePlayer,KeyboardUI。
不过这里的service与我们平时所讲的四大组件的service并不一样。这里的service只是继承了SystemUI类的普通对象而已。
例如SystemBars:
public class SystemBars extends SystemUI implements ServiceMonitor.Callbacks {
......
}
查看到这里,可以总结一下systemUI主体框架的启动流程:
SystemServer启动Android核心服务包括了ActivityManagerService
--->ActivityManagerService一旦启动完成就会在systemReady的回调里启动SystemUIService
--->SystemUIService.onCreate—--->SystemUIApplication.startServicesIfNeeded
--->循环中调用mServices[i].start()启动SystemUI的各种核心service。
二:各个service的基本流程介绍
①首先讲打交道最多的也是最复杂的一块:SystemBar
上述各服务启动之后其中的一项就包括SystemBar的启动
调用start
@Override 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 }
然后通过回调调用
@Override public void onNoService() { if (DEBUG) Log.d(TAG, "onNoService"); createStatusBarFromConfig(); // fallback to using an in-process implementation }
private void createStatusBarFromConfig() { if (DEBUG) Log.d(TAG, "createStatusBarFromConfig"); final String clsName = mContext.getString(R.string.config_statusBarComponent); if (clsName == null || clsName.length() == 0) { throw andLog("No status bar component configured", null); } 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(); if (DEBUG) Log.d(TAG, "started " + mStatusBar.getClass().getSimpleName()); }
name="config_statusBarComponent" translatable="false">com.android.systemui.statusbar.phone.PhoneStatusBar
PhoneStatusBar的start方法
@Override public void start() {
super.start(); // calls createAndAddWindows()
}
@Override public void createAndAddWindows() { addStatusBarWindow(); } private void addStatusBarWindow() { makeStatusBarView(); mStatusBarWindowManager = new StatusBarWindowManager(mContext); mRemoteInputController = new RemoteInputController(mStatusBarWindowManager, mHeadsUpManager); mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight()); }
protected PhoneStatusBarView makeStatusBarView() { final Context context = mContext; updateDisplaySize(); // populates mDisplayMetrics updateResources(); inflateStatusBarWindow(context);
}
protected void inflateStatusBarWindow(Context context) { mStatusBarWindow = (StatusBarWindowView) View.inflate(context, R.layout.super_status_bar, null); }
至此我我找到了PhoneStatusBar的关键布局,现在结束,下面的布局细分我后续细讲。
②接下来我们讲解一下KeyguardViewMediator(也就是锁屏界面启动流程)
Zygote进程启动后会首先创建一个SystemServer进程,SystemServer进程在调用startOtherServices同时也会调用WindowManagerService的systemReady()方法
//frameworks/base/services/java/com/android/server/SystemServer.java
private
void
startOtherServices() {
...
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore);
...
try
{
wm.systemReady();
Slog.i(
"jason11"
,
"SystemServer wm.systemReady"
);
}
catch
(Throwable e) {
reportWtf(
"making Window Manager Service ready"
, e);
}
...
}
//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
//final WindowManagerPolicy mPolicy = new PhoneWindowManager();
public
void
systemReady() {
mPolicy.systemReady();
}
//frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
/** {@inheritDoc} */
@Override
public
void
systemReady() {
mKeyguardDelegate =
new
KeyguardServiceDelegate(mContext);
mKeyguardDelegate.onSystemReady();
readCameraLensCoverState();
updateUiMode();
boolean
bindKeyguardNow;
synchronized
(mLock) {
updateOrientationListenerLp();
mSystemReady =
true
;
mHandler.post(
new
Runnable() {
@Override
public
void
run() {
updateSettings();
}
});
bindKeyguardNow = mDeferBindKeyguard;
if
(bindKeyguardNow) {
// systemBooted ran but wasn't able to bind to the Keyguard, we'll do it now.
mDeferBindKeyguard =
false
;
}
}
if
(bindKeyguardNow) {
mKeyguardDelegate.bindService(mContext);
mKeyguardDelegate.onBootCompleted();
}
mSystemGestures.systemReady();
}
//frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
/** {@inheritDoc} */
@Override
public
void
systemBooted() {
boolean
bindKeyguardNow =
false
;
synchronized
(mLock) {
// Time to bind Keyguard; take care to only bind it once, either here if ready or
// in systemReady if not.
if
(mKeyguardDelegate !=
null
) {
bindKeyguardNow =
true
;
}
else
{
// Because mKeyguardDelegate is null, we know that the synchronized block in
// systemReady didn't run yet and setting this will actually have an effect.
mDeferBindKeyguard =
true
;
}
}
if
(bindKeyguardNow) {
mKeyguardDelegate.bindService(mContext);
mKeyguardDelegate.onBootCompleted();
}
synchronized
(mLock) {
mSystemBooted =
true
;
}
startedWakingUp();
screenTurningOn(
null
);
screenTurnedOn();
}
下面就通过如下的时序图看看是如何调用到systemBooted的,就不在一步步跟了
通过上面的分析知道,无论是在systemReady或systemBooted,都调用了KeyguardServiceDelegate对象的bindService方法,下面就以这个方法开始,看看锁屏界面是怎么显示出来的,先看看下面的时序图,再来分步讲解
1、先来看看在KeyguardServiceDelegate如何绑定KeyguardService的
//frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
public
class
KeyguardServiceDelegate {
...
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.setComponent(keyguardComponent);
if
(!context.bindServiceAsUser(intent, mKeyguardConnection,
Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
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"
);
}
}
...
}
在bindService中调用了bindServiceAsUser绑定指定intent的service,config_keyguardComponent的定义如下
//frameworks/base/core/res/res/values/config.xml
"config_keyguardComponent"
translatable=
"false"
>com.android.systemui/com.android.systemui.keyguard.KeyguardService
当绑定成功后会调用mKeyguardConnection里的onServiceConnected方法
//frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
public
class
KeyguardServiceDelegate {
...
private
final
ServiceConnection mKeyguardConnection =
new
ServiceConnection() {
@Override
public
void
onServiceConnected(ComponentName name, IBinder service) {
if
(DEBUG) Log.v(TAG,
"*** Keyguard connected (yay!)"
);
mKeyguardService =
new
KeyguardServiceWrapper(mContext,
IKeyguardService.Stub.asInterface(service));
if
(mKeyguardState.systemIsReady) {
// If the system is ready, it means keyguard crashed and restarted.
mKeyguardService.onSystemReady();
// 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);
}
}
@Override
public
void
onServiceDisconnected(ComponentName name) {
if
(DEBUG) Log.v(TAG,
"*** Keyguard disconnected (boo!)"
);
mKeyguardService =
null
;
}
};
...
}
//frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
public
class
KeyguardServiceWrapper
implements
IKeyguardService {
...
@Override
// Binder interface
public
void
onSystemReady() {
try
{
mService.onSystemReady();
}
catch
(RemoteException e) {
Slog.w(TAG ,
"Remote Exception"
, e);
}
}
...
}
在KeyguardService的onSystemReady里调用了KeyguardViewMediator里的onSystemReady,在这里就不贴这个代码了,直接看看KeyguardViewMediator.onSystemReady这个里面干啥了
2、KeyguardViewMediator.onSystemReady
//frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
public
class
KeyguardViewMediator
extends
SystemUI {
...
public
void
onSystemReady() {
mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
synchronized
(
this
) {
if
(DEBUG) Log.d(TAG,
"onSystemReady"
);
mSystemReady =
true
;
doKeyguardLocked(
null
);
mUpdateMonitor.registerCallback(mUpdateCallback);
}
// Most services aren't available until the system reaches the ready state, so we
// send it here when the device first boots.
maybeSendUserPresentBroadcast();
}
...
}
在这个方法里主要调用了doKeyguardLocked和注册了KeyguardUpdateMonitorCallback
3、通过调用doKeyguardLocked显示锁屏界面
//frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
public
class
KeyguardViewMediator
extends
SystemUI {
...
private
void
doKeyguardLocked(Bundle options) {
// if another app is disabling us, don't show
if
(!mExternallyEnabled) {
if
(DEBUG) Log.d(TAG,
"doKeyguard: not showing because externally disabled"
);
// note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes
// for an occasional ugly flicker in this situation:
// 1) receive a call with the screen on (no keyguard) or make a call
// 2) screen times out
// 3) user hits key to turn screen back on
// instead, we reenable the keyguard when we know the screen is off and the call
// ends (see the broadcast receiver below)
// TODO: clean this up when we have better support at the window manager level
// for apps that wish to be on top of the keyguard
return
;
}
// if the keyguard is already showing, don't bother
if
(mStatusBarKeyguardViewManager.isShowing()) {
if
(DEBUG) Log.d(TAG,
"doKeyguard: not showing because it is already showing"
);
resetStateLocked();
return
;
}
// if the setup wizard hasn't run yet, don't show
final
boolean
requireSim = !SystemProperties.getBoolean(
"keyguard.no_require_sim"
,
false
);
final
boolean
absent = SubscriptionManager.isValidSubscriptionId(
mUpdateMonitor.getNextSubIdForState(IccCardConstants.State.ABSENT));
final
boolean
disabled = SubscriptionManager.isValidSubscriptionId(
mUpdateMonitor.getNextSubIdForState(IccCardConstants.State.PERM_DISABLED));
final
boolean
lockedOrMissing = mUpdateMonitor.isSimPinSecure()
|| ((absent || disabled) && requireSim);
if
(!lockedOrMissing && shouldWaitForProvisioning()) {
if
(DEBUG) Log.d(TAG,
"doKeyguard: not showing because device isn't provisioned"
+
" and the sim is not locked or missing"
);
return
;
}
if
(mLockPatternUtils.isLockScreenDisabled(KeyguardUpdateMonitor.getCurrentUser())
&& !lockedOrMissing) {
if
(DEBUG) Log.d(TAG,
"doKeyguard: not showing because lockscreen is off"
);
return
;
}
if
(mLockPatternUtils.checkVoldPassword(KeyguardUpdateMonitor.getCurrentUser())) {
if
(DEBUG) Log.d(TAG,
"Not showing lock screen since just decrypted"
);
// Without this, settings is not enabled until the lock screen first appears
setShowingLocked(
false
);
hideLocked();
mUpdateMonitor.reportSuccessfulStrongAuthUnlockAttempt();
return
;
}
if
(DEBUG) Log.d(TAG,
"doKeyguard: showing the lock screen"
);
showLocked(options);
}
...
}
4、在handleShow里设置一些锁屏状态和显示锁屏界面
//frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
public
class
KeyguardViewMediator
extends
SystemUI {
...
private
void
handleShow(Bundle options) {
synchronized
(KeyguardViewMediator.
this
) {
if
(!mSystemReady) {
if
(DEBUG) Log.d(TAG,
"ignoring handleShow because system is not ready."
);
return
;
}
else
{
if
(DEBUG) Log.d(TAG,
"handleShow"
);
}
setShowingLocked(
true
);
mStatusBarKeyguardViewManager.show(options);
mHiding =
false
;
mWakeAndUnlocking =
false
;
resetKeyguardDonePendingLocked();
mHideAnimationRun =
false
;
updateActivityLockScreenState();
adjustStatusBarLocked();
userActivity();
mShowKeyguardWakeLock.release();
}
mKeyguardDisplayManager.show();
}
...
}
5、通过调用StatusBarKeyguardViewManager的show重置当前状态显示keyguard
//frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
public
class
StatusBarKeyguardViewManager {
...
public
void
show(Bundle options) {
mShowing =
true
;
mStatusBarWindowManager.setKeyguardShowing(
true
);
mScrimController.abortKeyguardFadingOut();
reset();
}
...
}
6、KeyguardSecurityContainer.showPrimarySecurityScreen
在KeyguardHostView的showPrimarySecurityScreen里调用KeyguardSecurityContainer的showPrimarySecurityScreen方法,如下
//frameworks/base/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
public
class
KeyguardSecurityContainer
extends
FrameLayout
implements
KeyguardSecurityView {
...
void
showPrimarySecurityScreen(
boolean
turningOff) {
SecurityMode securityMode = mSecurityModel.getSecurityMode();
if
(DEBUG) Log.v(TAG,
"showPrimarySecurityScreen(turningOff="
+ turningOff +
")"
);
showSecurityScreen(securityMode);
}
...
}
//frameworks/base/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java
public
class
KeyguardSecurityModel {
public
enum
SecurityMode {
Invalid,
// NULL state
None,
// No security enabled
Pattern,
// Unlock by drawing a pattern.
Password,
// Unlock by entering an alphanumeric password
PIN,
// Strictly numeric password
SimPin,
// Unlock by entering a sim pin.
SimPuk
// Unlock by entering a sim puk
}
...
}
showSecurityScreen方法如下:
//frameworks/base/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityContainer.java
public
class
KeyguardSecurityContainer
extends
FrameLayout
implements
KeyguardSecurityView {
...
private
void
showSecurityScreen(SecurityMode securityMode) {
if
(DEBUG) Log.d(TAG,
"showSecurityScreen("
+ securityMode +
")"
);
if
(securityMode == mCurrentSecuritySelection)
return
;
KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
KeyguardSecurityView newView = getSecurityView(securityMode);
//根据securityMode获取对应的view
// Emulate Activity life cycle
if
(oldView !=
null
) {
oldView.onPause();
oldView.setKeyguardCallback(mNullCallback);
// ignore requests from old view
}
if
(securityMode != SecurityMode.None) {
newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
newView.setKeyguardCallback(mCallback);
}
// Find and show this child.
final
int
childCount = mSecurityViewFlipper.getChildCount();
final
int
securityViewIdForMode = getSecurityViewIdForMode(securityMode);
for
(
int
i =
0
; i < childCount; i++) {
if
(mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) {
mSecurityViewFlipper.setDisplayedChild(i);
break
;
}
}
mCurrentSecuritySelection = securityMode;
mSecurityCallback.onSecurityModeChanged(securityMode,
securityMode != SecurityMode.None && newView.needsInput());
}
...
}