最近在弄开机向导,网上查了查,基本都是参照系统的Provision应用来做的,而且还要将apk打包到系统目录下的pri-app目录下,打包到其他目录下不行,参照着做是没问题,但是好奇为什么要这么做?调用流程是怎样?这就是接下来要来探究的。
launcher启动
以如下模拟器为例来看Google首次开机涉及到哪些launcher
开机时过滤日志ActivityTaskManager
2023-01-29 16:44:03.285 1993-1993/system_process I/ActivityTaskManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.android.settings/.CryptKeeper} from uid 0 2023-01-29 16:44:03.289 1993-1993/system_process I/ActivityTaskManager: Loaded persisted task ids for user 0 2023-01-29 16:44:04.089 1993-2096/system_process I/ActivityTaskManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.google.android.setupwizard/.SetupWizardActivity} from uid 0 2023-01-29 16:44:05.129 1993-2347/system_process I/ActivityTaskManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.android.settings/.FallbackHome} from uid 0 2023-01-29 16:44:06.212 1993-2032/system_process I/ActivityTaskManager: Displayed com.android.settings/.FallbackHome: +1s80ms 2023-01-29 16:44:14.244 1993-2610/system_process I/ActivityTaskManager: Config changes=3 {1.0 310mcc260mnc [en_US] ldltr sw392dp w392dp h757dp 440dpi nrml long port finger qwerty/v/v dpad/v winConfig={ mBounds=Rect(0, 0 - 1080, 2280) mAppBounds=Rect(0, 0 - 1080, 2148) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_0} s.6} 2023-01-29 16:44:14.264 1993-2610/system_process I/ActivityTaskManager: Override config changes=3 {1.0 310mcc260mnc [en_US] ldltr sw392dp w392dp h757dp 440dpi nrml long port finger qwerty/v/v dpad/v winConfig={ mBounds=Rect(0, 0 - 1080, 2280) mAppBounds=Rect(0, 0 - 1080, 2148) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=undefined mAlwaysOnTop=undefined mRotation=ROTATION_0} s.6} for displayId=0 2023-01-29 16:44:16.159 1993-2024/system_process I/ActivityTaskManager: Loading recents for user 0 into memory. 2023-01-29 16:44:16.436 1993-3016/system_process I/ActivityTaskManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.google.android.sdksetup/.DefaultActivity} from uid 0 2023-01-29 16:44:19.084 1993-2047/system_process I/ActivityTaskManager: Force finishing activity ActivityRecord{cb6abf3 u0 com.google.android.sdksetup/.DefaultActivity t4} 2023-01-29 16:44:19.086 1993-2047/system_process I/ActivityTaskManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000100 cmp=com.google.android.apps.nexuslauncher/.NexusLauncherActivity} from uid 0 2023-01-29 16:44:19.234 1993-2028/system_process W/system_server: Long monitor contention with owner PackageManager (2047) at void com.android.server.wm.ActivityTaskManagerService$LocalService.cleanupDisabledPackageComponents(java.lang.String, java.util.Set, int, boolean)(ActivityTaskManagerService.java:6820) waiters=0 in void com.android.server.wm.InputMonitor$1.run() for 150ms 2023-01-29 16:44:26.653 1993-2032/system_process I/ActivityTaskManager: Displayed com.google.android.apps.nexuslauncher/.NexusLauncherActivity: +5s191ms
涉及到应用包括:
1、com.android.settings/.CryptKeeper
2、com.google.android.setupwizard/.SetupWizardActivity
3、com.android.settings/.FallbackHome
4、com.google.android.sdksetup/.DefaultActivity
5、com.google.android.apps.nexuslauncher/.NexusLauncherActivity
开机涉及到这么多页面,但我们实际能感觉到的就只有FallbackHome和NexusLauncherActivity两个页面,NexusLauncherActivity就是我们最终显示的launcher了,FallbackHome则是开机动画完成后出现的一个加载动画(Pixs is Starting ...)。
再说launcher启动前,先来看下验证的几个方法:
fun testApi() {
val homeIntent = Intent(Intent.ACTION_MAIN)
homeIntent.addCategory(Intent.CATEGORY_HOME)
val resolvedType = homeIntent.resolveTypeIfNeeded(contentResolver)
val queryIntentActivities = AppGlobals.getPackageManager().queryIntentActivities(
homeIntent, resolvedType,
PackageManager.GET_SHARED_LIBRARY_FILES, 0
)
for (parcelable in queryIntentActivities.list) {
val toString = (parcelable as ResolveInfo).toString()
Log.d(ZZQ_TAG, "queryIntentActivities: "+toString)
}
val resolveIntent = AppGlobals.getPackageManager().resolveIntent(
homeIntent, resolvedType,
PackageManager.GET_SHARED_LIBRARY_FILES, 0
)
Log.d(ZZQ_TAG, "resolveIntent: "+resolveIntent.toString())
}
queryIntentActivities()查询到的就是当前所有匹配到的activity信息,而resolveIntent()就是获取当前优先级最高的launcher,用这个方法测试时,需要注意这个应用要是系统应用,有系统签名和menifest中需要加android:sharedUserId="android.uid.system",否则拿到的就不全,
对于launcher启动属性的页面,当页面起来后,调用它的finish()方法,就会重新去启动优先级高的launcher(调用Activity的pause()方法后,系统会重新执行启动launcher),可能会有疑问,重新启动不还是刚finish()点的launcher么,正常来说是的,但是如果调用了如下方法:
val defaultName = ComponentName("com.android.provision", "com.android.provision.DefaultActivity")
pm.setComponentEnabledSetting(
defaultName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP
)
那么在PackageManager中就会去掉这个Activity的信息,自然重新查找launcher就不是原来的launcher了,按照这个逻辑,除了首次开机会启动所有的launcher,其他时候开机就只剩NexusLauncherActivity了,但是每次打开模拟器时,都会有FallbackHome的加载动画,那就说明FallbackHome并没有从包管理中去掉,那是怎么跳过FallbackHome启动后面的Launcher的呢?那这就要看android:directBootAware="true"了,这个属性的意思是当设备未解锁时是否允许被启动,true表示可以启动,false表示不可以启动,FallbackHome就配置了这个属性,并且FallbackHome是在监听到解锁后finish()掉的,接下来看下这几个页面在menifest的配置:
1、com.android.settings/.CryptKeeper
<application
android:directBootAware="true">
<activity
android:theme="@ref/0x7f13027a"
android:name="com.android.settings.CryptKeeper"
android:process=":CryptKeeper"
android:excludeFromRecents="true"
android:launchMode="1"
android:screenOrientation="5"
android:configChanges="0x233"
android:windowSoftInputMode="0x10"
android:immersive="true"
androidprv:systemUserOnly="true">
<intent-filter android:priority="10">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
2、com.google.android.setupwizard/.SetupWizardActivity
<activity
android:theme="@ref/0x7f1100b0"
android:label="@ref/0x7f100028"
android:name="com.google.android.setupwizard.SetupWizardActivity"
android:excludeFromRecents="true"
android:launchMode="2"
android:configChanges="0x4b3"
android:immersive="true"
android:directBootAware="true">
<intent-filter android:priority="5">
<action android:name="android.intent.action.MAIN" />
<action android:name="android.intent.action.DEVICE_INITIALIZATION_WIZARD" />
<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>
3、com.android.settings/.FallbackHome
<application
android:directBootAware="true">
<activity
android:theme="@ref/0x7f130104"
android:label="@string/0x32"
android:name="com.android.settings.FallbackHome"
android:taskAffinity="com.android.settings.FallbackHome"
android:excludeFromRecents="true"
android:screenOrientation="5">
<intent-filter android:priority="-1000">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
4、com.google.android.sdksetup/.DefaultActivity
<activity
android:name="DefaultActivity"
android:excludeFromRecents="true">
<intent-filter android:priority="3">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
5、com.google.android.apps.nexuslauncher/.NexusLauncherActivity
<activity
android:theme="@ref/0x7f12000a"
android:name="com.google.android.apps.nexuslauncher.NexusLauncherActivity"
android:enabled="true"
android:taskAffinity="@string/0x35"
android:clearTaskOnLaunch="true"
android:stateNotNeeded="true"
android:launchMode="2"
android:screenOrientation="-1"
android:configChanges="0xdf3"
android:windowSoftInputMode="0x20"
android:resumeWhilePausing="true"
android:resizeableActivity="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>
<meta-data
android:name="com.android.launcher3.grid.control"
android:value="com.google.android.apps.nexuslauncher.grid_control" />
</activity>
上面这些就是Pixel 5模拟器所有launcher配置,系统启动选取launcher是通过上面提到的resolveIntent()这个方法解决,感兴趣的可以自己去看看源码,在开机时,系统是没有解锁的,所以只会找到配置了android:directBootAware="true"的Activity,所以这里先找到的是CryptKeeper、SetupWizardActivity、FallbackHome,然后再来看下优先级,CryptKeeper的priority是10、SetupWizardActivity的priority是5、FallbackHome的priority是-1000,优先级高的先执行,CryptKeeper、SetupWizardActivity执行完后通常都是会从包管理中移除,在执行到FallbackHome时,FallbackHome有解锁的监听,解锁完成就直接finish()掉了,但不会从包管理中移除,这也就是每次开机都能看到一个加载的动画,FallbackHome执行完后,再次寻找launcher时,找到是FallbackHome、DefaultActivity、NexusLauncherActivity,同样根据优先级,这时候由于FallbackHome的优先级是-1000,所以是不会执行了,DefaultActivity执行完后会直接从包管理中移除,所以最终显示的就是NexusLauncherActivity了,而在后续的开机中,就只会涉及到FallbackHome和NexusLauncherActivity了,这样开机中涉及到的所有launcher就全了。上面所涉及到的五个Activity,其中SetupWizardActivity、DefaultActivity是开机向导,为什么Pixel 5中会有两个开机向导,这就不是很明白了(个人猜测可能是在设置SetupWizardActivity的过程中,就已经解锁了,而有些设置需要在解锁后设置)。
2、自定义开机向导
明白了开机流程中的原理,再来弄开机向导就很简单。
现在就可以来自定义我们的开机向导了,按照网上大部分教程是参照Provision这个应用,这里就会有个问题,如果开机向导中有设置语言,在首次开机时,FallbackHome的加载动画的文本就只能使用系统默认的语言了,而且还延长了开机时间,所以建议参照SetupWizardActivity来配置,这样有几个好处:
1、可以不将包放到系统目录的pri-app目录下;
2、首次开机可以省略掉FallbackHome加载动画(前提是开机向导设置完成时设备已解锁);
如果有些需求需要系统解锁后在进行设置,那就可以参照网上的Provision了,如果你不想将包打包进pri-app目录下(后续手动安装覆盖不掉),也是有解决方案的,那就是重新设置launcher的优先级,不过这里需要注意下,不再pri-app目录下,设置1到1000是无效的,只能设置0到-1000,这样所有问题都可以解决了。