Launcher启动器
Launcher(启动器、桌面) 是 Android 操作系统上用于展示应用图标、搜索应用、管理桌面快捷方式以及执行其他与设备主屏幕相关任务的用户界面。设备的主屏幕布局和外观是用户与设备交互的主要方式。
Launcher特点:
- 「自定义性」:大多数 Launcher 都允许用户自定义图标、壁纸、桌面布局等。
- 「性能」:高效的 Launcher 可以提高设备的整体性能,因为它需要快速响应触摸输入并加载图标和布局。
- 「兼容性」:随着 Android 版本的更新,Launcher 需要确保与最新版本的 Android 兼容。
- 「安全性」:Launcher 必须确保用户数据的安全,并防止恶意软件的攻击。
- 「多样性」:市场上有许多不同的 Launcher 应用,每个应用都有其独特的功能和界面设计。
Launcher进程启动流程
- 「SystemServer进程启动」:
SystemServer是Android系统中的一个核心进程,负责启动和初始化各种系统服务。
在SystemServer的启动过程中,会调用其他服务,如PackageManagerService(PMS)和ActivityManagerService(AMS)的初始化方法。
public final class SystemServer {
private void run() {
...
startBootstrapServices();
startOtherServices();
...
}
private void startBootstrapServices() {
...
mActivityManagerService = mSystemServiceManager.startService(ActivityManagerService.Lifecycle.class).getService();
mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
mActivityManagerService.setInstaller(installer);
...
}
private void startOtherServices() {
...
mActivityManagerService.systemReady(() -> {
}, BOOT_TIMINGS_TRACE_LOG);
}
}
在SystemServer启动的时候,执行startOtherServices()方法中调用了AMS的systemReady()方法,通过该方法来启动Launcher。
// Tag for timing measurement of main thread.
private static final String SYSTEM_SERVER_TIMING_TAG = "SystemServerTiming";
private static final TimingsTraceLog BOOT_TIMINGS_TRACE_LOG
= new TimingsTraceLog(SYSTEM_SERVER_TIMING_TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
private void startOtherServices() {
...
mActivityManagerService.systemReady(() -> {
Slog.i(TAG, "Making services ready");
traceBeginAndSlog("StartActivityManagerReadyPhase");
mSystemServiceManager.startBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
...
}, BOOT_TIMINGS_TRACE_LOG);
}
- 「PMS服务初始化」:
PMS服务会完成系统中应用程序的安装和管理工作。
PMS会扫描/data/app目录,加载已经安装的应用程序信息。
- 「AMS服务初始化」:
AMS是Android系统中负责管理应用程序生命周期和活动(Activity)状态的服务。
在AMS的初始化过程中,会注册各种系统广播接收器,包括与Launcher启动相关的广播。
4.「Launcher应用程序的注册」:
Launcher应用程序是一个特殊的系统应用,它在AndroidManifest.xml文件中配置了特定的Intent Filter,以便系统能够识别并启动它。
通常,Launcher应用程序的Action被设置为Intent.ACTION_MAIN,而Category被设置为Intent.CATEGORY_HOME。
5.「SystemReady阶段」:
当系统完成初始化并准备好启动桌面时,AMS会调用其systemReady()方法。
在systemReady()方法中,AMS会检查系统是否准备好启动Launcher,并调用相关方法来启动。
public void systemReady(final Runnable goingCallback, TimingsTraceLog traceLog) {
...
synchronized (this) {
...
startHomeActivityLocked(currentUserId, "systemReady");
...
}
...
}
在startHomeActivityLocked()方法中,通过getHomeIntent()方法获取到要启动的HomeActivity的intent对象,mTopAction默认为INTENT.ACTION_MAIN,并添加CATEGORY_HOME的category标志。通过PackageManager去获取对应符合的Activity,获取对应的ActivityInfo,并获取对应的进程记录,此时对应的进程还没启动,为intent添加FLAG_ACTIVITY_NEW_TASK启动参数开启新栈,随后调用ActivityStartController类的startHomeActivity()方法去执行启动。
boolean startHomeActivityLocked(int userId, String reason) {
...
Intent intent = getHomeIntent();
ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
if (aInfo != null) {
intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
// Don't do this if the home app is currently being instrumented.
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);
// For ANR debugging to verify if the user activity is the one that actually launched.
final String myReason = reason + ":" + userId + ":" + resolvedUserId;
mActivityStartController.startHomeActivity(intent, aInfo, myReason);
}
}
...
return true;
}
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;
}
- 「启动Launcher进程」:
AMS启动Launcher进程。
该方法会创建一个新的进程(如果Launcher尚未运行)来启动Launcher应用程序。
void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {
mSupervisor.moveHomeStackTaskToTop(reason);
mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
.setOutActivity(tmpOutRecord)
.setCallingUid(0)
.setActivityInfo(aInfo)
.execute();
mLastHomeActivityStartRecord = tmpOutRecord[0];
if (mSupervisor.inResumeTopActivity) {
// If we are in resume section already, home activity will be initialized, but not
// resumed (to avoid recursive resume) and will stay that way until something pokes it
// again. We need to schedule another resume.
mSupervisor.scheduleResumeTopActivities();
}
}
int execute() {
try {
// TODO(b/64750076): Look into passing request directly to these methods to allow
// for transactional diffs and preprocessing.
if (mRequest.mayWait) {
return startActivityMayWait(mRequest.caller, mRequest.callingUid, ...);
} else {
return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent, ...);
}
} finally {
onExecutionComplete();
}
}
- 「Launcher进程启动后的操作」:
Launcher进程启动后,会向PMS请求已安装应用程序的信息,并将这些信息展示在桌面上。
用户可以通过点击桌面上的应用程序图标来启动相应的应用程序。
@TargetApi(23)
public InvariantDeviceProfile(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
DisplayMetrics dm = new DisplayMetrics();
display.getMetrics(dm);
...
ArrayList<InvariantDeviceProfile> closestProfiles = findClosestDeviceProfiles(minWidthDps, minHeightDps, getPredefinedDeviceProfiles(context));
...
}
ArrayList<InvariantDeviceProfile> getPredefinedDeviceProfiles(Context context) {
ArrayList<InvariantDeviceProfile> profiles = new ArrayList<>();
try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
final int depth = parser.getDepth();
int type;
while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if ((type == XmlPullParser.START_TAG) && "profile".equals(parser.getName())) {
TypedArray a = context.obtainStyledAttributes(Xml.asAttributeSet(parser), R.styleable.InvariantDeviceProfile);
int numRows = a.getInt(R.styleable.InvariantDeviceProfile_numRows, 0);
int numColumns = a.getInt(R.styleable.InvariantDeviceProfile_numColumns, 0);
float iconSize = a.getFloat(R.styleable.InvariantDeviceProfile_iconSize, 0);
profiles.add(new InvariantDeviceProfile(
a.getString(R.styleable.InvariantDeviceProfile_name),
a.getFloat(R.styleable.InvariantDeviceProfile_minWidthDps, 0),
a.getFloat(R.styleable.InvariantDeviceProfile_minHeightDps, 0),
numRows,
numColumns,
a.getInt(R.styleable.InvariantDeviceProfile_numFolderRows, numRows),
a.getInt(R.styleable.InvariantDeviceProfile_numFolderColumns, numColumns),
iconSize,
a.getFloat(R.styleable.InvariantDeviceProfile_landscapeIconSize, iconSize),
a.getFloat(R.styleable.InvariantDeviceProfile_iconTextSize, 0),
a.getInt(R.styleable.InvariantDeviceProfile_numHotseatIcons, numColumns),
a.getResourceId(R.styleable.InvariantDeviceProfile_defaultLayoutId, 0),
a.getResourceId(R.styleable.InvariantDeviceProfile_demoModeLayoutId, 0)));
a.recycle();
}
}
} catch (IOException|XmlPullParserException e) {
throw new RuntimeException(e);
}
return profiles;
}
InvariantDeviceProfile对象主要是存储App的基本配置信息,例如App图标的尺寸大小,文字大小,每个工作空间或文件夹能显示多少App等。
在LauncherModel的startLoader()方法中,新建了一个LoaderResults对象,通过startLoaderForResults()方法创建出一个LoaderTask的Runnable任务。
public boolean startLoader(int synchronousBindPage) {
...
synchronized (mLock) {
// Don't bother to start the thread if we know it's not going to do anything
if (mCallbacks != null && mCallbacks.get() != null) {
...
LoaderResults loaderResults = new LoaderResults(mApp, sBgDataModel, mBgAllAppsList, synchronousBindPage, mCallbacks);
if (mModelLoaded && !mIsLoaderTaskRunning) {
...
return true;
} else {
startLoaderForResults(loaderResults);
}
}
}
return false;
}
public void startLoaderForResults(LoaderResults results) {
synchronized (mLock) {
stopLoader();
mLoaderTask = new LoaderTask(mApp, mBgAllAppsList, sBgDataModel, results);
runOnWorkerThread(mLoaderTask);
}
}
private static void runOnWorkerThread(Runnable r) {
if (sWorkerThread.getThreadId() == Process.myTid()) {
r.run();
} else {
// If we are not on the worker thread, then post to the worker handler
sWorker.post(r);
}
}
在LoaderTask的run()方法中,加载手机已安装的App的信息,查询数据库获取已安装的App的相关信息,加载Launcher布局,并将数据转化为View,绑定到界面上,最终就可以看到桌面显示的宫格列表的桌面图标了。
public void run() {
...
try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
// 查询数据库整理App信息,转化为View绑定到界面
loadWorkspace();
mResults.bindWorkspace();
loadAllApps();
mResults.bindAllApps();
loadDeepShortcuts();
mResults.bindDeepShortcuts();
mBgDataModel.widgetsModel.update(mApp, null);
mResults.bindWidgets();
transaction.commit();
} catch (CancellationException e) {
// Loader stopped, ignore
TraceHelper.partitionSection(TAG, "Cancelled");
}
TraceHelper.endSection(TAG);
}
随着Android系统的不断发展和更新,Launcher进程的启动流程也可能会发生相应的变化和优化。Android系统还支持多种启动Launcher的方式,如开机后自动启动、短按Home键启动以及异常崩溃后自动重启等。这些启动方式的实现流程也有所不同,但基本流程都与上述步骤相似。