源码分析之Launcher 启动

作者: 不正经的创作者 | 来源:发表于2020-05-09 16:47 被阅读0次

    前言

    我们熟知一般 Android 工程师都是在应用层上开发,不会涉及系统源码,但是如果你想往底层发展,或者深入插件化、Framework 系统层等开发工作,如果不了解 Android 源码可是不行的,那么接下来我基于自己的理解跟学习来记录跟 Android 开发息息相关的源码分析,大概从 Android 中的 SystemServer 启动、四大组件启动、AMS、PMS 等几个维度来介绍,下面是我的计划,当然在未来也有可能改变。

    介绍

    上面一章介绍了 Android 中 SystemServer 启动和一些其它服务的启动,那么系统服务启动完了之后,就该显示 Android 系统桌面了,在显示之前会请求 PMS 返回系统中已经安装的应用程序信息,并将这些信息封装成一个快捷图标列表显示在系统屏幕上,这样用户就可以通过点击桌面上的图标来启动相应的应用程序了,在 Android 中用于显示桌面的 Activity 是 Launcher.java 下面我们就分析
    Launcher 启动过程。

    Launcher 的启动分析

    SystemServer 进程在启动过程中会启动 PMS ,PMS 启动后会将系统中的应用程序安装完成。在此前已经启动的 AMS 会将 Launcher 启动起来。可以看下执行的时序图

    启动 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 为空的时候

    • 2.创建 Launcher 所需要的 Intent

    • 3.判断符合 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 具有消息循环的线程

    • 2.在当前 LauncherModel 的成员声明了 Handler 并把 HandlerThread 的 Looper 作为参数传递给了 Handler,那么 Handler 这里就是把消息指向了 HandlerThread

    • 3.创建 LoaderTask 实例,它实现了 Runnable 接口

    • 4.将 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 的启动流程吧。

    相关文章

      网友评论

        本文标题:源码分析之Launcher 启动

        本文链接:https://www.haomeiwen.com/subject/ggiknhtx.html