一、Android 系统启动流程

  1. Bootloader 系统引导

  2. 启动 Linux 内核

  3. 启动 init 进程

  4. 启动 Zygote 进程

  5. 启动 SystemServer 进程

    • 启动 Binder 线程池
    • 创建 SystemServiceManager 并启动各种 SystemService

二、启动设置向导或 Launcher

SystemServer 会在 startBootstrapServices() 方法中会启动 ActivityManagerService 。

private void startBootstrapServices() {...// Activity manager runs the show.mActivityManagerService = mSystemServiceManager.startService(ActivityManagerService.Lifecycle.class).getService();...
}
复制代码

在 startOtherServices() 方法中会调用 ActivityManagerService 的 systemReday() 方法。

private void startOtherServices() {
...mActivityManagerService.systemReady(new Runnable() {@Overridepublic void run() {Slog.i(TAG, "Making services ready");mSystemServiceManager.startBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);...}
...
}
复制代码

ActivityManagerService 的 systemReday() 方法中会调用 startHomeActivityLocked() 方法。

public void systemReady(final Runnable goingCallback) {...synchronized (this) {...startHomeActivityLocked(currentUserId, "systemReady");...}...
}
复制代码

startHomeActivityLocked() 方法中会获取到 Action 为 Intent.ACTION_MAIN,Category 为 Intent.CATEGORY_HOME 的 Intent,根据该 Intent 获取到符合条件的应用,并判断该应用是否已经启动,没有启动则启动该应用。

...
String mTopAction = Intent.ACTION_MAIN;
...
// 获取 Action 为 Intent.ACTION_MAIN,Category 为 Intent.CATEGORY_HOME 的 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;
}boolean startHomeActivityLocked(int userId, String reason) {if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL&& mTopAction == null) {// We are running in factory test mode, but unable to find// the factory test app, so just sit around displaying the// error message and don't try to start anything.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));// 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.instrumentationClass == null) {intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);// 启动应用程序mActivityStarter.startHomeActivityLocked(intent, aInfo, reason);}} else {Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());}return true;
}
复制代码

一般情况下,被启动的应用就是 Launcher,因为 Launcher 的 Manifest 文件中有匹配了 Action 为 Intent.ACTION_MAIN,Category 为 Intent.CATEGORY_HOME 的 Activity。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.android.launcher3"><uses-sdk android:targetSdkVersion="23" android:minSdkVersion="21"/>
...<applicationandroid:backupAgent="com.android.launcher3.LauncherBackupAgent"android:fullBackupOnly="true"android:fullBackupContent="@xml/backupscheme"android:hardwareAccelerated="true"android:icon="@mipmap/ic_launcher_home"android:label="@string/derived_app_name"android:largeHeap="@bool/config_largeHeap"android:restoreAnyVersion="true"android:supportsRtl="true" ><activityandroid:name="com.android.launcher3.Launcher"android:launchMode="singleTask"android:clearTaskOnLaunch="true"android:stateNotNeeded="true"android:theme="@style/LauncherTheme"android:windowSoftInputMode="adjustPan"android:screenOrientation="nosensor"android:configChanges="keyboard|keyboardHidden|navigation"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"/></intent-filter></activity>...</application>
</manifest>
复制代码

但是,当首次开机时,被启动的应用就是设置向导,因为设置向导的 Manifest 文件中也有匹配了 Action 为 Intent.ACTION_MAIN,Category 为 Intent.CATEGORY_HOME 的 Activity,并且优先级高于 Launcher。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.android.provision">...<application><activityandroid:name="DefaultActivity"android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen"android:excludeFromRecents="true"><intent-filter android:priority="1"><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.SETUP_WIZARD" /></intent-filter></activity></application>
</manifest>
复制代码

Launcher 的 Manifest 中 intent-filter 没有设置优先级,默认为 0;设置向导的 Manifest 中 intent-filter 的优先级为 1;所以在 resolveActivityInfo() 方法获取符合的应用时会优先获取到设置向导。

private ActivityInfo resolveActivityInfo(Intent intent, int flags, int userId) {ActivityInfo ai = null;ComponentName comp = intent.getComponent();try {if (comp != null) {// Factory test.ai = AppGlobals.getPackageManager().getActivityInfo(comp, flags, userId);} else {ResolveInfo info = AppGlobals.getPackageManager().resolveIntent(intent,intent.resolveTypeIfNeeded(mContext.getContentResolver()),flags, userId);if (info != null) {ai = info.activityInfo;}}} catch (RemoteException e) {// ignore}return ai;
}
复制代码

获取最优 Activity 的具体实现在 PackageManagerService 的 chooseBestActivity() 方法中。

三、priority 及 android.intent.category.SETUP_WIZARD

Manifest 中 Activity 的 intent-filter 的优先级设置只有系统应用才会生效,非系统应用会被 PackageManagerService 调整为 0。

/*** Adjusts the priority of the given intent filter according to policy.* <p>* <ul>* <li>The priority for non privileged applications is capped to '0'</li>* <li>The priority for protected actions on privileged applications is capped to '0'</li>* <li>The priority for unbundled updates to privileged applications is capped to the*      priority defined on the system partition</li>* </ul>* <p>* <em>NOTE:</em> There is one exception. For security reasons, the setup wizard is* allowed to obtain any priority on any action.*/
private void adjustPriority(List<PackageParser.Activity> systemActivities, ActivityIntentInfo intent) {...final boolean privilegedApp =((applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0);if (!privilegedApp) {// non-privileged applications can never define a priority >0Slog.w(TAG, "Non-privileged app; cap priority to 0;"+ " package: " + applicationInfo.packageName+ " activity: " + intent.activity.className+ " origPrio: " + intent.getPriority());intent.setPriority(0);return;}if (systemActivities == null) {// the system package is not disabled; we're parsing the system partitionif (isProtectedAction(intent)) {if (mDeferProtectedFilters) {// We can't deal with these just yet. No component should ever obtain a// >0 priority for a protected actions, with ONE exception -- the setup// wizard. The setup wizard, however, cannot be known until we're able to// query it for the category CATEGORY_SETUP_WIZARD. Which we can't do// until all intent filters have been processed. Chicken, meet egg.// Let the filter temporarily have a high priority and rectify the// priorities after all system packages have been scanned.mProtectedFilters.add(intent);if (DEBUG_FILTERS) {Slog.i(TAG, "Protected action; save for later;"+ " package: " + applicationInfo.packageName+ " activity: " + intent.activity.className+ " origPrio: " + intent.getPriority());}return;} else {if (DEBUG_FILTERS && mSetupWizardPackage == null) {Slog.i(TAG, "No setup wizard;"+ " All protected intents capped to priority 0");}if (intent.activity.info.packageName.equals(mSetupWizardPackage)) {if (DEBUG_FILTERS) {Slog.i(TAG, "Found setup wizard;"+ " allow priority " + intent.getPriority() + ";"+ " package: " + intent.activity.info.packageName+ " activity: " + intent.activity.className+ " priority: " + intent.getPriority());}// setup wizard gets whatever it wantsreturn;}Slog.w(TAG, "Protected action; cap priority to 0;"+ " package: " + intent.activity.info.packageName+ " activity: " + intent.activity.className+ " origPrio: " + intent.getPriority());intent.setPriority(0);return;}}// privileged apps on the system image get whatever priority they requestreturn;}...
}
复制代码

在 adjustPriority 方法中,如果 packageName 为 mSetupWizardPackage 就不会调整其优先级,保持其 Manifest 中设置的优先级。mSetupWizardPackage 的值从 getSetupWizardPackageName() 方法中获取。

final @Nullable String mSetupWizardPackage;
...public PackageManagerService(Context context, Installer installer,boolean factoryTest, boolean onlyCore) {...mSetupWizardPackage = getSetupWizardPackageName();...
}private @Nullable String getSetupWizardPackageName() {final Intent intent = new Intent(Intent.ACTION_MAIN);intent.addCategory(Intent.CATEGORY_SETUP_WIZARD);final List<ResolveInfo> matches = queryIntentActivitiesInternal(intent, null,MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE| MATCH_DISABLED_COMPONENTS,UserHandle.myUserId());if (matches.size() == 1) {return matches.get(0).getComponentInfo().packageName;} else {Slog.e(TAG, "There should probably be exactly one setup wizard; found " + matches.size()+ ": matches=" + matches);return null;}
}
复制代码

所以 mSetupWizardPackage 就是有添加了 Category 为 android.intent.category.SETUP_WIZARD 的 Activity 的应用。

四、设置向导完成

既然设置向导的优先级高于 Launcher,那每次开机时不是都会先启动设置向导么,为什么设置向导完成后再次开机直接进入了 Launcher?

因为设置向导在最后退出时会禁用掉添加了 Category 为 Intent.CATEGORY_HOME 的 Activity,所以 ActivityManagerService 在 resolveActivityInfo() 获取匹配的应用时就不会获取到设置向导,直接获取到了 Launcher。

// remove this activity from the package manager.
PackageManager pm = getPackageManager();
ComponentName name = new ComponentName(this, DefaultActivity.class);
pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,PackageManager.DONT_KILL_APP);
复制代码

Android 设置向导启动分析相关推荐

  1. Android DHCP 启动分析【2】

    2019独角兽企业重金招聘Python工程师标准>>> 一.DHCP client 和 dhcpcd server直接的信息交互: Client 和server 通过property ...

  2. android挂载usb设备,android usb挂载分析---MountService启动

    在android usb挂载分析----vold启动,我们的vold模块已经启动了,通信的机制也已经建立起来了,接下来我们分析一下MountService的启动,也就是我们FrameWork层的启动, ...

  3. 【Android 安全】DEX 加密 ( Application 替换 | Android 应用启动原理 | ActivityThread 后续分析 | Application 替换位置 )

    文章目录 一.ActivityThread 后续分析 二.ActivityThread 相关源码 三.Application 替换位置 dex 解密时 , 需要将 代理 Application 替换为 ...

  4. 【Android 性能优化】应用启动优化 ( 安卓应用启动分析 | Launcher 应用启用普通安卓应用 | 应用进程分析 )

    文章目录 一. Launcher 应用 startActivitySafely 方法分析 二. Launcher 中的 startActivity(View v, Intent intent, Obj ...

  5. Android 启动分析 1

    1.概述 Android虽然被称作一种操作系统,其实它仍然使用的Linux的kernel.所以本质上可以说,Android是一个适用于移动设备的Linux发行版.也就是说,之前的分析Linux内核的经 ...

  6. 图解Android - Zygote, System Server 启动分析

    Init 是所有Linux程序的起点,而Zygote于Android,正如它的英文意思,是所有java程序的'孵化池'(玩过星际虫族的兄弟都晓得的).用ps 输出可以看到 >adb shell ...

  7. 分析启动耗时 android,Android app启动耗时分析

    首先编译你的程序,打开Android Studio里面的Android Monitor,找到下图的按钮 &amp;amp;amp;amp;amp;lt;img src="//bbsm ...

  8. android usb挂载分析---MountService启动

    在android usb挂载分析----vold启动,我们的vold模块已经启动了,通信的机制也已经建立起来了,接下来我们分析一下MountService的启动,也就是我们FrameWork层的启动, ...

  9. 【SemiDrive源码分析】【X9芯片启动流程】30 - AP1 Android Kernel 启动流程 start_kernel 函数详细分析(一)

    [SemiDrive源码分析][X9芯片启动流程]30 - AP1 Android Kernel 启动流程 start_kernel 函数详细分析(一) 一.Android Kernel 启动流程分析 ...

最新文章

  1. 深度睡眠中,记忆如何被保持?
  2. Web安全测试检查单
  3. Select 标签可输入
  4. 程序进入后台继续执行
  5. 【Compiling Swift source files】编译很慢;
  6. 实例解析网络编程中的另类内存泄漏
  7. primefaces教程_Primefaces FileUpload组件示例教程
  8. 一元线性回归(Linear Regression)
  9. 肿瘤浸润淋巴细胞的扩增方法学评估
  10. 3D游戏编程与设计4——游戏对象与图形基础
  11. 通俗地讲一下庞加莱猜想是怎么回事(from 鼓浪)
  12. 第八章:加载Maya2011模型
  13. 网页加速之Link prefetching
  14. 双十一之后,留给证券区块链转型的时间不多了……
  15. C语言头文件.h互相包含所引发的一系列错误C2143之类的解决方法
  16. VSCode安装教程
  17. 小数乘法的C语言,小数乘法 5
  18. 正则表达式匹配——《剑指offer》
  19. EasyUI学习-如何使用jQuery EasyUI?
  20. C语言 记忆测试(Memory Test)小游戏的实现

热门文章

  1. 小白学习latex的辅助资料
  2. 风吹柳叶(Curtain creeper)
  3. 神经元网络技术有限公司,神经网络网站
  4. java汉字获取拼音的方法
  5. CTF简单的文件修复
  6. html div 自动滚动到底部,javascript让DIV的滚动自动滚动到最底部-4种方法
  7. Unity 获取文件夹下所有文件夹/文件
  8. 投稿开奖丨“轻量应用服务器”征文活动阳光普照奖(8月)开奖啦
  9. 网站换服务器ip会降权不,频繁更换IP会导致网站降权
  10. 组合业务流程管理与区块链