Android的Launcher啟動過程分析(2)

Android的Launcher啟動過程分析(1)
通過上篇文章我們了解了AndroidLauncher的啟動過程,下面我們繼續探究點擊Launcher中圖片啟動應用的過程。
通過上篇文章我們知道,最終ActivityStartController的startHomeActivity(Intent intent, ActivityInfo aInfo, String reason)函數啟動Launcher,代碼如下:

void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {
        mSupervisor.moveHomeStackTaskToTop(reason);

        mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
                .setOutActivity(tmpOutRecord)
                .setCallingUid(0)
                .setActivityInfo(aInfo)
                .execute();
        mLastHomeActivityStartRecord = tmpOutRecord[0];
        if (mSupervisor.inResumeTopActivity) {
            // If we are in resume section already, home activity will be initialized, but not
            // resumed (to avoid recursive resume) and will stay that way until something pokes it
            // again. We need to schedule another resume.
            mSupervisor.scheduleResumeTopActivities();
        }
    }

最終會調用ActivityStarter.startActivity函數將各種啟動參數輸入。
下面我們來看Launcher的onCreat方法:

  @Override
    protected void onCreate(Bundle savedInstanceState) {
       ...
        LauncherAppState app = LauncherAppState.getInstance();//tag1
        mDeviceProfile = getResources().getConfiguration().orientation
                == Configuration.ORIENTATION_LANDSCAPE ?
                app.getInvariantDeviceProfile().landscapeProfile
                : app.getInvariantDeviceProfile().portraitProfile;

        mSharedPrefs = Utilities.getPrefs(this);
        mIsSafeModeEnabled = getPackageManager().isSafeMode();
        mModel = app.setLauncher(this);//tag2
        ....
        if (!mRestoring) {
            if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {
                mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);//2
            } else {
                mModel.startLoader(mWorkspace.getRestorePage());
            }
        }
...
    }

tag1處獲取LauncherAppState的實例并在注釋tag2處調用它的setLauncher函數并將Launcher對象傳入,LauncherAppState的setLauncher函數如下所示:

   LauncherModel setLauncher(Launcher launcher) {
        getLocalProvider(mContext).setLauncherProviderChangeListener(launcher);
        mModel.initialize(launcher);
        return mModel;
    }

我們看到mModel.initialize(launcher)函數,代碼如下:

/**
     * Set this as the current Launcher activity object for the loader.
     */
    public void initialize(Callbacks callbacks) {
        synchronized (mLock) {
            Preconditions.assertUIThread();
            mCallbacks = new WeakReference<>(callbacks);
        }
    }

在initialize函數中會將Callbacks,也就是傳入的Launcher 封裝成一個弱引用對象。因此我們得知mCallbacks變量指的就是封裝成弱引用對象的Launcher,這個mCallbacks后文會用到它。
再回到Launcher的onCreate函數,在tag2處調用了LauncherModel的startLoader函數:

@Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");//tag1
    static {
        sWorkerThread.start();
    }
    @Thunk static final Handler sWorker = new Handler(sWorkerThread.getLooper());//tag2
...
 public boolean startLoader(int synchronousBindPage) {
        // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
        InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_LOADER_RUNNING);
        synchronized (mLock) {
            // Don't bother to start the thread if we know it's not going to do anything
            if (mCallbacks != null && mCallbacks.get() != null) {
                final Callbacks oldCallbacks = mCallbacks.get();
                // Clear any pending bind-runnables from the synchronized load process.
                mUiExecutor.execute(oldCallbacks::clearPendingBinds);

                // If there is already one running, tell it to stop.
                stopLoader();
                LoaderResults loaderResults = new LoaderResults(mApp, sBgDataModel,
                        mBgAllAppsList, synchronousBindPage, mCallbacks);//tag3
                if (mModelLoaded && !mIsLoaderTaskRunning) {
                    // Divide the set of loaded items into those that we are binding synchronously,
                    // and everything else that is to be bound normally (asynchronously).
                    loaderResults.bindWorkspace();
                    // For now, continue posting the binding of AllApps as there are other
                    // issues that arise from that.
                    loaderResults.bindAllApps();
                    loaderResults.bindDeepShortcuts();
                    loaderResults.bindWidgets();
                    return true;
                } else {
                    startLoaderForResults(loaderResults);
                }
            }
        }
        return false;
    }

tag1處創建了具有消息循環的線程HandlerThread對象。tag2處創建了Handler,并且傳入HandlerThread的Looper。Hander的作用就是向HandlerThread發送消息的,如果已經load或者正在load就直接綁定數據,否者接著看startLoaderForResults函數如下:

public void startLoaderForResults(LoaderResults results) {
        synchronized (mLock) {
            stopLoader();
            mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, sBgDataModel, results);
            runOnWorkerThread(mLoaderTask);
        }
    }

我們看到先將load任務停止,然后創建一個LoaderTask(Runnable)對像,同時調用 runOnWorkerThread(mLoaderTask);函數如下:

/** Runs the specified runnable immediately if called from the worker thread, otherwise it is
     * posted on the worker thread handler. */
    private static void runOnWorkerThread(Runnable r) {
        if (sWorkerThread.getThreadId() == Process.myTid()) {
            r.run();
        } else {
            // If we are not on the worker thread, then post to the worker handler
            sWorker.post(r);
        }
    }

如果沒有工作在worker thread線程的話直接調用handler通訊將Runnable發送給HandlerThread,否者直接調用Runnable的run函數,LoaderTask的run方法如下:

 public void run() {
        try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
            TraceHelper.partitionSection(TAG, "step 1.1: loading workspace");
            loadWorkspace();

            verifyNotStopped();
            TraceHelper.partitionSection(TAG, "step 1.2: bind workspace workspace");
            mResults.bindWorkspace();

            // Notify the installer packages of packages with active installs on the first screen.
            TraceHelper.partitionSection(TAG, "step 1.3: send first screen broadcast");
            sendFirstScreenActiveInstallsBroadcast();

            // Take a break
            TraceHelper.partitionSection(TAG, "step 1 completed, wait for idle");
            waitForIdle();
            verifyNotStopped();

            // second step
            TraceHelper.partitionSection(TAG, "step 2.1: loading all apps");
            loadAllApps();

            TraceHelper.partitionSection(TAG, "step 2.2: Binding all apps");
            verifyNotStopped();
            mResults.bindAllApps();

            verifyNotStopped();
            TraceHelper.partitionSection(TAG, "step 2.3: Update icon cache");
            updateIconCache();

            // Take a break
            TraceHelper.partitionSection(TAG, "step 2 completed, wait for idle");
            waitForIdle();
            verifyNotStopped();

            // third step
            TraceHelper.partitionSection(TAG, "step 3.1: loading deep shortcuts");
            loadDeepShortcuts();

            verifyNotStopped();
            TraceHelper.partitionSection(TAG, "step 3.2: bind deep shortcuts");
            mResults.bindDeepShortcuts();

            // Take a break
            TraceHelper.partitionSection(TAG, "step 3 completed, wait for idle");
            waitForIdle();
            verifyNotStopped();

            // fourth step
            TraceHelper.partitionSection(TAG, "step 4.1: loading widgets");
            mBgDataModel.widgetsModel.update(mApp, null);

            verifyNotStopped();
            TraceHelper.partitionSection(TAG, "step 4.2: Binding widgets");
            mResults.bindWidgets();

            transaction.commit();
        } catch (CancellationException e) {
            // Loader stopped, ignore
            TraceHelper.partitionSection(TAG, "Cancelled");
        }
        TraceHelper.endSection(TAG);
    }

auncher是用工作區的形式來顯示系統安裝的應用程序的快捷圖標,每一個工作區都是來描述一個抽象桌面的,它由n個屏幕組成,每個屏幕又分n個單元格,每個單元格用來顯示一個應用程序的快捷圖標。step 1.1處調用loadWorkspace()函數用來加載工作區信息,step 1.2調用 mResults.bindWorkspace()來綁定工作區信息;step2.1處的loadAllApps()函數是用來加載系統已經安裝的應用程序信息,step2.2處調用mResults.bindAllApps()綁定已安裝應用程序信息,這里我們來看下loadAllApps函數代碼如下所示:

  private void loadAllApps() {
        final List<UserHandle> profiles = mUserManager.getUserProfiles();

        // Clear the list of apps
        mBgAllAppsList.clear();
        for (UserHandle user : profiles) {
            // Query for the set of apps
            final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
            // Fail if we don't have any apps
            // TODO: Fix this. Only fail for the current user.
            if (apps == null || apps.isEmpty()) {
                return;
            }
            boolean quietMode = mUserManager.isQuietModeEnabled(user);
            // Create the ApplicationInfos
            for (int i = 0; i < apps.size(); i++) {
                LauncherActivityInfo app = apps.get(i);
                // This builds the icon bitmaps.
                mBgAllAppsList.add(new AppInfo(app, user, quietMode), app);
            }
        }

        if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS) {
            // get all active sessions and add them to the all apps list
            for (PackageInstaller.SessionInfo info :
                    mPackageInstaller.getAllVerifiedSessions()) {
                mBgAllAppsList.addPromiseApp(mApp.getContext(),
                        PackageInstallerCompat.PackageInstallInfo.fromInstallingState(info));
            }
        }

        mBgAllAppsList.added = new ArrayList<>();

通過LauncherAppsCompat.getActivityList(null, user)函數獲取app,并且封裝成AppInfo存儲在mBgAllAppsList中,這里的getActivityList函數是調用的LauncherAppsCompatVL中的,代碼如下:

 public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
        ParceledListSlice<ResolveInfo> activities = null;
        try {
            activities = mService.getLauncherActivities(packageName, user);
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
        if (activities == null) {
            return Collections.EMPTY_LIST;
        }
        ArrayList<LauncherActivityInfo> lais = new ArrayList<LauncherActivityInfo>();
        for (ResolveInfo ri : activities.getList()) {
            LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri.activityInfo, user);
            if (DEBUG) {
                Log.v(TAG, "Returning activity for profile " + user + " : "
                        + lai.getComponentName());
            }
            lais.add(lai);
        }
        return lais;
    }

我們看到mService.getLauncherActivities(packageName, user),這里的mService是 ILauncherApps.aidl,通過IPC獲取ILauncherApps的?;氐絃oaderTask的run方法中,我們看到繼續執行step2.2 mResults.bindAllApps(),代碼如下:

 public void bindAllApps() {
        // shallow copy
        @SuppressWarnings("unchecked")
        final ArrayList<AppInfo> list = (ArrayList<AppInfo>) mBgAllAppsList.data.clone();

        Runnable r = new Runnable() {
            public void run() {
                Callbacks callbacks = mCallbacks.get();
                if (callbacks != null) {
                    callbacks.bindAllApplications(list);
                }
            }
        };
        mUiExecutor.execute(r);
    }

這里的callBacks就是Launcher實例,所以我們直接去看Launcher的bindAllApplications方法代碼如下:

public void bindAllApplications(ArrayList<AppInfo> apps) {
        mAppsView.getAppsStore().setApps(apps);//tag1

        if (mLauncherCallbacks != null) {
            mLauncherCallbacks.bindAllApplications(apps);
        }
    }

在tag1處會調用AllAppsContainerView的setApps函數,并將包含應用信息的列表apps傳進去,AllAppsContainerView的setApps函數如下所示:
包含應用信息的列表apps已經傳給了AllAppsContainerView,查看AllAppsContainerView的setApps函數:

        void setup(@NonNull View rv, @Nullable ItemInfoMatcher matcher) {
            appsList.updateItemFilter(matcher);
            recyclerView = (AllAppsRecyclerView) rv;
            recyclerView.setEdgeEffectFactory(createEdgeEffectFactory());
            recyclerView.setApps(appsList, mUsingTabs);//tag1
            recyclerView.setLayoutManager(layoutManager);
            recyclerView.setAdapter(adapter);//tag2
            recyclerView.setHasFixedSize(true);
            // No animations will occur when changes occur to the items in this RecyclerView.
            recyclerView.setItemAnimator(null);
            FocusedItemDecorator focusedItemDecorator = new FocusedItemDecorator(recyclerView);
            recyclerView.addItemDecoration(focusedItemDecorator);
            adapter.setIconFocusListener(focusedItemDecorator.getFocusListener());
            applyVerticalFadingEdgeEnabled(verticalFadingEdge);
            applyPadding();
        }

tag1處得到AllAppsRecyclerView用來顯示App列表,并將apps的信息列表傳進去,并在tag2處為AllAppsRecyclerView設置Adapter。這樣應用程序快捷圖標的列表就會顯示在屏幕上。
Launcher啟動流程就講到這里,不足之處還請指正。

總結一下Android系統的啟動流程大體如下:

1.啟動電源以及系統啟動
當電源按下時引導芯片代碼開始從預定義的地方(固化在ROM)開始執行。加載引導程序Bootloader到RAM,然后執行。
2.引導程序BootLoader
引導程序BootLoader是在Android操作系統開始運行前的一個小程序,它的主要作用是把系統OS拉起來并運行。
3.Linux內核啟動
內核啟動時,設置緩存、被保護存儲器、計劃列表、加載驅動。當內核完成系統設置,它首先在系統文件中尋找init.rc文件,并啟動init進程。
4.init進程啟動
初始化和啟動屬性服務,并且啟動Zygote進程。
5.Zygote進程啟動
創建JavaVM并為JavaVM注冊JNI,創建服務端Socket,啟動SystemServer進程。
6.SystemServer進程啟動
啟動Binder線程池和SystemServiceManager,并且啟動各種系統服務。
7.Launcher啟動
被SystemServer進程啟動的ActivityManagerService會啟動Launcher,Launcher啟動后會將已安裝應用的快捷圖標顯示到界面上。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容