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

Android的Launcher啟動過程分析(1)
通過上篇文章我們了解了AndroidLauncher的啟動過程阶牍,下面我們繼續(xù)探究點擊Launcher中圖片啟動應用的過程腿倚。
通過上篇文章我們知道卵贱,最終ActivityStartController的startHomeActivity(Intent intent, ActivityInfo aInfo, String reason)函數(shù)啟動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函數(shù)將各種啟動參數(shù)輸入。
下面我們來看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函數(shù)并將Launcher對象傳入诫欠,LauncherAppState的setLauncher函數(shù)如下所示:

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

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

/**
     * 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函數(shù)中會將Callbacks浴栽,也就是傳入的Launcher 封裝成一個弱引用對象荒叼。因此我們得知mCallbacks變量指的就是封裝成弱引用對象的Launcher,這個mCallbacks后文會用到它典鸡。
再回到Launcher的onCreate函數(shù)被廓,在tag2處調用了LauncherModel的startLoader函數(shù):

@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處創(chuàng)建了具有消息循環(huán)的線程HandlerThread對象。tag2處創(chuàng)建了Handler萝玷,并且傳入HandlerThread的Looper嫁乘。Hander的作用就是向HandlerThread發(fā)送消息的,如果已經(jīng)load或者正在load就直接綁定數(shù)據(jù)球碉,否者接著看startLoaderForResults函數(shù)如下:

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

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

/** 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發(fā)送給HandlerThread睁冬,否者直接調用Runnable的run函數(shù)挎春,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是用工作區(qū)的形式來顯示系統(tǒng)安裝的應用程序的快捷圖標,每一個工作區(qū)都是來描述一個抽象桌面的豆拨,它由n個屏幕組成直奋,每個屏幕又分n個單元格,每個單元格用來顯示一個應用程序的快捷圖標施禾。step 1.1處調用loadWorkspace()函數(shù)用來加載工作區(qū)信息脚线,step 1.2調用 mResults.bindWorkspace()來綁定工作區(qū)信息;step2.1處的loadAllApps()函數(shù)是用來加載系統(tǒng)已經(jīng)安裝的應用程序信息弥搞,step2.2處調用mResults.bindAllApps()綁定已安裝應用程序信息邮绿,這里我們來看下loadAllApps函數(shù)代碼如下所示:

  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)函數(shù)獲取app渠旁,并且封裝成AppInfo存儲在mBgAllAppsList中,這里的getActivityList函數(shù)是調用的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的∩低伲回到LoaderTask的run方法中投慈,我們看到繼續(xù)執(zhí)行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函數(shù)伪煤,并將包含應用信息的列表apps傳進去,AllAppsContainerView的setApps函數(shù)如下所示:
包含應用信息的列表apps已經(jīng)傳給了AllAppsContainerView凛辣,查看AllAppsContainerView的setApps函數(shù):

        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系統(tǒng)的啟動流程大體如下:

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末改艇,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子坟岔,更是在濱河造成了極大的恐慌谒兄,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件社付,死亡現(xiàn)場離奇詭異承疲,居然都是意外死亡,警方通過查閱死者的電腦和手機鸥咖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門燕鸽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人啼辣,你說我怎么就攤上這事啊研。” “怎么了鸥拧?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵党远,是天一觀的道長。 經(jīng)常有香客問我富弦,道長沟娱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任舆声,我火速辦了婚禮花沉,結果婚禮上柳爽,老公的妹妹穿的比我還像新娘媳握。我一直安慰自己,他們只是感情好磷脯,可當我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布蛾找。 她就那樣靜靜地躺著,像睡著了一般赵誓。 火紅的嫁衣襯著肌膚如雪打毛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天俩功,我揣著相機與錄音幻枉,去河邊找鬼。 笑死诡蜓,一個胖子當著我的面吹牛熬甫,可吹牛的內容都是我干的。 我是一名探鬼主播蔓罚,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼椿肩,長吁一口氣:“原來是場噩夢啊……” “哼瞻颂!你這毒婦竟也來了?” 一聲冷哼從身側響起郑象,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤贡这,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后厂榛,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盖矫,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了户秤。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蹄衷。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖辐马,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情局义,我是刑警寧澤喜爷,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站萄唇,受9級特大地震影響檩帐,放射性物質發(fā)生泄漏。R本人自食惡果不足惜另萤,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一湃密、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧四敞,春花似錦泛源、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至铺厨,卻和暖如春缎玫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背解滓。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工赃磨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人洼裤。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓邻辉,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子恩沛,可洞房花燭夜當晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內容