美文网首页
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