(1)Launcher的啟動
在前面的 Android開機流程可以知道,SystemServer最後會調用到AMS.systemReady。
//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public void systemReady(final Runnable goingCallback, TimingsTraceLog traceLog) {
traceBeginAndSlog("StartSystemUI");
try {
//startSystemUi
startSystemUi(context, windowManagerF);
} catch (Throwable e) {
reportWtf("starting System UI", e);
}
traceEnd();
//startHomeActivity
traceLog.traceBegin("PhaseActivityManagerReady");
startHomeActivityLocked(currentUserId, "systemReady");
...
}
Intent getHomeIntent() {
Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
intent.setComponent(mTopComponent);
intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
intent.addCategory(Intent.CATEGORY_HOME);
}
return intent;
}
當System程序将系統中的關鍵服務啟動起來之後,就會将Launcher啟動起來。
一個Activity元件在啟動起來之後,就會被記錄起來,等到它所運作在的主線程空閑的時候,這個主線程就會向ActivityManagerService發送一個Activity元件空閑的通知。
由于應用程式Launcher是系統中第一個被啟動的應用程式,即它的根Activity元件是系統中第一個被啟動的Activity元件,是以,當ActivityManagerService接收到它的空閑通知的時候,就可以知道系統是剛剛啟動起來的。
在這種情況下,ActivityManagerService就會停止顯示開機動畫,以便可以在螢幕中顯示應用程式Lancher的界面。
注:
如果一個線程想要在空閑的時候處理一些事務,那麼就必須要向這個線程的消息隊列注冊一個空閑消息處理器。自定義的空閑消息處理器燈必須要從MessageQueue.IdleHandler類繼承下來,并且重寫成員函數queueIdle。當一個線程空閑的時候,即消息隊列中沒有新的消息需要處理的時候,那些注冊了的空閑消息處理器的成員函數queueIdle就會被調用。
應用程式的主線程是通過ActivityThread類來描述的,每當有一個新的Activity元件啟動起來的時候,ActivityThread類都會向它所描述的應用程式主線程的消息隊列注冊一個類型為Idler的空閑消息處理器。這樣一個應用程式的主線程就可以在空閑的時候,向ActivityManagerService發送一個Activity元件空閑通知,相當于是通知ActivityManagerService一個新的Activity元件已經準備就緒了。
另每一個新啟動的Activity元件隻有一次機會向ActivityManagerService發送一個空閑通知。
(2)退出動畫
應用程式主線程通知AMS有Activity元件空閑通知後,AMS就會調用mStackSupervisor.activityIdleInternalLocked,在這裡面檢查booting狀态并決定是否向AMS報告結束啟動過程,也就是結束顯示開機動畫。
(A)ActivityStack.activityIdleInternal
final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
Configuration config) {
......
boolean booting = false;
boolean enableScreen = false;
boolean activityRemoved = false;
ActivityRecord r = ActivityRecord.forToken(token);
if (r != null) {
......
if (isFrontStack(r.task.stack) || fromTimeout) {
booting = mService.mBooting;
mService.mBooting = false;
if (!mService.mBooted) {
mService.mBooted = true;
enableScreen = true;
}
}
}
......
if (booting || enableScreen) {
mService.postFinishBooting(booting, enableScreen);
}
......
return r;
}
(B)AMS.postFinishBooting
final MainHandler mHandler;
void postFinishBooting(boolean finishBooting, boolean enableScreen) {
mHandler.sendMessage(mHandler.obtainMessage(FINISH_BOOTING_MSG,
finishBooting? 1 : 0, enableScreen ? 1 : 0));
}
final class MainHandler extends Handler {
@Override
public void handleMessage(Message msg) {
......
case FINISH_BOOTING_MSG: {
if (msg.arg1 != 0) {
finishBooting();
}
if (msg.arg2 != 0) {
enableScreenAfterBoot();
}
break;
}
}
}
這裡最後調用enableScreenAfterBoot方法,以便可以将螢幕讓出來顯示應用程式Launcher的界面。
(C)AMS.enableScreenAfterBoot
void enableScreenAfterBoot() {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
SystemClock.uptimeMillis());
mWindowManager.enableScreenAfterBoot();
synchronized (this) {
updateEventDispatchingLocked();
}
}
(D)WMS.enableScreenAfterBoot
public void enableScreenAfterBoot() {
synchronized(mWindowMap) {
if (mSystemBooted) {
return;
}
// 用來記錄系統是否已經啟動完成,true代表完成
mSystemBooted = true;
hideBootMessagesLocked();
// 設定30s逾時
mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30 * 1000);
}
mPolicy.systemBooted();
performEnableScreen();
}
WMS的成員變量mSystemBooted用來記錄系統是否已經啟動完成的。如果已經啟動完成的話,那麼這個成員變量的值就會等于true,這時候WMS的成員函數enableScreenAfterBoot直接return,否則的話,WMS的成員函數enableScreenAfterBoot首先将這個成員變量的值設定為true,接着再調用另外一個成員函數performEnableScreen來執行停止顯示開機動畫的操作。
(E)WMS.performEnableScreen
private void performEnableScreen() {
...
if (!mBootAnimationStopped) {
Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
// stop boot animation
// formerly we would just kill the process, but we now ask it to exit so it
// can choose where to stop the animation.
// (1)設定屬性以通知bootanim程序退出
SystemProperties.set("service.bootanim.exit", "1");
mBootAnimationStopped = true;
}
// 執行SurfaceFlinger結束開機動畫的操作
try {
IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
if (surfaceFlinger != null) {
Parcel data = Parcel.obtain();
data.writeInterfaceToken("android.ui.ISurfaceComposer");
//(2)當SurfaceFlinger服務接收到類型為IBinder::FIRST_CALL_TRANSACTION,即類型為BOOT_FINISHED的程序間通信請求時,它就會将該請求交給它的成員函數bootFinished來處理。
surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED
data, null, 0);
data.recycle();
}
} catch (RemoteException ex) {
ProtoLog.e(WM_ERROR, "Boot completed: SurfaceFlinger is dead!");
}
}
...
try {
mActivityManager.bootAnimationComplete();
} catch (RemoteException e) {
}
mPolicy.enableScreenAfterBoot();
(F)SurfaceFlinger.bootFinished
void SurfaceFlinger::bootFinished()
{
if (mBootFinished == true) {
ALOGE("Extra call to bootFinished");
return;
}
mBootFinished = true;
// stop boot animation
property_set("service.bootanim.exit", "1");
...
}
以上兩處都會将屬性"service.bootanim.exit"設定為"1",當屬性"service.bootanim.exit"值被設為"1"時,android()就會退出,開機動畫顯示自然也就結束了。由于android()退出且傳回值為false,BootAnimation::threadLoop()線程也就結束了。
static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
void BootAnimation::checkExit() {
// Allow surface flinger to gracefully request shutdown
char value[PROPERTY_VALUE_MAX];
property_get(EXIT_PROP_NAME, value, "0");
int exitnow = atoi(value);
if (exitnow) {
// 屬性值為1,代表可以AMS層啟動完畢,launcher界面也準備好了, Thread請求退出
requestExit();
mCallbacks->shutdown();
}
}
至此,Android系統開機動畫的分析就全部結束了。
(3)總結
- 當我們設定一個系統屬性的時候,init程序都會接收到一個系統屬性變化事件。當發生變化的系統屬性的名稱等于“ctl.start”或者“ctl.stop”,那麼實際上是向init程序發出一個啟動或者停止服務的指令;
- BootAnimation通過在每幀繪制完畢後檢查屬性service.bootanim.exit的值判斷是否結束播放開機動畫,而這個值會在launcher應用的根Activity啟動完成處于idle狀态時由該應用通知AMS進而通知WMS設定的。其實在這一過程中SurfaceFlinger也會設定一次;