Android系統啟動流程(四)Launcher程序啟動過程解析(附帶面試題)

2020-10-08 16:00:22

前面我們分析了init程序,zygote程序,SystemServer程序,本篇的Launcher是系統啟動流程的最後一個程序。

1 Launcher概述

Launcher程序是一個系統的應用程式,位於packages/apps/Launcher3中,它用於顯示已經安裝的應用程式,它通過存取PackageManagerService獲取安裝的應用程式,然後將他們封裝成一個個的快捷圖示顯示到螢幕上,每一個圖示包含了被啟動應用程式的Intent資訊,點選之後就可以啟動對應應用程式。

2 Launcher程序解析

private void startOtherServices() {
	mActivityManagerService.systemReady(new Runnable() {
		@Override
            public void run() {
			...
			mSystemServiceManager.startBootPhase(
                        SystemService.PHASE_ACTIVITY_MANAGER_READY);
		}
	}
}

Launcher的入口函數在System.startOtherServices方法中,我們進入systemReady方法中:

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

    public void systemReady(final Runnable goingCallback) {
    	...
		mStackSupervisor.resumeFocusedStackTopActivityLocked();
		...
	}

進入到了ActivityStackSupervisor.resumeFocusedStackTopActivityLocked中

frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

	
	boolean resumeFocusedStackTopActivityLocked() {
        return resumeFocusedStackTopActivityLocked(null, null, null);
    }

    boolean resumeFocusedStackTopActivityLocked(
            ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
        if (targetStack != null && isFocusedStack(targetStack)) {
            return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
        }
        final ActivityRecord r = mFocusedStack.topRunningActivityLocked();
        if (r == null || r.state != RESUMED) {
            mFocusedStack.resumeTopActivityUncheckedLocked(null, null);
        }
        return false;
    }

在上面最後呼叫到了mFocusedStack.resumeTopActivityUncheckedLocked,mFocusedStack是ActivityStack型別的,我們進入檢視原始碼:

frameworks/base/services/core/java/com/android/server/am/ActivityStack.java

boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
        if (mStackSupervisor.inResumeTopActivity) {
            // Don't even start recursing.
            return false;
        }

        boolean result = false;
        try {
            // Protect against recursion.
            mStackSupervisor.inResumeTopActivity = true;
            if (mService.mLockScreenShown == ActivityManagerService.LOCK_SCREEN_LEAVING) {
                mService.mLockScreenShown = ActivityManagerService.LOCK_SCREEN_HIDDEN;
                mService.updateSleepIfNeededLocked();
            }
            result = resumeTopActivityInnerLocked(prev, options);
        } finally {
            mStackSupervisor.inResumeTopActivity = false;
        }
        return result;
    }

	private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
        ...
        
        final TaskRecord prevTask = prev != null ? prev.task : null;
        if (next == null) {
            /***
             * 這裡啟動了Launcher
             */
            return isOnHomeDisplay() &&
                    mStackSupervisor.resumeHomeStackTask(returnTaskType, prev, reason);
        }
    }

上面從resumeTopActivityUncheckedLocked呼叫到了resumeTopActivityInnerLocked中,最後呼叫到了ActivityStackSupervisor.resumeHomeStackTask函數。

frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

	boolean resumeHomeStackTask(int homeStackTaskType, ActivityRecord prev, String reason) {
        ...
        
        // Only resume home activity if isn't finishing.
        if (r != null && !r.finishing) {
            mService.setFocusedActivityLocked(r, myReason);
            return resumeFocusedStackTopActivityLocked(mHomeStack, prev, null);
        }
        return mService.startHomeActivityLocked(mCurrentUser, myReason);
    }

接著進入到了ActivityManagerService.startHomeActivityLocked

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

boolean startHomeActivityLocked(int userId, String reason) {
        if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
                && mTopAction == null) {
            // We are running in factory test mode, but unable to find
            // the factory test app, so just sit around displaying the
            // error message and don't try to start anything.
            return false;
        }
        // 1 
        Intent intent = getHomeIntent();
        ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
        if (aInfo != null) {
            intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
            // Don't do this if the home app is currently being
            // instrumented.
            aInfo = new ActivityInfo(aInfo);
            aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
            ProcessRecord app = getProcessRecordLocked(aInfo.processName,
                    aInfo.applicationInfo.uid, true);
            if (app == null || app.instrumentationClass == null) {
                intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
                mActivityStarter.startHomeActivityLocked(intent, aInfo, reason);
            }
        } else {
            Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
        }

        return true;
    }

在註釋1處的getHomeIntent方法程式碼如下:

	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);//1 
        }
        return intent;
    }

在註釋1處新增了一個CATEGORY_HOMECATEGORY_HOME = "android.intent.category.HOME",這裡我們鎖定就是啟動了Launcher頁面,因為我們檢視系統應用Launcher的清單檔案中匹配了它,Launcher的清單檔案如下:

<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.launcher3">
    <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="16"/>

    <uses-permission android:name="android.permission.CALL_PHONE" />
    ...
    
    <application
        android:allowBackup="@bool/enable_backup"
        android:backupAgent="com.android.launcher3.LauncherBackupAgentHelper"
        android:hardwareAccelerated="true"
        android:icon="@mipmap/ic_launcher_home"
        android:label="@string/app_name"
        android:largeHeap="@bool/config_largeHeap"
        android:restoreAnyVersion="true"
        android:supportsRtl="true" >

        <activity
            android:name="com.android.launcher3.Launcher"
            android:launchMode="singleTask"
            android:clearTaskOnLaunch="true"
            android:stateNotNeeded="true"
            android:theme="@style/Theme"
            android:windowSoftInputMode="adjustPan"
            android:screenOrientation="nosensor"
            android:configChanges="keyboard|keyboardHidden|navigation"
            android:resumeWhilePausing="true"
            android:taskAffinity=""
            android:enabled="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.HOME" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.MONKEY"/>
            </intent-filter>
        </activity>      
    </application>
</manifest>

從上面可以看出Launcher的intent-filter匹配了上面的Intent,所以Launcher就被啟動起來了。就會執行onCreate方法。


3 Launcher程序圖示顯示過程

Launcher.onCreate函數如下:

packages/apps/Launcher3/src/com/android/launcher3/Launcher.java

@Override
    protected void onCreate(Bundle savedInstanceState) {
       
        super.onCreate(savedInstanceState);
		//1
        LauncherAppState app = LauncherAppState.getInstance();
		//2
        mModel = app.setLauncher(this);
       
        setContentView(R.layout.launcher);
        
        if (!mRestoring) {
           		//3
                mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);
            } else {
                // We only load the page synchronously if the user rotates (or triggers a
                // configuration change) while launcher is in the foreground
                mModel.startLoader(mWorkspace.getRestorePage());
            }
        }
    }

註釋1處單例獲取LauncherAppState的範例,在註釋2處呼叫它的setLauncher函數並將Launcher物件傳入,setLauncher函數如下所示:

packages/apps/Launcher3/src/com/android/launcher3/LauncherAppState.java

 	LauncherModel setLauncher(Launcher launcher) {
        getLauncherProvider().setLauncherProviderChangeListener(launcher);
        mModel.initialize(launcher);
        mAccessibilityDelegate = ((launcher != null) && Utilities.ATLEAST_LOLLIPOP) ?
            new LauncherAccessibilityDelegate(launcher) : null;
        return mModel;
    }

接著呼叫到了LauncherModel的initialize函數:

packages/apps/Launcher3/src/com/android/launcher3/LauncherModel.java

/**
     * Set this as the current Launcher activity object for the loader.
     */
    public void initialize(Callbacks callbacks) {
        synchronized (mLock) {
            // Disconnect any of the callbacks and drawables associated with ItemInfos on the
            // workspace to prevent leaking Launcher activities on orientation change.
            unbindItemInfosAndClearQueuedBindRunnables();
            mCallbacks = new WeakReference<Callbacks>(callbacks);
        }
    }

我們看到上面其實就是設定了一個回撥函數,回撥函數就是Launcher自身,不過是以一個弱參照的方式,這個回撥後面會用到。

接著分析onCreate中的註釋3的部分,launcherModel.startLoader方法:

  @Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
    static {
        sWorkerThread.start();
    }
    @Thunk static final Handler sWorker = new Handler(sWorkerThread.getLooper());

	public void startLoader(int synchronousBindPage, int loadFlags) {
        // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
        InstallShortcutReceiver.enableInstallQueue();
        synchronized (mLock) {
            // Clear any deferred bind-runnables from the synchronized load process
            // We must do this before any loading/binding is scheduled below.
            synchronized (mDeferredBindRunnables) {
                mDeferredBindRunnables.clear();
            }

            // Don't bother to start the thread if we know it's not going to do anything
            if (mCallbacks != null && mCallbacks.get() != null) {
                // If there is already one running, tell it to stop.
                stopLoaderLocked();
                mLoaderTask = new LoaderTask(mApp.getContext(), loadFlags);
                if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
                        && mAllAppsLoaded && mWorkspaceLoaded && !mIsLoaderTaskRunning) {
                    mLoaderTask.runBindSynchronousPage(synchronousBindPage);
                } else {
                    sWorkerThread.setPriority(Thread.NORM_PRIORITY);
                    sWorker.post(mLoaderTask);
                }
            }
        }
    }

上面建立了一個HandlerThread的子執行緒的Handler,用來處理耗時任務,在上面的最後post了一個任務,這個任務的類是LoaderTask.java,實現了Runnable介面,run方法如下:

	public void run() {
            synchronized (mLock) {
                if (mStopped) {
                    return;
                }
                mIsLoaderTaskRunning = true;
            }
            // Optimize for end-user experience: if the Launcher is up and // running with the
            // All Apps interface in the foreground, load All Apps first. Otherwise, load the
            // workspace first (default).
            keep_running: {
                if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
                //1 
                loadAndBindWorkspace();

                if (mStopped) {
                    break keep_running;
                }

                waitForIdle();

                // second step
                if (DEBUG_LOADERS) Log.d(TAG, "step 2: loading all apps");
                // 2
                loadAndBindAllApps();
            }

            // Clear out this reference, otherwise we end up holding it until all of the
            // callback runnables are done.
            mContext = null;

            synchronized (mLock) {
                // If we are still the last one to be scheduled, remove ourselves.
                if (mLoaderTask == this) {
                    mLoaderTask = null;
                }
                mIsLoaderTaskRunning = false;
                mHasLoaderCompletedOnce = true;
            }
        }

上面的兩個註釋寫的很清楚,在註釋1處loadAndBindWorkspace是載入工作空間,在註釋2處loadAndBindAllApps用來載入所有的app。

loadAndBindAllApps原始碼如下:

 	private void loadAndBindAllApps() {
            if (DEBUG_LOADERS) {
                Log.d(TAG, "loadAndBindAllApps mAllAppsLoaded=" + mAllAppsLoaded);
            }
            if (!mAllAppsLoaded) {
            	// 1 
                loadAllApps();
                synchronized (LoaderTask.this) {
                    if (mStopped) {
                        return;
                    }
                }
                updateIconCache();
                synchronized (LoaderTask.this) {
                    if (mStopped) {
                        return;
                    }
                    mAllAppsLoaded = true;
                }
            } else {
                onlyBindAllApps();
            }
        }

註釋1處呼叫了loadAllApps(),原始碼如下:

	private void loadAllApps() {
		...
        mHandler.post(new Runnable() {
            public void run() {
                final long bindTime = SystemClock.uptimeMillis();
                final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                if (callbacks != null) {
                    callbacks.bindAllApplications(added);//1
                    ...
                }
            }
        });
       ...
    }

這裡的callbacks就是上面弱參照儲存的Launcher,所以我們直接檢視Launcher中的bindAllApplications,程式碼如下:

packages/apps/Launcher3/src/com/android/launcher3/Launcher.java

	 public void bindAllApplications(final ArrayList<AppInfo> apps) {
        if (waitUntilResume(mBindAllApplicationsRunnable, true)) {
            mTmpAppsList = apps;
            return;
        }
        if (mAppsView != null) {
        	//1 
            mAppsView.setApps(apps);
        }
        if (mLauncherCallbacks != null) {
            mLauncherCallbacks.bindAllApplications(apps);
        }
    }

在註釋1處呼叫了 mAppsView的setApps,mAppsView是一個AllAppsContainerView型別的,我們檢視它的setApps方法:

packages/apps/Launcher3/src/com/android/launcher3/allapps/AllAppsContainerView.java

	private final AlphabeticalAppsList mApps;
	
	public void setApps(List<AppInfo> apps) {
        mApps.setApps(apps);
    }

這裡我們檢視onFinishInflate函數

    
    private AllAppsRecyclerView mAppsRecyclerView;
	
	@Override
    protected void onFinishInflate() {
        super.onFinishInflate();

		//1 初始化控制元件
        mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view);
        //2 傳遞應用
        mAppsRecyclerView.setApps(mApps);
        mAppsRecyclerView.setLayoutManager(mLayoutManager);
        //3 設定adapter
        mAppsRecyclerView.setAdapter(mAdapter);
        mAppsRecyclerView.setHasFixedSize(true);
        mAppsRecyclerView.addOnScrollListener(mElevationController);
        mAppsRecyclerView.setElevationController(mElevationController);
    }

onFinishInflate在xml載入完成之後就會呼叫,這裡launcher中app的顯示邏輯和我們平時將資料顯示到一個RecyclerView上基本一致,所以這裡也比較好理解。

4 Android系統啟動流程

在此對整個Android系統啟動流程做一個全面的總結:

1 按Power鍵啟動電源及系統啟動

當按下電源鍵,引導晶片程式碼開始從固化在ROM中預定義的地方開始執行,載入載入程式Bootloader到RAM,然後執行載入程式。

2 載入程式Bootloader

載入程式是Android作業系統被拉起來之前的一個程式,類似於window一樣,它的作用就是把系統拉起執行起來。它是針對特定的主機板與晶片的。裝置製造商要麼使用很受歡迎的載入程式比如redboot、uboot、qibootloader或者開發自己的載入程式,它不是Android作業系統的一部分。載入程式是OEM廠商或者運營商加鎖和限制的地方。

3 linux核心啟動

Android核心與桌面linux核心啟動的方式差不多。核心啟動時,設定快取、被保護記憶體、計劃列表,載入驅動。當核心完成系統設定,它首先在系統檔案中尋找」init」檔案,然後啟動root程序或者系統的第一個程序。

4 init程序啟動
建立和掛載啟動所需要的檔案目錄,初始化和啟動屬性服務,解析init.rc設定並且啟動Zygote程序。

5 Zygote程序啟動

建立JavaVM併為JavaVM註冊JNI,建立伺服器端Socket,啟動SystemServer程序。

6 SystemServer程序啟動

啟動Binder執行緒池和SystemServiceManager,並且啟動各種系統服務。

7 Launcher啟動

被SystemServer程序啟動的ActivityManagerService會啟動Launcher,Launcher啟動後會將已安裝應用的快捷圖示顯示到介面上。

5 面試題

1 Android系統啟動流程? 答案如上所述

2 什麼是寫時拷貝(copy- on-write)?

寫時拷貝copy_on_write技術: fork的實現是使用copy_on_write技術實現的,它是一種可以推遲甚至避免拷貝的技術,核心此時並不複製整個程序的地址空間,而是讓父子程序共用同一個地址空間,只有在子程序真正需要寫入資料的時候才會賦值地址空間,從而使得每個程序擁有自己的程序空間,也就是說資源的複製是在需要寫入資料的時候才會進行,在此之前只會以唯讀的形式共用。

3 孤兒程序和殭屍程序是什麼?

fork系統呼叫之後,父子程序將交替執行,執行順序不定。如果父程序先退出,子程序還沒退出那麼子程序的父程序將變為init程序(託孤給了init程序)。(注:任何一個程序都必須有父程序)如果子程序先退出,父程序還沒退出,那麼子程序必須等到父程序捕獲到了子程序的退出狀態才真正結束,否則這個時候子程序就成為僵程序(殭屍程序:只保留一些退出資訊供父程序查詢).

4 system_server 為什麼要在 Zygote 中啟動,而不是由 init 直接啟動呢?

Zygote 作為一個孵化器,可以提前載入一些資源,這樣 fork() 時基於 Copy-On-Write 機制建立的其他程序就能直接使用這些資源,而不用重新載入。比如 system_server 就可以直接使用 Zygote 中的 JNI函數、共用庫、常用的類、以及主題資源。

5 為什麼要專門使用 Zygote 程序去孵化應用程序,而不是讓 system_server 去孵化呢?

system_server 相比 Zygote 多執行了 AMS、WMS 等服務,這些對一個應用程式來說是不需要的。另外程序的 fork() 對多執行緒不友好,僅會將發起呼叫的執行緒拷貝到子程序,這可能會導致死鎖,而system_server 中肯定是有很多執行緒的。

6 多執行緒的程序fork呼叫為什麼會導致死鎖?

多執行緒的程序的fork呼叫:複製整個使用者空間的資料(通常使用 copy-on-write 的策略,
所以可以實現的速度很快)以及所有系統物件,然後僅複製當前執行緒到子程序。這裡:所有父程序中別的執行緒,到了子程序中都是突然蒸發掉的假設這麼一個環境,在 fork 之前,有一個子執行緒 lock 了某個鎖,獲得了對鎖的所有權。fork 以後,在子程序中,所有的額外執行緒都人間蒸發了。而鎖卻被正常複製了,在子程序看來,這個鎖沒有主人,所以沒有任何人可以對它解鎖。當子程序想 lock 這個鎖時,不再有任何手段可以解開了。程式發生死鎖

7 Zygote 為什麼不採用 Binder 機制進行 IPC 通訊?

Binder 機制中存在 Binder 執行緒池,是多執行緒的,如果 Zygote 採用 Binder 的話就存在上面說的fork() 與 多執行緒的問題了。其實嚴格來說,Binder 機制不一定要多執行緒,所謂的 Binder 執行緒只不過是在迴圈讀取 Binder 驅動的訊息而已,只註冊一個 Binder 執行緒也是可以工作的,比如 service manager就是這樣的。實際上 Zygote 儘管沒有采取 Binder 機制,它也不是單執行緒的,但它在 fork() 前主動停止了其他執行緒,fork() 後重新啟動了。