Launcher启动器
Launcher(启动器、桌面) 是 Android 操作系统上用于展现运行图标、搜查运行、控制桌面快捷模式以及口头其余与设备主屏幕关系义务的用户界面。设备的主屏幕规划和外观是用户与设备交互的关键模式。
Launcher特点:
Launcher进程启动流程
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会扫描/data/app目录,加载曾经装置的运行程序消息。
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;}
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进程启动后,会向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 anythingif (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 handlersWorker.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, ignoreTraceHelper.partitionSection(TAG, "Cancelled");}TraceHelper.endSection(TAG);}
随着Android系统的始终开展和降级,Launcher进程的启动流程也或者会出现相应的变动和提升。Android系统还支持多种启动Launcher的模式,如开机后智能启动、短按Home键启动以及意外解体后智能重启等。这些启动模式的成功流程也有所不同,但基本流程都与上述步骤相似。