Android源码指南之Launcher剖析
上一篇文章《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
这是一个很有趣的问题,大家可以先看一下。