美文网首页
Android源码指南之Launcher剖析

Android源码指南之Launcher剖析

作者: 码上就说 | 来源:发表于2018-10-12 19:06 被阅读182次

    上一篇文章《Android启动时应用程序解析与安装》主要介绍了如何解析一个apk,文章结尾留下来一个问题:打开手机,主界面有很多icon所代表应用程序,点击应用程序就会调起一个应用,这里就是使用Launcher来整合的。Launcher是一个应用程序,本来就介绍Launcher的基本功能以及工作流程。

    一、Launcher介绍

    Android中管理着主屏幕上app shortcut的是一个应用程序,我们称之为Launcher app,通过点击放在屏幕上的app shortcut,可以启动相应的应用程序,启动目标app。
    摊开Android源码,可以参考《Android源码下载与编译》来自己下载源码编译玩玩,更/home/jeffmony/xiaomi/dipper-p/out/target/common/obj/APPS/MiuiDaemon_intermediates/classes.jar好地体会源码的精神。

    1.1 Launcher app介绍

    Android源码中包含一些系统原生的app,而launcher app就是必不可少的原生app,下载最新的Android源码,找到packages/apps/目录下面,发现有Launcher2与Launcher3两个工程,这是两个不同的版本,Launcher3是新版本。Launcher3的源码工程目录如下:


    Launcher app工程目录.png

    发现其中有Android.mk,Android源码的整个构建都是通过make来串联整个工程,每个单独的工程或者单独的jar包下面都有一个makefile文件,每个makefile指定一个module-name来标识当前的make target-name,当前的LOCAL_PACKAGE_NAME := Launcher3

    1.2 编译Launcher app

    我们编译的指令是:

    • source build/envsetup.sh
      初始化当前的编译环境,export必要的变量。
    • lunch aosp_arm-eng
      编译源码的情况下建议选择这个,如果是x86架构的话,要选择x86,这步主要是选择支持的编译架构。
    • make Launcher3 -j4
      编译结束后,显示下面的结果:
      [100% 2305/2305] Install: out/target/product/generic/system/priv-app/Launcher3/Launcher3.apk
      表明当前的apk编译成功。
    • adb install out/target/product/generic/system/priv-app/Launcher3/Launcher3.apk
      直接安装apk

    安装了Launcher之后,打开手机设置-->桌面与最近任务-->默认桌面,显示如下:
    下面的Launcher3就是我们刚刚安装进去的。


    二、Launcher启动流程

    Launcher启动流程一.jpg

    这是只是Launcher启动过程中的小部分,我们首先要搞清楚来龙去脉,再谈一下Launcher内部的关系。

    • 系统启动的时候,Zygote进程fork出System Server进程,就是执行到了SystemServer.java中
    • system server进程会首先启动AMS和PMS以及其他的系统service,AMS是管理组件的服务,PMS是解析本地文件的服务,单单从应用角度来讲,Launcher是其他的应用一样,也是packages/apps/下面的一个apk
    • PMS解析本地的应用,具体是怎么解析的,可以参考我上一篇文章《Android启动时应用程序解析与安装》,解析完了之后,应用相关的数据信息会保存起来。
    • 这是后会启动systemManager管理的一个服务LauncherAppsService.java,下面会简单描述一下这个Service,这个service主要是协助Launcher管理本地的package的。
    • AMS触发systemReady(...)方法,其中执行了startHomeActivity(...),这个方法最终会启动Launcher app,为什么会调用到Launcher app,这个后续会讲解。

    2.1 LaunchAppsService剖析

    2.1.1 启动介绍
    traceBeginAndSlog("StartLauncherAppsService");
    mSystemServiceManager.startService(LauncherAppsService.class);
    traceEnd();
    

    在SystemServer->startOtherServices()中执行,mSystemServiceManager是SystemServiceManager.java的实例,这是管理service的一个类,此service不同于彼service,AMS是远程管理的service,跨进程调用到AMS,但是SystemServiceManager中管理的service只是一个本地反射的中枢,在这里会触发反射方法,这儿起名service,还是会和跨进程的service的混淆的,大家注意即可。

    2.1.2 功能介绍
    • 实例化LauncherAppsImpl类
    • LauncherAppsImpl构造函数中执行的重要的一部,mShortcutServiceInternal.addListener(mPackageMonitor);mPackageMonitor是MyPackageMonitor的实例,见下面的分析。
    private class MyPackageMonitor extends PackageMonitor implements ShortcutChangeListener {
    }
    
    public interface ShortcutChangeListener {
            void onShortcutChanged(@NonNull String packageName, @UserIdInt int userId);
    }
    

    这个接口函数一看就明白了,就是桌面Launcher图标发生改变的时候触发的方法,父类PackageMonitor如下:

    public abstract class PackageMonitor extends android.content.BroadcastReceiver {
        static {
                sPackageFilt.addAction(Intent.ACTION_PACKAGE_ADDED);
                sPackageFilt.addAction(Intent.ACTION_PACKAGE_REMOVED);
                sPackageFilt.addAction(Intent.ACTION_PACKAGE_CHANGED);
                sPackageFilt.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
                sPackageFilt.addAction(Intent.ACTION_PACKAGE_RESTARTED);
                sPackageFilt.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
                sPackageFilt.addDataScheme("package");
                sNonDataFilt.addAction(Intent.ACTION_UID_REMOVED);
                sNonDataFilt.addAction(Intent.ACTION_USER_STOPPED);
                sNonDataFilt.addAction(Intent.ACTION_PACKAGES_SUSPENDED);
                sNonDataFilt.addAction(Intent.ACTION_PACKAGES_UNSUSPENDED);
                sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
                sExternalFilt.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
        }
    
        public void onPackageRemoved(String packageName, int uid) {
        }
    
        public void onPackageAdded(String packageName, int uid) {
        }
    
    //......
    }
    

    还有很对方法没有放上去,这个父类是一个广播,而且还有这些方法,这个类的主要作用是通过广播来监听本地的package情况。如果发生改变就会触发它的内部方法。
    通过广播来实现对package安装、删除、更新等等操作的管理。

    2.2 如何启动Launcher

    这是从ActivityManagerService->systemReady(...)中触发的,抛弃其他的部分,我们只关注启动的部分。这个方法触发了startHomeActivityLocked(...)方法

    boolean startHomeActivityLocked(int userId, String reason) {
            if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
                    && mTopAction == null) {
                return false;
            }
            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);
                if (app == null || app.instr == null) {
                    intent.setFlags(intent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
                    final int resolvedUserId = UserHandle.getUserId(aInfo.applicationInfo.uid);
                    final String myReason = reason + ":" + userId + ":" + resolvedUserId;
                    mActivityStartController.startHomeActivity(intent, aInfo, myReason);
                }
            } else {
                Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
            }
            return true;
        }
    

    getHomeIntent()方法中有设置Intent的过程。

    Intent getHomeIntent() {
            Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
            intent.setComponent(mTopComponent);
            intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
            if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
                intent.addCategory(Intent.CATEGORY_HOME);
            }
            return intent;
        }
    

    这是的String mTopAction = Intent.ACTION_MAIN;mTopComponent描述系统中第一个被启动的Activity组件,这个组件就是我们Launcher中的Main activity,后续的调用是设置ActivityStack的过程,当前的ActivityStack是为空的,因为之前没有启动过Activity,所以在看到后续的调度时发现:

     boolean resumeFocusedStackTopActivityLocked() {
            return resumeFocusedStackTopActivityLocked(null, null, null);
        }
    boolean resumeFocusedStackTopActivityLocked(
                ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
    
            if (!readyToResume()) {
                return false;
            }
    
            if (targetStack != null && isFocusedStack(targetStack)) {
                return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
            }
    
            final ActivityRecord r = mFocusedStack.topRunningActivityLocked();
            if (r == null || !r.isState(RESUMED)) {
                mFocusedStack.resumeTopActivityUncheckedLocked(null, null);
            } else if (r.isState(RESUMED)) {
                // Kick off any lingering app transitions form the MoveTaskToFront operation.
                mFocusedStack.executeAppTransition(targetOptions);
            }
    
            return false;
        }
    
    

    这是的调度的时候,这个的targetStack为null,ActivityRecord为null,说明当前启动的是第一个Activity。再到Launcher app模块中,看看AndroidManifest.xml中设置。

    <application
            android:backupAgent="com.android.launcher3.LauncherBackupAgent"
            android:fullBackupOnly="true"
            android:fullBackupContent="@xml/backupscheme"
            android:hardwareAccelerated="true"
            android:icon="@drawable/ic_launcher_home"
            android:label="@string/derived_app_name"
            android:theme="@style/LauncherTheme"
            android:largeHeap="@bool/config_largeHeap"
            android:restoreAnyVersion="true"
            android:supportsRtl="true" >
    
            <!--
            Main launcher activity. When extending only change the name, and keep all the
            attributes and intent filters the same
            -->
            <activity
                android:name="com.android.launcher3.Launcher"
                android:launchMode="singleTask"
                android:clearTaskOnLaunch="true"
                android:stateNotNeeded="true"
                android:windowSoftInputMode="adjustPan"
                android:screenOrientation="unspecified"
                android:configChanges="keyboard|keyboardHidden|mcc|mnc|navigation|orientation|screenSize|screenLayout|smallestScreenSize"
                android:resizeableActivity="true"
                android:resumeWhilePausing="true"
                android:taskAffinity=""
                android:enabled="true">
                <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"/>
                    <category android:name="android.intent.category.LAUNCHER_APP" />
                </intent-filter>
            </activity>
    //......
    </application>
    

    启动的是Launcher.java

    2.3 Launcher app启动流程

    • Launcher.java中的onCreate中执行
      LauncherAppState app = LauncherAppState.getInstance(this);
      mModel = app.setLauncher(this);
      mModel.startLoader(currentScreen)
    • LauncherModel.startLoader(...)
    public boolean startLoader(int synchronousBindPage) {
            InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_LOADER_RUNNING);
            synchronized (mLock) {
                if (mCallbacks != null && mCallbacks.get() != null) {
                    final Callbacks oldCallbacks = mCallbacks.get();
                    mUiExecutor.execute(oldCallbacks::clearPendingBinds);
                    stopLoader();
                    LoaderResults loaderResults = new LoaderResults(mApp, sBgDataModel,
                            mBgAllAppsList, synchronousBindPage, mCallbacks);
                    if (mModelLoaded && !mIsLoaderTaskRunning) {
                        loaderResults.bindWorkspace();
                        loaderResults.bindAllApps();
                        loaderResults.bindDeepShortcuts();
                        loaderResults.bindWidgets();
                        return true;
                    } else {
                        startLoaderForResults(loaderResults);
                    }
                }
            }
            return false;
        }
    

    开始的时候肯定执行的else->startLoaderForResults(...)
    最终执行到LoaderTask中的run()方法

    public void run() {
            synchronized (this) {
                // Skip fast if we are already stopped.
                if (mStopped) {
                    return;
                }
            }
    
            TraceHelper.beginSection(TAG);
            try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
                TraceHelper.partitionSection(TAG, "step 1.1: loading workspace");
                loadWorkspace();
    
                verifyNotStopped();
                TraceHelper.partitionSection(TAG, "step 1.2: bind workspace workspace");
                mResults.bindWorkspace();
    
                // Notify the installer packages of packages with active installs on the first screen.
                TraceHelper.partitionSection(TAG, "step 1.3: send first screen broadcast");
                sendFirstScreenActiveInstallsBroadcast();
    
                // Take a break
                TraceHelper.partitionSection(TAG, "step 1 completed, wait for idle");
                waitForIdle();
                verifyNotStopped();
    
                // second step
                TraceHelper.partitionSection(TAG, "step 2.1: loading all apps");
                loadAllApps();
    
                TraceHelper.partitionSection(TAG, "step 2.2: Binding all apps");
                verifyNotStopped();
                mResults.bindAllApps();
    
                verifyNotStopped();
                TraceHelper.partitionSection(TAG, "step 2.3: Update icon cache");
                updateIconCache();
    
                // Take a break
                TraceHelper.partitionSection(TAG, "step 2 completed, wait for idle");
                waitForIdle();
                verifyNotStopped();
    
                // third step
                TraceHelper.partitionSection(TAG, "step 3.1: loading deep shortcuts");
                loadDeepShortcuts();
    
                verifyNotStopped();
                TraceHelper.partitionSection(TAG, "step 3.2: bind deep shortcuts");
                mResults.bindDeepShortcuts();
    
                // Take a break
                TraceHelper.partitionSection(TAG, "step 3 completed, wait for idle");
                waitForIdle();
                verifyNotStopped();
    
                // fourth step
                TraceHelper.partitionSection(TAG, "step 4.1: loading widgets");
                mBgDataModel.widgetsModel.update(mApp, null);
    
                verifyNotStopped();
                TraceHelper.partitionSection(TAG, "step 4.2: Binding widgets");
                mResults.bindWidgets();
    
                transaction.commit();
            } catch (CancellationException e) {
                // Loader stopped, ignore
                TraceHelper.partitionSection(TAG, "Cancelled");
            }
            TraceHelper.endSection(TAG);
        }
    

    就以loadAllApps()为例,下面是调度的流程图。


    loadApps调度流程.jpg

    可以看出这一系列的调度最终还是会到PMS中取package相关的信息,这时候可以看出来LauncherAppsService的重要,这是Launcher app与PMS之间的桥梁,P版本改动的架构代码,我觉得这样改动更好一点,因为之前直接通过PackageManager操作PMS不太好,Package的管理也放在Launcher中显得太乱了,因为Launcher只管显示,具体的操作还是交给PMS好一点。
    本文到这里就结束了,讲解了Launcher的启动流程以及工作原理,多谢鼓励。

    三、Launcher小问题

    关注一下这个问题:https://www.zhihu.com/question/32775665
    这是一个很有趣的问题,大家可以先看一下。

    相关文章

      网友评论

          本文标题:Android源码指南之Launcher剖析

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