Android15 Framework(4):桌面应用 Launcher 解析

6 阅读12分钟

前言

在前面3篇文章

我们看了Init、Zygote、SystemServer进程,本文是Android 15 Framework解析系列的第四篇,一起来看看 Launcher吧。

注意:本文出现的源码基于Android - 15.0.0_r1。另外本文关注主要逻辑,省略部分代码。

一、 Android系统启动流程

本文是介绍Launcher,照例先看下Android系统启动流程: image.png

启动电源及系统启动 -> Bootloader -> Linux内核启动 -> Init -> Zygote -> SystemServer ->  Launcher

二、Launcher

image.png

本文将从Launcher启动流程、页面加载、点击事件介绍Launcher。

先说说Launcher是什么?Launcher是由Android系统的一个桌面应用,它用来显示安装好的应用,比如电话,短信...。它的源码位置是在packages/apps/Launcher3。

那么首先看看Launcher启动流程吧

2.1 Launcher启动流程

Launcher是SystemServer启动的,就从SystemServer#run方法开始看

    // SystemServer#run
    private void run() {
        ...

        // 启动服务
        try {
            t.traceBegin("StartServices");
            // 引导服务,如AMS,DMS
            startBootstrapServices(t);
            // 核心服务,如BatteryService
            startCoreServices(t);
            // 其他服务,如WMS, IMS,这里还会启动 Launcher
            startOtherServices(t);
            ...
        } catch (Throwable ex) {
            Slog.e("System", "******************************************");
            Slog.e("System", "************ Failure starting system services", ex);
            throw ex;
        } finally {
            t.traceEnd(); // StartServices
        }
        ...
    }
    
    private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
        ...
     
        // 调用 AMS#systemReady
        mActivityManagerService.systemReady(() -> {
            ...
        })
        ...
    }
 
    // AMS#systemReady
    public void systemReady(final Runnable goingCallback, @NonNull TimingsTraceAndSlog t) {
        ...

        synchronized (this) {
            ...
            
            // 当前启动的用户是否是 系统用户 (user 0)
            boolean isBootingSystemUser = currentUserId == UserHandle.USER_SYSTEM;
            // 是系统用户 且 不是 Headless System User 模式 (普通 Android 手机正常启动)
            if (isBootingSystemUser && !UserManager.isHeadlessSystemUserMode()) {
                t.traceBegin("startHomeOnAllDisplays");
                // 启动Launcher,调用 LocalServices#startHomeOnAllDisplays
                // ATMS 构造方法构造 LocalServices 对象,start 将此对象存入 LocalServices
                // AMS 构造方法会获取此对象,即LocalServices对象
                mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady");
                t.traceEnd();
            }
            ...
        }
    }
    
    // ATMS的构造方法
    public ActivityTaskManagerService(Context context) {
        ...
        mInternal = new LocalService();
        ...
    }
    
    // ATMS#start
    private void start() {
        LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
    }
    
    // AMS的构造方法
    public ActivityManagerService(Context systemContext, ActivityTaskManagerService atm) {
        ...
        mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
        ...
    }
    
    // LocalService定义在ATMS类中
    final class LocalService extends ActivityTaskManagerInternal {
        ...
        
        @Override
        public boolean startHomeOnAllDisplays(int userId, String reason) {
            synchronized (mGlobalLock) {
                // 调用 RootWindowContainer#startHomeOnAllDisplays
                return mRootWindowContainer.startHomeOnAllDisplays(userId, reason);
            }
        }
        
        ...
    }
    
    // RootWindowContainer#startHomeOnAllDisplays
    boolean startHomeOnAllDisplays(int userId, String reason) {
        boolean homeStarted = false;
        for (int i = getChildCount() - 1; i >= 0; i--) {
            final int displayId = getChildAt(i).mDisplayId;
            homeStarted |= startHomeOnDisplay(userId, reason, displayId);
        }
        return homeStarted;
    }
    
    boolean startHomeOnDisplay(int userId, String reason, int displayId) {
        return startHomeOnDisplay(userId, reason, displayId, false /* allowInstrumenting */,
                false /* fromHomeKey */);
    }

    boolean startHomeOnDisplay(int userId, String reason, int displayId, boolean allowInstrumenting,
            boolean fromHomeKey) {
        // Fallback to top focused display or default display if the displayId is invalid.
        if (displayId == INVALID_DISPLAY) {
            final Task rootTask = getTopDisplayFocusedRootTask();
            displayId = rootTask != null ? rootTask.getDisplayId() : DEFAULT_DISPLAY;
        }

        final DisplayContent display = getDisplayContent(displayId);
        // reduceOnAllTaskDisplayAreas兼容单/多屏, startHomeOnTaskDisplayArea启动Launcher
        return display.reduceOnAllTaskDisplayAreas((taskDisplayArea, result) ->
                        result | startHomeOnTaskDisplayArea(userId, reason, taskDisplayArea,
                                allowInstrumenting, fromHomeKey),
                false /* initValue */);
    }
    
    boolean startHomeOnTaskDisplayArea(int userId, String reason, TaskDisplayArea taskDisplayArea,
            boolean allowInstrumenting, boolean fromHomeKey) {
        ...

        Intent homeIntent = null;
        ActivityInfo aInfo = null;
        // 主屏 (默认显示屏 / 单屏设备) 执行 if 分支
        if (taskDisplayArea == getDefaultTaskDisplayArea()
                || mWmService.shouldPlacePrimaryHomeOnDisplay(
                        taskDisplayArea.getDisplayId(), userId)) {
            // 获取启动 Launcher 的Intent:action是Intent.ACTION_MAIN,category是Intent.CATEGORY_HOME
            homeIntent = mService.getHomeIntent();
            // 获取Launcher 的ActivityInfo 
            // 通过调用 PMS#resolveIntent 去获取 ResolveInfo,它的activityInfo属性就是ActivityInfo
            aInfo = resolveHomeActivity(userId, homeIntent);
        } else if (shouldPlaceSecondaryHomeOnDisplayArea(taskDisplayArea)) {
            ...
        }

        ...
        
        // 设置 Component, 即packageName 和 acitivityInfo.name
        homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
        // 设置 flags 为 _NEW_TASK
        homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
        ...
        
        // 添加额外信息
        homeIntent.putExtra(WindowManagerPolicy.EXTRA_START_REASON, reason);

        // Update the reason for ANR debugging to verify if the user activity is the one that
        // actually launched.
        final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId(
                aInfo.applicationInfo.uid) + ":" + taskDisplayArea.getDisplayId();
                
        // 调用 ActivityStartController#startHomeActivity
        mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
                taskDisplayArea);
        return true;
    }
    
    // ActivityStartController#startHomeActivity
    void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason,
            TaskDisplayArea taskDisplayArea) {
        ...

        // 先调用了各种setter, 最后调用 ActivityStarter#execute 去开启Launcher
        mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
                .setOutActivity(tmpOutRecord)
                .setCallingUid(0)
                .setActivityInfo(aInfo)
                .setActivityOptions(options.toBundle())
                .execute();
        mLastHomeActivityStartRecord = tmpOutRecord[0];
        
        // Launcher是否已经开启了
        if (rootHomeTask.mInResumeTopActivity) {
            // 已经开启,将 Launcher 拉到栈顶
            mSupervisor.scheduleResumeTopActivities();
        }
    }

至此,我们已经分析了Launcher启动的触发时机和前期流程。从 ActivityStarter#execute 开始,Launcher的启动便进入了标准的应用冷启动流程,这包括进程创建、应用初始化、Activity生命周期等阶段。冷启动流程,我将在后续的启动速度优化文章中详细解析。

2.2 Launcher界面加载

看完了Launcher启动流程,我们继续看Launcher界面加载。既然Launcher是一个应用,那么它也会执行生命周期函数,我们就从它的 onCreate 开始看

    // Launcher#onCreate
    protected void onCreate(Bundle savedInstanceState) {
        ...
        
        if (!mModel.addCallbacksAndLoad(this)) {
            if (!internalStateHandled) {
                // If we are not binding synchronously, pause drawing until initial bind complete,
                // so that the system could continue to show the device loading prompt
                mOnInitialBindListener = Boolean.FALSE::booleanValue;
            }
        }

        ...
    }
    
    // LauncherModel#addCallbacksAndLoad
    public boolean addCallbacksAndLoad(@NonNull final Callbacks callbacks) {
        synchronized (mLock) {
            // 这里往 addCallbacksAndLoad 添加了 callbacks
            addCallbacks(callbacks);
            // 调用 startLoader 加载页面
            return startLoader(new Callbacks[] { callbacks });

        }
    }
    
    // 参数中的 callbacks 是 Launcher 自己
    public void addCallbacks(@NonNull final Callbacks callbacks) {
        Preconditions.assertUIThread();
        synchronized (mCallbacksList) {
            mCallbacksList.add(callbacks);
        }
    }
    
    public boolean startLoader() {
        return startLoader(new Callbacks[0]);
    }

    private boolean startLoader(@NonNull final Callbacks[] newCallbacks) {
        // Enable queue before starting loader. It will get disabled in Launcher#finishBindingItems
        ItemInstallQueue.INSTANCE.get(mApp.getContext())
                .pauseModelPush(ItemInstallQueue.FLAG_LOADER_RUNNING);
        synchronized (mLock) {
            // 是否有LoaderTask正在运行:首次执行时, 没有Task运行, 返回false
            boolean wasRunning = stopLoader();
            // 是否已经加载过并且Task正在执行:首次执行,mModelLoaded 和 mIsLoaderTaskRunning 均为false
            boolean bindDirectly = mModelLoaded && !mIsLoaderTaskRunning;
            // bindDirectly 为 false,所以bindAllCallbacks 为 true
            boolean bindAllCallbacks = wasRunning || !bindDirectly || newCallbacks.length == 0;
            // 在 addCallbacksAndLoad 中调用了 addCallbacks,getCallbacks不会返回空
            final Callbacks[] callbacksList = bindAllCallbacks ? getCallbacks() : newCallbacks;

            // 这里的 callbacksList 里面只有 Launcher 自己
            BaseLauncherBinder launcherBinder = new BaseLauncherBinder(
                        mApp, mBgDataModel, mBgAllAppsList, callbacksList);
            // 不返回空,执行此处
            if (callbacksList.length > 0) {
                ...
                // bindDirectly 为 false, 不执行if分支
                if (bindDirectly) {
                    ...
                    return true;
                } else {
                    stopLoader();
                    // 构造 LoaderTask 对象, 且 LoaderTask 实现了 Runnable 接口
                    mLoaderTask = new LoaderTask(
                            mApp, mBgAllAppsList, mBgDataModel, mModelDelegate, launcherBinder);
                    // MODEL_EXECUTOR 其实是一个Handler
                    MODEL_EXECUTOR.post(mLoaderTask);
                }
            }
        }
        return false;
    }
    
    public Callbacks[] getCallbacks() {
        synchronized (mCallbacksList) {
            return mCallbacksList.toArray(new Callbacks[mCallbacksList.size()]);
        }
    }
    
    // Executors.MODEL_EXECUTOR
    public static final LooperExecutor MODEL_EXECUTOR =
            new LooperExecutor(createAndStartNewLooper("launcher-loader"));
        
    // LooperExecutor的构造方法
    public LooperExecutor(Looper looper) {
        mHandler = new Handler(looper);
    }
    
    public Handler getHandler() {
        return mHandler;
    }
    
    public void post(Runnable runnable) {
        getHandler().post(runnable);
    }
    
    // LoaderTask的构造方法
    LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel,
            ModelDelegate modelDelegate, @NonNull BaseLauncherBinder launcherBinder,
            UserManagerState userManagerState) {
        ...
        
        // mApp.getContext()是返回了 Context 对象,而 Context 的实现类是 ContextImp
        mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class);
        ...
    }
    
    
    // ContextImp#getSystemService
    public Object getSystemService(String name) {
        ...
        return SystemServiceRegistry.getSystemService(this, name);
    }
    
// SystemServiceRegistry
public final class SystemServiceRegistry {
    public static boolean sEnableServiceNotFoundWtf = false;
    ...
    
    // SystemServer#run 调用了 SystemServiceRegistry.sEnableServiceNotFoundWtf = true, 会执行此静态块
    static {
        ...
        registerService(Context.LAUNCHER_APPS_SERVICE, LauncherApps.class,
                new CachedServiceFetcher<LauncherApps>() {
            @Override
            public LauncherApps createService(ContextImpl ctx) {
                return new LauncherApps(ctx);
            }});
        ...
    }
        
    private static <T> void registerService(@NonNull String serviceName,
            @NonNull Class<T> serviceClass, @NonNull ServiceFetcher<T> serviceFetcher) {
        SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
        SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
        SYSTEM_SERVICE_CLASS_NAMES.put(serviceName, serviceClass.getSimpleName());
    }
    
    public static Object getSystemService(@NonNull ContextImpl ctx, String name) {
        // 先获取到 ServiceFetcher
        final ServiceFetcher<?> fetcher = getSystemServiceFetcher(name);
        if (fetcher == null) {
            return null;
        }
        
        // 再调用 ServiceFetcher.getService
        final Object ret = fetcher.getService(ctx);
        ...
        return ret;
    }
    
    private static ServiceFetcher<?> getSystemServiceFetcher(String name) {
        if (name == null) {
            return null;
        }
        final ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
        if (fetcher == null) {
            if (sEnableServiceNotFoundWtf) {
                Slog.wtf(TAG, "Unknown manager requested: " + name);
            }
            return null;
        }
        return fetcher;
    }
    ...
}  

    // CachedServiceFetcher
    public final T getService(ContextImpl ctx) {
        ...
        
        // 这个 createService 就是 registerService 传的最后一个参数里重写方法
        // LauncherApps 这里就是调用了构造方法 return new LauncherApps(ctx);
        service = createService(ctx);
        ret = service;
        ...
        return ret;
    }

    // LauncherApps
    public LauncherApps(Context context, ILauncherApps service) {
        mContext = context;
        mService = service;
        mPm = context.getPackageManager();
        mUserManager = context.getSystemService(UserManager.class);
    }

    public LauncherApps(Context context) {
        // LauncherApps 中的 mService 其实是 LauncherAppsService BinderProxy
        this(context, ILauncherApps.Stub.asInterface(
                ServiceManager.getService(Context.LAUNCHER_APPS_SERVICE)));
    }

首先SystemServer#run 调用了 SystemServiceRegistry.sEnableServiceNotFoundWtf = true, 从而调用了 SystemServiceRegistry的 静态块, 静态块去调用了 registerService(Context.LAUNCHER_APPS_SERVICE, LauncherApps.class...), 将 CachedServiceFetcher<LauncherApps> 对象,放到了SYSTEM_SERVICE_FETCHERS中。

后续执行 LauncherModel#startLoader方法,调用了 LoaderTask的构造方法,它里面调用了 mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class), 跑到了ContentImp#getSystemService, 继续调用 SystemServiceRegistry#getSystemService,而在此方法中,先通过 getSystemServiceFetcher方法从 SYSTEM_SERVICE_FETCHERS 中将 CachedServiceFetcher<LauncherApps> 对象 取了出来, 然后再调用了 CachedServiceFetcher#getService, 在这个 getService 里面调用了 LauncherApps, 进一步就调用到了 LauncherApps的构造方法, 构造方法中去赋值了 LauncherApps 的成员变量 mService 为 LauncherAppsService BinderProxy。

这里稍稍有一点绕,其实就是先往 SYSTEM_SERVICE_FETCHERS 中塞东西,后面去拿出来,最终就调用到了 LauncherApps的构造方法里面。之所以要解释这么多,是因为后续调用涉及到 LauncherApps 里的 mService。

看完了LoadTask 的构造方法,我们看看MODEL_EXECUTOR.post。

在 Launcher#onCreate 中,通过调用 addCallbacksAndLoad 方法,执行了 MODEL_EXECUTOR.post(mLoaderTask), 而 MODEL_EXECUTOR 是 LooperExecutor对象,在它里面有一个 Handler对象成员变成,而MODEL_EXECUTOR#post其实就是调用了 LooperExecutor 里的 Handler 对象的 post。因此我们后续就去看 LoaderTask#run方法

    // LoaderTask#run
    public void run() {
        ...
        try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {

            List<ShortcutInfo> allShortcuts = new ArrayList<>();
            // 加载工作区
            loadWorkspace(allShortcuts, "", memoryLogger, restoreEventLogger);

            ...

            verifyNotStopped();
            // 绑定工作区
            mLauncherBinder.bindWorkspace(true /* incrementBindId */, /* isBindSync= */ false);
            ...
            
            try {
                // 加载所有的apps,重点看apps相关,其余感兴趣的可以自己看下
                allActivityList = loadAllApps();
            } finally {
                Trace.endSection();
            }
            ...
            
            // 绑定所有的apps
            mLauncherBinder.bindAllApps();
            
            // 还会加载快捷方式、小部件等增强功能
            ...
        } catch (CancellationException e) {
            // Loader stopped, ignore
            logASplit("Cancelled");
        } catch (Exception e) {
            memoryLogger.printLogs();
            throw e;
        }
        TraceHelper.INSTANCE.endSection();
    }

    private List<LauncherActivityInfo> loadAllApps() {
        final List<UserHandle> profiles = mUserCache.getUserProfiles();
        ...
        for (UserHandle user : profiles) {
            // 获取所有的app信息,
            // mLauncherApps初始化是在
            final List<LauncherActivityInfo> apps = mLauncherApps.getActivityList(null, user);
            ...
           
            allActivityList.addAll(apps);
        }


        ...
        return allActivityList;
    }
    
    // LauncherApp#getActivityList
    public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
        logErrorForInvalidProfileAccess(user);
        try {
            // mService 就是上面说的 LauncherAppsService BinderProxy。
            // 这里 Binder 通信,我们直接去 Server 端看看做了什么
            return convertToActivityList(mService.getLauncherActivities(mContext.getPackageName(),
                    packageName, user), user);
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }
    }
    
    // 涉及 Binder 通信,因为数据可能较大,所以使用了 ParceledListSlice
    // client 使用此方法 将其转换为普通的 List
    private List<LauncherActivityInfo> convertToActivityList(
            @Nullable ParceledListSlice<LauncherActivityInfoInternal> internals, UserHandle user) {
        if (internals == null || internals.getList().isEmpty()) {
            return Collections.EMPTY_LIST;
        }
        ArrayList<LauncherActivityInfo> lais = new ArrayList<>();
        for (LauncherActivityInfoInternal internal : internals.getList()) {
            LauncherActivityInfo lai = new LauncherActivityInfo(mContext, internal);
            if (DEBUG) {
                Log.v(TAG, "Returning activity for profile " + user + " : "
                        + lai.getComponentName());
            }
            lais.add(lai);
        }
        return lais;
    }
    
    // LauncherAppsService
    public ParceledListSlice<LauncherActivityInfoInternal> getLauncherActivities(
                String callingPackage, @Nullable String packageName, UserHandle user)
                throws RemoteException {
            ...
            try {
                ...
                
                // 调用 PMS BinderProxy 去获取所有安装的应用和信息
                final List<ApplicationInfo> installedPackages =
                        mPackageManagerInternal.getInstalledApplications(
                                /* flags= */ 0, user.getIdentifier(), callingUid);
                for (ApplicationInfo applicationInfo : installedPackages) {
                    if (!visiblePackages.contains(applicationInfo.packageName)) {
                        if (!shouldShowSyntheticActivity(user, applicationInfo)) {
                            continue;
                        }
                        LauncherActivityInfoInternal info =
                                getHiddenAppActivityInfo(
                                        applicationInfo.packageName, callingUid, user);
                        if (info != null) {
                            result.add(info);
                        }
                    }
                }
                return new ParceledListSlice<>(result);
            } finally {
                injectRestoreCallingIdentity(ident);
            }
        }

后续就是调用 PMS 去获取所有安装的应用信息,这里就先不跟了,本文毕竟是讲 Launcher, 等后续写 PMS 文章时,再来填这个坑吧。

现在我们拿到安装的应用信息,后面就去绑定所有的apps

    // BaseLauncherBinder#bindAllApps
    public void bindAllApps() {
        // shallow copy
        AppInfo[] apps = mBgAllAppsList.copyData();
        int flags = mBgAllAppsList.getFlags();
        Map<PackageUserKey, Integer> packageUserKeytoUidMap = Arrays.stream(apps).collect(
                Collectors.toMap(
                        appInfo -> new PackageUserKey(appInfo.componentName.getPackageName(),
                                appInfo.user), appInfo -> appInfo.uid, (a, b) -> a));
        // mUiExecutor = MAIN_EXECUTOR, 就是 new LooperExecutor(Looper.getMainLooper())
        executeCallbacksTask(c -> c.bindAllApplications(apps, flags, packageUserKeytoUidMap),
                mUiExecutor);
    }
    
    protected void executeCallbacksTask(CallbackTask task, Executor executor) {
        // 把绑定apps的操作放到主线程中
        executor.execute(() -> {
            if (mMyBindingId != mBgDataModel.lastBindId) {
                Log.d(TAG, "Too many consecutive reloads, skipping obsolete data-bind");
                return;
            }
            // mCallbacksList 当前只有 Launcher 本身,也就跑到了 Launcher#bindAllApplications
            // mCallbacksList 赋值在构造方法,构造方法则是在 LauncherModel#startLoader中调用的
            for (Callbacks cb : mCallbacksList) {
                task.execute(cb);
            }
        });
    }
    
    // Launcher#bindAllApplications
    public void bindAllApplications(AppInfo[] apps, int flags,
            Map<PackageUserKey, Integer> packageUserKeytoUidMap) {
        mModelCallbacks.bindAllApplications(apps, flags, packageUserKeytoUidMap);
        ...
    }
    
    // ModelCallbacks#bindAllApplications
    override fun bindAllApplications(
        apps: Array<AppInfo>,
        flags: Int,
        packageUserKeytoUidMap: Map<PackageUserKey, Int>,
    ) {
        Preconditions.assertUIThread()
        val hadWorkApps = launcher.appsView.shouldShowTabs()
        // setApps方法会更新应用列表数据,调用notifyUpdate通知观察者更新UI
        launcher.activityComponent.appsStore.setApps(apps, flags, packageUserKeytoUidMap)
        ...
    }

后续具体是怎么更新应用列表的,此处就不再跟进了,感兴趣可以自己跟进看看

2.3 Launcher点击事件

看完了Launcher的启动流程和页面加载流程,我们最后看看Launcher的点击事件是怎么设置的吧。

这还得从 Launcher#onCreate 说起

    // Launcher#onCreate
    protected void onCreate(Bundle savedInstanceState) {
        ...
        setupViews();
        ...
    }

    protected void setupViews() {
        mStartupLatencyLogger.logStart(LAUNCHER_LATENCY_STARTUP_VIEW_INFLATION);
        inflateRootView(R.layout.launcher);
        ...
    }

在setupViews 加载了 launcher 布局文件,继续往后跟

// launcher.xml
<com.android.launcher3.LauncherRootView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:launcher="http://schemas.android.com/apk/res-auto"
    android:id="@+id/launcher"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">
    ...

    <include
        android:id="@+id/apps_view"
        layout="@layout/all_apps"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    ...
</com.android.launcher3.LauncherRootView>

// all_apps.xml
<com.android.launcher3.allapps.LauncherAllAppsContainerView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/apps_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipChildren="true"
    android:clipToPadding="false"
    android:focusable="false"
    android:saveEnabled="false" />

Launcher#onCreate 中调用了 setupViews,而它去加载了 launcher.xml。在 launcher.xml 中使用了 all_apps.xml, all_apps.xml 使用了LauncherAllAppsContainerView,我们继续往下看看 LauncherAllAppsContainerView 的构造方法。

public class LauncherAllAppsContainerView extends ActivityAllAppsContainerView<Launcher> {

    public LauncherAllAppsContainerView(Context context) {
        this(context, null);
    }

    public LauncherAllAppsContainerView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LauncherAllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
        // LauncherAllAppsContainerView的构造方法都会去调用父类的构造方法
        super(context, attrs, defStyleAttr);
    }
    
    ...
}

    // ActivityAllAppsContainerView 构造方法
    public ActivityAllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
        mActivityContext = ActivityContext.lookupContext(context);

        ...
        initContent();

        ...
    }
    
    protected void initContent() {
        mMainAdapterProvider = mSearchUiDelegate.createMainAdapterProvider();
        
        // app list
        mAH.set(AdapterHolder.MAIN, new AdapterHolder(AdapterHolder.MAIN,
                new AlphabeticalAppsList<>(mActivityContext,
                        mAllAppsStore,
                        null,
                        mPrivateProfileManager)));
        mAH.set(AdapterHolder.WORK, new AdapterHolder(AdapterHolder.WORK,
                new AlphabeticalAppsList<>(mActivityContext, mAllAppsStore, mWorkManager, null)));
        mAH.set(SEARCH, new AdapterHolder(SEARCH,
                new AlphabeticalAppsList<>(mActivityContext, null, null, null)));

        ...
    }
    
    public class AdapterHolder {
        ...
        AdapterHolder(int type, AlphabeticalAppsList<T> appsList) {
            mType = type;
            mAppsList = appsList;
            // 这里去创建了 Adapter
            mAdapter = createAdapter(mAppsList);
            mAppsList.setAdapter(mAdapter);
            mLayoutManager = mAdapter.getLayoutManager();
        }
        ...
   }
   
    // ActivityAllAppsContainerView#createAdapter
    protected BaseAllAppsAdapter<T> createAdapter(AlphabeticalAppsList<T> appsList) {
        return new AllAppsGridAdapter<>(mActivityContext, getLayoutInflater(), appsList,
                mMainAdapterProvider);
    }

// AllAppsGridAdapter 没有重写 onCreateViewHolder,去它的父类 BaseAllAppsAdapter里找
public class AllAppsGridAdapter<T extends Context & ActivityContext> extends
        BaseAllAppsAdapter<T> {
    ...
}

// BaseAllAppsAdapter
public abstract class BaseAllAppsAdapter<T extends Context & ActivityContext> extends
        RecyclerView.Adapter<BaseAllAppsAdapter.ViewHolder> {
        
    ...

    public BaseAllAppsAdapter(T activityContext, LayoutInflater inflater,
            AlphabeticalAppsList<T> apps, SearchAdapterProvider<?> adapterProvider) {
        mActivityContext = activityContext;
        mApps = apps;
        mLayoutInflater = inflater;

        // mOnIconClickListener 赋值在这里
        mOnIconClickListener = mActivityContext.getItemOnClickListener();
        mOnIconLongClickListener = mActivityContext.getAllAppsItemLongClickListener();

        mAdapterProvider = adapterProvider;
    }

    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        switch (viewType) {
            case VIEW_TYPE_ICON:
                int layout = (Flags.enableTwolineToggle()
                        && LauncherPrefs.ENABLE_TWOLINE_ALLAPPS_TOGGLE.get(
                                mActivityContext.getApplicationContext()))
                        ? R.layout.all_apps_icon_twoline : R.layout.all_apps_icon;
                BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
                        layout, parent, false);
                icon.setLongPressTimeoutFactor(1f);
                icon.setOnFocusChangeListener(mIconFocusListener);
                // 设置应用图标点击事件
                icon.setOnClickListener(mOnIconClickListener);
                icon.setOnLongClickListener(mOnIconLongClickListener);
                // Ensure the all apps icon height matches the workspace icons in portrait mode.
                icon.getLayoutParams().height =
                        mActivityContext.getDeviceProfile().allAppsCellHeightPx;
                return new ViewHolder(icon);
            ...
        }
    }
}

我们目前跟踪到了 BaseAllAppsAdapter#onCreateViewHolder中,看到是通过 icon.setOnClickListener(mOnIconClickListener) 设置了应用图标点击事件,而 mOnIconClickListener 又是通过 mActivityContext.getItemOnClickListener 赋值给它的,所以现在就要看看这里的 mActivityContext 是什么

还记得 ActivityAllAppsContainerView 构造方法吗,我们前面只讲了 initContent,但没有说 ActivityContext.lookupContext(context)

    // ActivityAllAppsContainerView 构造方法
    public ActivityAllAppsContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
        // 这里的 context 就是 Launcher
        // ActivityAllAppsContainerView 中的 mActivityContext 后续会传给 BaseAllAppsAdapter
        mActivityContext = ActivityContext.lookupContext(context);

        ...
        initContent();

        ...
    }
    
    // ActivityContext#lookupContext
    static <T extends Context & ActivityContext> T lookupContext(Context context) {
        // 调用 lookupContextNoThrow
        T activityContext = lookupContextNoThrow(context);
        if (activityContext == null) {
            throw new IllegalArgumentException("Cannot find ActivityContext in parent tree");
        }
        return activityContext;
    }

    // ActivityContext#lookupContextNoThrow
    static <T extends Context & ActivityContext> T lookupContextNoThrow(Context context) {
        if (context instanceof ActivityContext) {
            return (T) context;
        } else if (context instanceof ActivityContextDelegate acd) {
            return (T) acd.mDelegate;
        } else if (context instanceof ContextWrapper) {
            return lookupContextNoThrow(((ContextWrapper) context).getBaseContext());
        } else {
            return null;
        }
    }

到这,我们就找到了,在 ActivityContext#lookupContextNoThrow 中,如果是 ActivityContext,就转类型,那么我们看看 Launcher 的父类

public class Launcher extends StatefulActivity<LauncherState> ... {}
public abstract class StatefulActivity<STATE_TYPE extends BaseState<STATE_TYPE>>
        extends BaseDraggingActivity ...{}
public abstract class BaseDraggingActivity extends BaseActivity ...{}
public abstract class BaseActivity extends Activity implements ActivityContext {}

// 从上面可以得出
Launcher
  extends StatefulActivity<…>
    extends BaseDraggingActivity
        extends BaseActivity
          implements ActivityContext

原来,Launcher 也是 ActivityContext的子类,那么 ActivityContext#lookupContextNoThrow 中,context instanceof ActivityContext 就为true, 所以 ActivityAllAppsContainerView 构造方法中的 ActivityContext.lookupContext(context) 就是 Launcher 自己,那么调用它的 getItemOnClickListener,但Launcher 并没有重写 getItemOnClickListener,因此会查找父类,在BaseDraggingActivity 中找到了此方法的实现,我们继续往后跟

    // BaseDraggingActivity#getItemOnClickListener
    public View.OnClickListener getItemOnClickListener() {
        return ItemClickHandler.INSTANCE;
    }
 
public class ItemClickHandler {
    public static final OnClickListener INSTANCE = ItemClickHandler::onClick;
    
    private static void onClick(View v) {
        ...

        Object tag = v.getTag();
        // 根据Tag, 调用不同的方法
        if (tag instanceof WorkspaceItemInfo) {
            onClickAppShortcut(v, (WorkspaceItemInfo) tag, launcher);
        } else if (tag instanceof FolderInfo) {
            onClickFolderIcon(v);
        } else if (tag instanceof AppPairInfo) {
            onClickAppPairIcon(v);
        } else if (tag instanceof AppInfo) { // 点击 App 图标
            startAppShortcutOrInfoActivity(v, (AppInfo) tag, launcher);
        } else if (tag instanceof LauncherAppWidgetInfo) {
            ...
        } else if (tag instanceof ItemClickProxy) {
            ((ItemClickProxy) tag).onItemClicked(v);
        } else if (tag instanceof PendingAddShortcutInfo) {
            CharSequence msg = Utilities.wrapForTts(
                    launcher.getText(R.string.long_press_shortcut_to_add),
                    launcher.getString(R.string.long_accessible_way_to_add_shortcut));
            Snackbar.show(launcher, msg, null);
        } else if (tag instanceof PendingAddWidgetInfo) {
            ...
            CharSequence msg = Utilities.wrapForTts(
                    launcher.getText(R.string.long_press_widget_to_add),
                    launcher.getString(R.string.long_accessible_way_to_add));
            Snackbar.show(launcher, msg, null);
        }
    }
    
    ...
}    

Launcher 点击事件流程最后是到了 ItemClickHandler#onClick 中,根据不同Tag, 调用不同方法,比如说点击 shortcut, 会调用 onClickAppShortcut,这里就不每一个往后跟了,冷启动后续流程会在 启动速度优化 文章中详细写调用链。

三、小结与思考

本文主要介绍了 Launcher 启动流程,Launcher 页面加载流程,以及 Launcher 点击事件流程。

最后想说点个人思考,在AI时代,还有必要看源码吗?毕竟大部分问AI都可以知道,但我觉得仍然是有必要的。首先我们自己不去阅读源码,怎么判断AI的回答是对还是错呢;其次阅读源码是提升自己能力的重要方式,可以从中学习到一些优秀的设计模式和思想;最后通过阅读源码,我们可以加深对知识的理解,那么改bug, 做优化,我们就知道从哪里下手。比如优化Android冷启动速度,如果我们都不知道冷启动流程是什么,又何谈去优化它呢。

感谢阅读,希望本文对你有所帮助,如有任何不对的地方,欢迎大家指正