Android 8.0 源碼分析 (二) Launcher 啓動

前言

咱們熟知通常 Android 工程師都是在應用層上開發,不會涉及系統源碼,可是若是你想往底層發展,或者深刻插件化、Framework 系統層等開發工做,若是不瞭解 Android 源碼但是不行的,那麼接下來我基於本身的理解跟學習來記錄跟 Android 開發息息相關的源碼分析,大概從 Android 中的 SystemServer 啓動、四大組件啓動、AMS、PMS 等幾個維度來介紹,下面是個人計劃,固然在將來也有可能改變。java

尚未關注的小夥伴,能夠先關注一波,系列文章會持續更新。android

Android 8.0 源碼分析 (一) SystemServer 進程啓動app

Android 8.0 源碼分析 (二) Launcher 啓動ide

Android 8.0 源碼分析 (三) 應用程序進程建立到應用程序啓動的過程函數

Android 8.0 源碼分析 (四) Activity 啓動oop

Android 8.0 源碼分析 (五) Service 啓動源碼分析

Android 8.0 源碼分析 (六) BroadcastReceiver 啓動佈局

Android 8.0 源碼分析 (七) ContentProvider 啓動post

介紹

上面一章介紹了 Android 中 SystemServer 啓動和一些其它服務的啓動,那麼系統服務啓動完了以後,就該顯示 Android 系統桌面了,在顯示以前會請求 PMS 返回系統中已經安裝的應用程序信息,並將這些信息封裝成一個快捷圖標列表顯示在系統屏幕上,這樣用戶就能夠經過點擊桌面上的圖標來啓動相應的應用程序了,在 Android 中用於顯示桌面的 Activity 是 Launcher.java 下面咱們就分析 Launcher 啓動過程。學習

Launcher 的啓動分析

SystemServer 進程在啓動過程當中會啓動 PMS ,PMS 啓動後會將系統中的應用程序安裝完成。在此前已經啓動的 AMS 會將 Launcher 啓動起來。能夠看下執行的時序圖

KDsaIP.png

啓動 Launcher 的入口爲 AMS 的 systemRead 函數,它在 SystemServer 的 startOtherServices 函數中被調用,以下代碼所示:

//com.android.server SystemServer.java

public void startOtherServices(){
  ...
  //1. 這裏是 lambda 語法
  mActivityManagerService.systemReady(() -> {
  ...
  }     
}
複製代碼

咱們看 AMS 的 systemRead 函數具體實現

//com.android.server.am ActivityManagerService.java

  public void systemReady(final Runnable goingCallback, BootTimingsTraceLog traceLog) {
        ...
        synchronized(this) {
            ...
			//1. 
            mStackSupervisor.resumeFocusedStackTopActivityLocked();
            mUserController.sendUserSwitchBroadcastsLocked(-1, currentUserId);
            
        }
    }

複製代碼

systemReady 函數中調用了 ActivityStackSupervisor 的 resumeFocusedStackTopActivityLocked() 函數,以下代碼所示

//package 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)) {
            /** * ActivityStack 描述堆棧的 */
            return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
        }
        ...
        return false;
    }
複製代碼

根據上面的代碼,首先判斷啓動的 Activity 堆棧是否爲空,而且是須要的棧,那麼久執行 targetStack.resumeTopActivityUncheckedLocked 函數

//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;
            /** * 1. */
            result = resumeTopActivityInnerLocked(prev, options);
        } finally {
            mStackSupervisor.inResumeTopActivity = false;
        }
        
        mStackSupervisor.checkReadyForSleepLocked();

        return result;
    }
複製代碼

執行到 resumeTopActivityInnerLocked 函數,具體看代碼

//com.android.server.am; ActivityStack.java

    private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
    ...
    
     return isOnHomeDisplay() &&
                        mStackSupervisor.resumeHomeStackTask(prev, "prevFinished");
            }
    ...
      
    }

複製代碼

調用 ActivityStackSupervisor 的 resumeHomeStackTask 函數,代碼以下:

//com.android.server.am; ActivityStackSupervisor.java

    boolean resumeHomeStackTask(ActivityRecord prev, String reason) {
        ...

        if (prev != null) {
            prev.getTask().setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
        }
        //將 home 移動到棧頂
        mHomeStack.moveHomeStackTaskToTop();
        //獲取 HomeActivity
        ActivityRecord r = getHomeActivity();
        final String myReason = reason + " resumeHomeStackTask";

        // Only resume home activity if isn't finishing.
        if (r != null && !r.finishing) {
            moveFocusableActivityStackToFrontLocked(r, myReason);
            return resumeFocusedStackTopActivityLocked(mHomeStack, prev, null);
        }
        //調用 AMS startHomeActivityLocked 函數
        return mService.startHomeActivityLocked(mCurrentUser, myReason);
    }

複製代碼

上面代碼先把 home 移動到棧頂,而後經過 AMS startHomeActivityLocked 來啓動 Launcher ,看下面代碼

//com.android.server.am; ActivityManagerService.java

    boolean startHomeActivityLocked(int userId, String reason) {
        /** * 1. 判斷值是否符合要求--> 判斷當前啓動的運行模式是不是低級工廠模式而且 mTopAction 爲空的時候 * * @mFactoryTest: 表明啓動的運行模式:非工廠模式、低級工廠模式、高級工廠模式、 * @mTopAction: 則描述第一個被啓動 Activity 組件的 Action,默認是 Intent.ACTION_MAIN. */

        if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
                && mTopAction == null) {
            return false;
        }
        /** * 2. 建立 Launcher 所須要的 Intent */
        Intent intent = getHomeIntent();
        ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
        if (aInfo != null) {
            intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
          
            aInfo = new ActivityInfo(aInfo);
            aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
            ProcessRecord app = getProcessRecordLocked(aInfo.processName,
                    aInfo.applicationInfo.uid, true);
            /** * 3. 判斷符合 action 爲 Intent.ACTION_MAIN、Category 爲 Intent.CATEGORY_HOME 的應用程序是否已經啓動 */
            if (app == null || app.instr == null) {
                intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
                final int resolvedUserId = UserHandle.getUserId(aInfo.applicationInfo.uid);
                // For ANR debugging to verify if the user activity is the one that actually
                // launched.
                final String myReason = reason + ":" + userId + ":" + resolvedUserId;
                /** * 3.1. 這個被啓動的應用程序就是 Launcher, */
                mActivityStarter.startHomeActivityLocked(intent, aInfo, myReason);
            }
        } else {
            Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
        }

        return true;
    }


複製代碼

上面代碼主要作了一下幾件事:

    1. 判斷 mFactoryTest、mTopAction 是否符合要求--> 判斷當前啓動的運行模式是不是低級工廠模式而且 mTopAction 爲空的時候
    1. 建立 Launcher 所須要的 Intent
    1. 判斷符合 action 爲 Intent.ACTION_MAIN、Category 爲 Intent.CATEGORY_HOME 的應用程序是否已經啓動
  • 3.1 若是沒有啓動調用 ActivityStarter 的 startHomeActivityLocked 函數

接着咱們看下 getHomeIntent() 是怎麼生成的 Launcher 的 Intent

//com.android.server.am; ActivityManagerService.java

    Intent getHomeIntent() {
        //傳入第一個啓動的 action 和 啓動的 mTopData
        Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
      	//傳入組件 ComponentName(app.packageName,ai.name);
        intent.setComponent(mTopComponent);
      	//內部標誌
        intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
      	//設置主頁面
        if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
            intent.addCategory(Intent.CATEGORY_HOME);
        }
        return intent;
    }
複製代碼

其實這裏的 getHomeIntent() 獲取到的就是 Launcher.java 啓動 Intent, 咱們能夠看下它的清單文件 Activity 的註冊

//android-8.0.0_r1/packages/apps/Launcher3/AndroidManifest.xml

<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.launcher3">
    <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="21"/>
   ....
      		//1. 手機啓動的主頁面
           <activity
            android:name="com.android.launcher3.Launcher"
            android:launchMode="singleTask"
            android:clearTaskOnLaunch="true"
            android:stateNotNeeded="true"
            android:windowSoftInputMode="adjustPan|stateUnchanged"
            android:screenOrientation="nosensor"
            android:configChanges="keyboard|keyboardHidden|navigation"
            android:resizeableActivity="true"
            android:resumeWhilePausing="true"
            android:taskAffinity=""
            android:enabled="true">

            <!--2. 設置了 android.intent.action.MAIN 屬性就是主啓動-->
            <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>   
   ...
  
 </manifest>
複製代碼

能夠看到 Launcher 的 內部配置了 android.intent.action.MAIN 屬性,那麼這樣 Launcher 的 Activity 也就成了主 Activity。回到 AMS 的 startHomeActivityLocked 函數中,看註釋 3.1 若是沒有啓動過 Launcher 那麼就執行 ActivityStarter 的 startHomeActivityLocked 函數來啓動 Launcher,看下面代碼:

//com.android.server.am; ActivityStarter.java
    void startHomeActivityLocked(Intent intent, ActivityInfo aInfo, String reason) {
        /** * 1. 將 home 放入 HomeStack 中 */
        mSupervisor.moveHomeStackTaskToTop(reason);
        /** * 2. 會執行到 Launcher 的 onCreate 函數中 */
        mLastHomeActivityStartResult = startActivityLocked(null /*caller*/, intent,
                null /*ephemeralIntent*/, null /*resolvedType*/, aInfo, null /*rInfo*/,
                null /*voiceSession*/, null /*voiceInteractor*/, null /*resultTo*/,
                null /*resultWho*/, 0 /*requestCode*/, 0 /*callingPid*/, 0 /*callingUid*/,
                null /*callingPackage*/, 0 /*realCallingPid*/, 0 /*realCallingUid*/,
                0 /*startFlags*/, null /*options*/, false /*ignoreTargetSecurity*/,
                false /*componentSpecified*/, mLastHomeActivityStartRecord /*outActivity*/,
                null /*container*/, null /*inTask*/, "startHomeActivity: " + reason);
        if (mSupervisor.inResumeTopActivity) {
            mSupervisor.scheduleResumeTopActivities();
        }
    }
複製代碼

在註釋 1 中將 Launcher 放入 HomeStack 中,HomeStack 是在 ActivityStackSupervisor 中定義的用於存儲 Launcher 的變量。接着調用 startActivityLocked 來啓動 Launcher Activity,Activity 啓動咱們後面單獨會寫一篇介紹,這裏不作過多說明。最終進入到 Launcher 的 onCreate 生命週期函數中

// package com.android.launcher3; Launcher.java

public class Launcher extends BaseActivity implements LauncherExterns, View.OnClickListener, OnLongClickListener,LauncherModel.Callbacks, View.OnTouchListener, LauncherProviderChangeListener, AccessibilityManager.AccessibilityStateChangeListener {
                     

  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
      
      ...
      //1. 加載佈局 R.layout.launcher 路徑在 android-8.0.0_r1/packages/apps/Launcher3/res/layout-port/launcher.xml
      mLauncherView = getLayoutInflater().inflate(R.layout.launcher, null);
      ...
      //2. 設置佈局底層解析 xml2View 並添加到 ViewGroup 中
      setContentView(mLauncherView); 
      ...
    }
                     
}
複製代碼

接下來咱們就分析 Launcher 中應用圖標的顯示。

Launcher 應用圖標顯示

上一小節咱們講到了 Launcher 啓動,那麼這一小節就講 Launcher 顯示過程,咱們直接看它的 onCreate 生命週期函數具體實現

//com.android.launcher3; Launcher.java

public class Launcher extends BaseActivity implements LauncherExterns, View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, View.OnTouchListener, LauncherProviderChangeListener, AccessibilityManager.AccessibilityStateChangeListener {
                     
                     
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
				...

        super.onCreate(savedInstanceState);

        /** * 1. 獲取 LauncherAppState 實例 */
        LauncherAppState app = LauncherAppState.getInstance(this);

       
        mDeviceProfile = app.getInvariantDeviceProfile().getDeviceProfile(this);
        if (isInMultiWindowModeCompat()) {
            Display display = getWindowManager().getDefaultDisplay();
            Point mwSize = new Point();
            display.getSize(mwSize);
            mDeviceProfile = mDeviceProfile.getMultiWindowProfile(this, mwSize);
        }

        mSharedPrefs = Utilities.getPrefs(this);
        mIsSafeModeEnabled = getPackageManager().isSafeMode();
        /** * 2. 將 Launcer 與 LauncherAppState 對象綁定 */
        mModel = app.setLauncher(this);
        mModelWriter = mModel.getWriter(mDeviceProfile.isVerticalBarLayout());
        mIconCache = app.getIconCache();
        mAccessibilityDelegate = new LauncherAccessibilityDelegate(this);

        mDragController = new DragController(this);
        mAllAppsController = new AllAppsTransitionController(this);
        mStateTransitionAnimation = new LauncherStateTransitionAnimation(this, mAllAppsController);

        mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);

        mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
        mAppWidgetHost.startListening();

        // If we are getting an onCreate, we can actually preempt onResume and unset mPaused here,
        // this also ensures that any synchronous binding below doesn't re-trigger another
        // LauncherModel load.
        mPaused = false;
				//經過打氣筒解析 xml 佈局轉化成 View 的過程
        mLauncherView = getLayoutInflater().inflate(R.layout.launcher, null);

        setupViews();
        mDeviceProfile.layout(this, false /* notifyListeners */);
        mExtractedColors = new ExtractedColors();
        loadExtractedColorsAndColorItems();

        mPopupDataProvider = new PopupDataProvider(this);

        ((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE))
                .addAccessibilityStateChangeListener(this);

        lockAllApps();

        restoreState(savedInstanceState);

        if (LauncherAppState.PROFILE_STARTUP) {
            Trace.endSection();
        }

        //獲取意外退出 Activity 保存的屬於新興
        if (savedInstanceState != null) {
            currentScreen = savedInstanceState.getInt(RUNTIME_STATE_CURRENT_SCREEN, currentScreen);
        }
        /** * 3.調用 LauncherModel 的 startLoader 函數 */
        if (!mModel.startLoader(currentScreen)) {
            mDragLayer.setAlpha(0);
        } else {
        
           ...
        }

     ...
       	//設置當前 Activity 顯示方向
        setOrientation();
				//設置佈局底層解析 xml2View 並添加到 ViewGroup 中
        setContentView(mLauncherView);
        if (mLauncherCallbacks != null) {
            mLauncherCallbacks.onCreate(savedInstanceState);
        }
    }                    
                                   
}

複製代碼

經過上面代碼咱們能夠知道主要是進行一些對象初始化的綁定,還有解析 xml 文件的過程,咱們先來看 LauncherAppState 的 setLauncher 函數實現:

//com.android.launcher3; LauncherAppState.java

    LauncherModel setLauncher(Launcher launcher) {
        getLocalProvider(mContext).setLauncherProviderChangeListener(launcher);
      	//1. 調用LauncherModel initialize 初始化工做
        mModel.initialize(launcher);
        return mModel;
    }
複製代碼

跟一下 LauncherModel initialize 函數具體實現

//com.android.launcher3; LauncherModel.java
    public void initialize(Callbacks callbacks) {
        synchronized (mLock) {
            Preconditions.assertUIThread();
            // Remove any queued UI runnables
            /** * 1. 取消隊列裏面全部的 Runnable */
            mHandler.cancelAll();
            /** * 2. 使用 弱引用添加 callbacks 避免內存泄漏 */
            mCallbacks = new WeakReference<>(callbacks);
        }
    }
複製代碼

在 initialize 函數中會將 Callbacks ,也就是傳入的 Launcher,封裝成一個弱引用對象。所以咱們得知 mCallBack 的成員變量指的就是封裝成弱引用對象 Launcher,這個 mCallbacks 在 Launcher 的 onCreate 註釋 3 中會用到,如今咱們看註釋 3 的 LauncherModel 調用 startLoader 函數實現:

//com.android.launcher3; LauncherModel.java

public class LauncherModel extends BroadcastReceiver implements LauncherAppsCompat.OnAppsChangedCallbackCompat {
        
  /** * 1. 建立了一個具備消息循環的線程 */
    @Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
    static {
        //啓動
        sWorkerThread.start();
    }

    /** * 2. 向 HanderThread 發送消息 */
    @Thunk static final Handler sWorker = new Handler(sWorkerThread.getLooper());       
        
    //Launcher 的弱引用
    @Thunk WeakReference<Callbacks> mCallbacks;   
    
    ...//省略部分代碼
    
    public boolean startLoader(int synchronousBindPage) {
        InstallShortcutReceiver.enableInstallQueue();
        synchronized (mLock) {
            if (mCallbacks != null && mCallbacks.get() != null) {
                final Callbacks oldCallbacks = mCallbacks.get();
                
                runOnMainThread(new Runnable() {
                    public void run() {
                        oldCallbacks.clearPendingBinds();
                    }
                });

                // If there is already one running, tell it to stop.
                stopLoaderLocked();
                /** * 3. 建立 LoaderTask */
                mLoaderTask = new LoaderTask(mApp.getContext(), synchronousBindPage);
                if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
                        && mModelLoaded && !mIsLoaderTaskRunning) {
                    mLoaderTask.runBindSynchronousPage(synchronousBindPage);
                    return true;
                } else {
                    sWorkerThread.setPriority(Thread.NORM_PRIORITY);
                    /** * 4. 將mLoaderTask 做爲消息發送給 HandlerThread */
                    sWorker.post(mLoaderTask);
                }
            }
        }
        return false;
    }
    
    ...//省略部分代碼
        
}
複製代碼

經過上面代碼跟註釋咱們總結下主要作了哪些事

    1. 在當前 LauncherModel 的成員聲明瞭 HandlerThread 具備消息循環的線程
    1. 在當前 LauncherModel 的成員聲明瞭 Handler 並把 HandlerThread 的 Looper 做爲參數傳遞給了 Handler,那麼 Handler 這裏就是把消息指向了 HandlerThread
    1. 建立 LoaderTask 實例,它實現了 Runnable 接口
    1. 將 LoaderTask 做爲消息發送給了 HandlerThread 的 Looper 來處理,最後 在 LoaderTask 的 run 函數回調

咱們來看下 LoaderTask run 具體實現

private class LoaderTask implements Runnable {
  
...
  
          public void run() {
            synchronized (mLock) {
                if (mStopped) {
                    return;
                }
                mIsLoaderTaskRunning = true;
            }

            try {
                if (DEBUG_LOADERS) Log.d(TAG, "step 1.1: loading workspace");
                // Set to false in bindWorkspace()
                mIsLoadingAndBindingWorkspace = true;
                /** * 1. 加載工做區信息 */
                loadWorkspace();

              ...

                /** * 2. 綁定工做區信息 */
                bindWorkspace(mPageToBindFirst);

								...

                /** * 3. 加載系統以及安裝額應用程序信息 */
                loadAllApps();

                verifyNotStopped();
       					...
            } catch (CancellationException e) {
             
            }
  
							...
  
  
}
複製代碼

Launcher 是工做區的形式來顯示系統安裝的應用程序的快捷圖標的,每個工做區都是用來描述一個抽象桌面的,它有 n 個屏幕組成,每個屏幕又分爲 n 個單元格,每一個單元格用來顯示一個應用程序的快捷圖標。在註釋 1 、2 處分別調用了 bindWorkspace 函數來加載和綁定工做區信息。註釋 3 處的 loadAllApps 函數用來加載系統已經安裝的應用程序信息,咱們來看下注釋 3 的實現

//com.android.launcher3 LauncherModel.java

        private void loadAllApps() {
          ...
            mHandler.post(new Runnable() {
                public void run() {

                    final long bindTime = SystemClock.uptimeMillis();
                    final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
                    if (callbacks != null) {
                        /** * 1. */
                        callbacks.bindAllApplications(added);
                        if (DEBUG_LOADERS) {
                            Log.d(TAG, "bound " + added.size() + " apps in "
                                    + (SystemClock.uptimeMillis() - bindTime) + "ms");
                        }
                    } else {
                        Log.i(TAG, "not binding apps: no Launcher activity");
                    }
                }
            });
           ...
        }
複製代碼

在註釋 1 處會調用 callbacks 的 bindAllApplications 函數,從以前 Launcher 的 onCreate 函數註釋 3 處咱們得知這個 callbacks 實際指向的 Launcher 的。因此咱們回到 Launcher.java 類中看它具體實現 bindAllApplications 作了什麼,以下代碼所示:

//com.android.launcher3; Launcher.java


    /** * 在 LauncherModel 中調用 */
    public void bindAllApplications(final ArrayList<AppInfo> apps) {
        if (waitUntilResume(mBindAllApplicationsRunnable, true)) {
            mTmpAppsList = apps;
            return;
        }

        if (mAppsView != null) {
            /** * 1. 將包含應用信息的列表 apps 傳入 mAppsView 中 */
            mAppsView.setApps(apps);
        }
        if (mLauncherCallbacks != null) {
            mLauncherCallbacks.bindAllApplications(apps);
        }
    }
複製代碼

在註釋 1 處會調用 AllAppsContainerView 的成員變量 mAppsView 的 setApps 函數, 它會把全部 APP 信息傳遞進去。

//com.android.launcher3.allapps; AllAppsContainerView.java
    public void setApps(List<AppInfo> apps) {
        mApps.setApps(apps);
    }
複製代碼

mApps 成員變量是 AlphabeticalAppsList 裏面的函數,接着在跟

//com.android.launcher3.allapps; AlphabeticalAppsList.java
    public void setApps(List<AppInfo> apps) {
        mComponentToAppMap.clear();
      	//1. 添加 APP
        addApps(apps);
    }

    public void addApps(List<AppInfo> apps) {
      	//2. 更新 APP
        updateApps(apps);
    }

    /** * Updates existing apps in the list */
    public void updateApps(List<AppInfo> apps) {
      
        for (AppInfo app : apps) {
            mComponentToAppMap.put(app.toComponentKey(), app);
        }
        onAppsUpdated();
    }

    private void onAppsUpdated() {
       ...//添加邏輯省略
         //更新 Adapter 中的對象數據
        updateAdapterItems();
    }
    private void updateAdapterItems() {
        refillAdapterItems();
        refreshRecyclerView();
    }

    private void refreshRecyclerView() {
        if (mAdapter != null) {
          //刷新 RecyclerView.Adapter 
            mAdapter.notifyDataSetChanged();
        }
    }
複製代碼

刷新 APP 信息咱們找到了,init Adapter 的地方其實就在 AllAppsContainerView 佈局對象加載完成以後的回調 onFinishInflate 中,咱們看下具體實現:

//com.android.launcher3.allapps; AllAppsContainerView.java
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
				....

			//找到主頁面搜索按鈕
        mSearchContainer = findViewById(R.id.search_container);
      	//輸入擴展 View
        mSearchInput = (ExtendedEditText) findViewById(R.id.search_box_input);
        /** * 1. 獲得 AllAppsRecyclerView 繼承的是 RecyclerView */
        mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view);
        /** * 2. 設置 mApps */
        mAppsRecyclerView.setApps(mApps);
        mAppsRecyclerView.setLayoutManager(mLayoutManager);
        /** * 3. 設置加載適配器 */
        mAppsRecyclerView.setAdapter(mAdapter);
        mAppsRecyclerView.setHasFixedSize(true);
        mAppsRecyclerView.addOnScrollListener(mElevationController);
        mAppsRecyclerView.setElevationController(mElevationController);


    }
複製代碼

在註釋 1 處獲得 AllAppsRecyclerView 對象用來顯示 APP 列表,並在註釋 2 處將此前的 mAPPs 設置進去,在註釋 3 處設置 RecyclerView 的 Apdapter 適配器,這樣 APP 就顯示在屏幕上了。

到這裏 Launcher 中應用圖標顯示過程以及 launcher 啓動流程就將完了,下一篇將爲帶來應用程序的啓動流程,尚未關注的小夥伴能夠先關注一波...

總結

這裏我就以一張圖來總結 Launcher 的啓動流程吧。

KshAMt.png

參考

  • 《Android 進階解密》
相關文章
相關標籤/搜索