天天看點

Android開機動畫流程—結束階段

(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也會設定一次;

繼續閱讀