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 进阶解密》
相关文章
相关标签/搜索