在launcher启动前,开启一个service

本文介绍如何通过设置Android应用的persistent属性实现在系统启动时优先于Launcher启动Service的方法,并提供了使用init.rc文件实现开机自启Service的备选方案。

最近有个客户需求:开机启动launcher前,先开启一个客户的service。 
想想觉得挺简单的,无非就是在AMS里面启动home前startservice就得了。不过,这样做不但不符合架构的风格,更出现了bug。接着,尝试在init.rc文件里面添加开机自启动项,把客户的service启动起来,但这又失败了。最后,在大神的指导下,终于完成了这项功能。下面来说一下实现的关键点。

关键点: 
当一个APP的AndroidManifest.xml中的application标签中声明了android:persistent="true"这个属性后,该APP会比launcher先加载。代码块如下:

<application
        android:name="com.example.myapp.MyApplication"
        android:persistent="true"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        ...
        ...
</application>

根据这个特点,我们可以在自己的APP里面声明android:persistent="true"这个属性,然后自己的APP就可以在launcher启动前,先加载了。而每个应用程序在第一次启动时,都会首先创建Application对象。所以,我们需要实现一个自己的Application,在其onCreate方法里面开启service,这样service就就会在launcher启动前开启了。 
部分代码如下:

public class MyApplication extends Application {


    @Override
    public void onCreate() {
        // TODO Auto-generated method stub
        super.onCreate();
        startService(new Intent("service_action"));

    }
}

打印日志,查看进程ID,确认service是比launcher先启动了。可是,为什么加了android:persistent="true"这个属性后,就会这样呢?于是,网上找找资料,看看源码,发现这是在AMS里面定义好了的。

在Android启动的时候,会在SystemServer.java里面启动各种系统service,包括AccountManagerService、ContentService、WindowManagerService、PackageManagerService、ActivityManagerService等服务。其中,在启动ActivityManagerService的最后,SystemServer.java会调用ActivityManagerService的systemReady方法。我们来看看此处调用的注释:

        // We now tell the activity manager it is okay to run third party
        // code.  It will call back into us once it has gotten to the state
        // where third party code can really run (but before it has actually
        // started launching the initial applications), for us to complete our
        // initialization.
        mActivityManagerService.systemReady(new Runnable() {
            @Override
            public void run() {
                ...
                ...
        });

从注释中可以看到,执行到systemReady这个方法就可以去加载第三方的app了,包括launcher这些系统应用。我们接着看AMS里面的systemReady方法:

public void systemReady(final Runnable goingCallback) {
    ...
    ...
    if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
        try {
            List apps = AppGlobals.getPackageManager().
                            getPersistentApplications(STOCK_PM_FLAGS);
            if (apps != null) {
                int N = apps.size();
                int i;
                for (i=0; i<N; i++) {
                    ApplicationInfo info = (ApplicationInfo)apps.get(i);
                    if (info != null && !info.packageName.equals("android")) {
                                        addAppLocked(info, false, null /* ABI override */);
                                }
                            }
                    }
        } catch (RemoteException ex) {
            // pm is in same process, this will never happen.
        }
    }
    // Start up initial activity.
    mBooting = true;
    startHomeActivityLocked(mCurrentUserId, "systemReady");//启动launcher
    ...
    ...
}

显然,getPersistentApplications这个方法名已经告诉我们了,这个方法是用来获取那些在AndroidManifest.xml中的application标签中声明了android:persistent="true"这个属性的app的信息。这个方法是在PackageManagerService(PKMS)中实现的,来看一下这个方法:

public List<ApplicationInfo> getPersistentApplications(int flags) {
        final ArrayList<ApplicationInfo> finalList = new ArrayList<ApplicationInfo>();

        // reader
        synchronized (mPackages) {
            final Iterator<PackageParser.Package> i = mPackages.values().iterator();
            final int userId = UserHandle.getCallingUserId();
            while (i.hasNext()) {
                final PackageParser.Package p = i.next();
                if (p.applicationInfo != null
                        && (p.applicationInfo.flags&ApplicationInfo.FLAG_PERSISTENT) != 0
                        && (!mSafeMode || isSystemApp(p))) {
                    PackageSetting ps = mSettings.mPackages.get(p.packageName);
                    if (ps != null) {
                        ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,
                                ps.readUserState(userId), userId);
                        if (ai != null) {
                            finalList.add(ai);
                        }
                    }
                }
            }
        }

        return finalList;
}

在PKMS中,有一个记录所有的程序包信息的哈希表(mPackages),每个表项中含有ApplicationInfo信息,该信息的flags(int型)数据中有一个专门的bit用于表示persistent。getPersistentApplications()函数会遍历这张表,找出所有persistent包,并返回ArrayList。

这里需要注意的是:

从代码里可以看出,带persistent标志的系统应用(即flags中设置了FLAG_SYSTEM)是一定会被选上的,但如果不是系统应用的话,则要进一步判断当前是否处于“安全模式”,一旦处于安全模式,那么就算应用设置了persistent属性,也不会被选中。

随后systemReady()开始遍历选中的ApplicationInfo,并对包名不为“android”的结点执行addAppLocked()。addAppLocked()的代码如下:

    final ProcessRecord addAppLocked(ApplicationInfo info, boolean isolated,
            String abiOverride) {
        ProcessRecord app;
        if (!isolated) {
            app = getProcessRecordLocked(info.processName, info.uid, true);
        } else {
            app = null;
        }

        if (app == null) {
            app = newProcessRecordLocked(info, null, isolated, 0);
            mProcessNames.put(info.processName, app.uid, app);
            if (isolated) {
                mIsolatedProcesses.put(app.uid, app);
            }
            updateLruProcessLocked(app, false, null);
            updateOomAdjLocked();
        }

        // This package really, really can not be stopped.
        try {
            AppGlobals.getPackageManager().setPackageStoppedState(
                    info.packageName, false, UserHandle.getUserId(app.uid));
        } catch (RemoteException e) {
        } catch (IllegalArgumentException e) {
            Slog.w(TAG, "Failed trying to unstop package "
                    + info.packageName + ": " + e);
        }

        if ((info.flags&(ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT))
                == (ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PERSISTENT)) {
            app.persistent = true;
            app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
        }
        if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
            mPersistentStartingProcesses.add(app);
            startProcessLocked(app, "added application", app.processName, abiOverride,
                    null /* entryPoint */, null /* entryPointArgs */);
        }

        return app;
    }

在AMS中,所谓的“add App”主要是指“添加一个与App进程对应的ProcessRecord节点”。当然,如果该节点已经添加过了,那么是不会重复添加的。在添加节点的动作完成以后,addAppLocked()还会检查App进程是否已经启动好了,如果尚未开始启动,此时就会调用startProcessLocked()启动这个进程。既然addAppLocked()试图确认App“正在正常运作”或者“将被正常启动”,那么其对应的package就不可能处于stopped状态,这就是上面代码调用setPackageStoppedState(…,false,…)的意思。

总的来说,带有android:persistent="true"这个属性的系统应用能在Android开机的时候开启。AMS里面的systemReady方法启动完这些应用后,接着执行startHomeActivityLocked方法来启动launcher。再把上面的代码贴一下:

public void systemReady(final Runnable goingCallback) {
    ...
    ...
    if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
        try {
            List apps = AppGlobals.getPackageManager().
                            getPersistentApplications(STOCK_PM_FLAGS);
            if (apps != null) {
                int N = apps.size();
                int i;
                for (i=0; i<N; i++) {
                    ApplicationInfo info = (ApplicationInfo)apps.get(i);
                    if (info != null && !info.packageName.equals("android")) {
                                        addAppLocked(info, false, null /* ABI override */);
                                }
                            }
                    }
        } catch (RemoteException ex) {
            // pm is in same process, this will never happen.
        }
    }
    // Start up initial activity.
    mBooting = true;
    startHomeActivityLocked(mCurrentUserId, "systemReady");//启动launcher
    ...
    ...
}

至此,我们就可以解释了为什么加了android:persistent="true"这个属性就能在launcher启动前,启动service了。

后记: 
前文提到,在init.rc文件里面添加开机自启动项,把service启动起来,失败了。但后来,我发现是我添加的代码有问题,现在把这个方法的实现写出来:

编写一个test.sh文件,让其启动service:
#! /system/bin/sh
echo "enter test.sh" > /Share/log.txt
date +%s >> /Share/log.txt

if [ ! -e /data/app/com.example.myapp-1.apk ] ; then

    pm install /system/preinstall/myapp/MyApp.apk
    am startservice -a service_action

fi  
echo "end test.sh" >> /Share/log.txt

在某个mk文件(除Android.mk文件)中,把test.sh文件拷贝到Android系统路径下:
PRODUCT_COPY_FILES += \
    device/common/test.sh:/system/bin/test.sh \
1
2
在init.rc文件里面声明启动test.sh脚本的代码:
service test /system/bin/sh /system/bin/test.sh
    user root
    group root
    disabled
    oneshot

on property:sys.system_ready=1
    start test

其中,service test /system/bin/sh /system/bin/test.sh声明了一个名为test的服务,这个服务调用了/system/bin/sh这个可执行程序,后面的/system/bin/test.sh是这个可执行程序的参数。然后,需要在一个特定条件下开启这个服务。on property:sys.system_ready=1就是当sys.system_ready这个属性值等于1的时候,会执行下面的start test来启动这个test服务。关于init.rc的其他语法,我就不详述了,有兴趣可以找一下这方面的资料。

千万要记得要指定/system/bin/sh来启动/system/bin/test.sh,之前我就是没有添加/system/bin/sh这句代码,就导致了开启失败的。
 

<think>我们正在处理的问题是:在Android 14中设置自定义Launcher为默认启动器。 根据引用[4]的内容,我们知道要让一个应用成为Launcher,需要在它的Activity中声明特定的intent-filter,即包含: <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.DEFAULT" /> 但是,当系统中有多个应用都声明了这些category时,系统会弹出选择器让用户选择。我们的目标是在系统级设置默认启动器,避免弹出选择器。 引用[3]和[5]提到,在Android 9.0和10.0中,可以通过修改frameworks/base/core/java/com/android/internal/app/ResolverActivity.java来设置默认Launcher。具体来说,在rebuildList()方法中,通过遍历当解析列表(rci:currentResolveList)并过滤掉非默认Launcher的包名,只保留默认Launcher,从而实现直接进入默认Launcher。 然而,在Android 14中,我们可能需要类似的方法,但ResolverActivity的代码可能有所变化。另外,我们也可以考虑另一种方法:在系统第一次启动时,通过系统属性或者系统服务直接设置默认Launcher。 以下是两种方法: 方法一:修改ResolverActivity(类似于Android 9.0/10.0的方法) 步骤: 1. 找到ResolverActivity的源码(路径:frameworks/base/core/java/com/android/internal/app/ResolverActivity.java) 2. 在rebuildList()方法中,找到处理HOME intent的地方(通常是通过mIntent的action是ACTION_MAIN,并且category包含CATEGORY_HOME) 3. 在构建列表之,判断是否有我们指定的默认Launcher,如果有,则直接启动它并finish当Activity。 方法二:在系统启动时设置默认Launcher(通过PackageManagerService) 在系统第一次启动时,通过系统服务设置默认的Home组件。我们可以修改PackageManagerService,在初始化时设置默认的Launcher。 这里我们采用方法一,因为方法二需要更深的系统集成,并且可能涉及更复杂的权限问题。 具体修改步骤(针对Android 14): 1. 打开ResolverActivity.java文件。 2. 在rebuildList()方法中,我们可以在构建解析列表之,判断是否有默认Launcher,如果有,则直接启动它并结束当Activity。 注意:我们需要定义一个默认Launcher的包名,可以通过系统属性或者直接硬编码(不推荐,最好通过系统属性配置)。 修改代码示例: 在rebuildList()方法中,找到处理HOME intent的地方(通常是通过mIntent的action是Intent.ACTION_MAIN,并且category包含Intent.CATEGORY_HOME)。然后,我们尝试获取一个默认的Launcher包名(例如,我们假设为"com.example.my.launcher"),然后检查这个包名的应用是否在解析列表中。如果在,则直接启动它。 代码修改如下: ```java // 在rebuildList()方法中,找到处理HOME intent的部分 if (mMultiProfilePagerAdapter.getActiveListController().getResolversForUser(mMultiProfilePagerAdapter.getCurrentUserHandle()).size() > 0) { // 新增:设置默认Launcher的包名 String defaultLauncherPackage = "com.example.my.launcher"; // 替换为你的Launcher包名 // 遍历当解析列表,查找是否有默认Launcher for (ResolvedComponentInfo info : rciList) { ActivityInfo ai = info.getResolveInfoAt(0).activityInfo; if (defaultLauncherPackage.equals(ai.packageName)) { // 找到默认Launcher,直接启动并结束当ResolverActivity onTargetSelected(info, false); finish(); return; } } } // 如果没有找到默认Launcher,则继续原来的流程,构建选择列表 ``` 但是,注意:在Android 14中,ResolverActivity可能已经重构,因此我们需要确保在正确的时机插入这段代码。另外,我们还需要考虑多用户的情况,因为解析列表可能是按用户分开的。 另一种更健壮的方法是:在系统构建时,将默认Launcher的包名写入系统属性(例如persist.sys.default_launcher),然后在代码中读取这个属性。 修改为读取系统属性: ```java // 读取系统属性 String defaultLauncherPackage = SystemProperties.get("persist.sys.default_launcher"); if (defaultLauncherPackage != null && !defaultLauncherPackage.isEmpty()) { // 遍历解析列表... } ``` 这样,我们就可以在设备启动时通过设置系统属性来指定默认Launcher。 注意:这种方法需要系统签名,因为ResolverActivity是系统级应用。 另外,我们还需要处理用户手动切换Launcher的情况。如果用户手动切换了Launcher,那么系统属性可能会被覆盖?实际上,系统属性是持久化的,但用户选择后,系统会记住用户的选择。所以,我们这种方法实际上是强制设置一个默认的,即使用户没有选择。因此,这种方法适用于企业设备或者固定用途设备。 如果希望用户仍然可以选择,但第一次启动时不弹出选择器,我们可以这样:只在第一次启动时(即没有设置过默认Launcher时)使用我们的默认设置。但是,ResolverActivity本身并不记录是否是第一次,所以我们需要在设置中记录。 更常见的做法是:在系统第一次启动时,通过PackageManagerService设置默认的Launcher。这通常在设备出厂时完成。 由于时间关系,我们这里先提供修改ResolverActivity的方法,因为它相对简单。 但是,请注意:在Android 14中,ResolverActivity可能已经改名为ResolveActivity,或者逻辑被移动到其他类中。因此,我们需要根据实际代码调整。 另外,引用[2]中提到,可以通过调用设置Launcher接口来设置默认Launcher。具体来说,我们可以使用以下代码: ```java PackageManager pm = context.getPackageManager(); ComponentName cn = new ComponentName("com.example.my.launcher", "com.example.my.launcher.MainActivity"); pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN); filter.addCategory(Intent.CATEGORY_HOME); filter.addCategory(Intent.CATEGORY_DEFAULT); pm.addPreferredActivity(filter, IntentFilter.MATCH_CATEGORY_EMPTY, new ComponentName[]{cn}, cn); ``` 但是,这段代码需要系统权限,并且只能在系统应用中使用。我们可以在系统启动时(例如在SystemUI中)执行这段代码来设置默认Launcher。 然而,用户的问题是如何在系统级设置,因此修改系统源码是必要的。 综合以上,我们提供两种方案: 方案一(修改ResolverActivity): 修改ResolverActivity,在出现多个Launcher时,优先使用我们指定的默认Launcher(通过系统属性配置包名),并直接启动它,跳过选择界面。 方案二(在系统启动时设置默认Launcher): 在系统启动的某个阶段(例如SystemServer中),通过PackageManagerService设置默认Launcher。这需要系统权限,并且需要知道Launcher的ComponentName。 由于方案二更彻底,且符合出厂设置,我们推荐方案二。 方案二具体步骤: 1. 在系统启动过程中,在PackageManagerService初始化完成后,设置默认Launcher。 2. 我们可以创建一个系统服务(或者修改SystemServer)来执行这个操作。 但是,在Android 14中,设置默认Home应用的标准方法是使用RoleManager。在AOSP中,RoleManager是管理角色(如默认浏览器、短信应用、Launcher等)的系统服务。我们可以通过RoleManager来设置默认Launcher。 在系统应用中使用RoleManager的示例: ```java RoleManager roleManager = (RoleManager) context.getSystemService(Context.ROLE_SERVICE); if (roleManager.isRoleAvailable(RoleManager.ROLE_HOME)) { if (!roleManager.isRoleHeld(RoleManager.ROLE_HOME, myComponentName.getPackageName())) { Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_HOME); // 由于是系统应用,我们可以直接赋予角色,不需要用户交互 roleManager.addRoleHolderAsUser(RoleManager.ROLE_HOME, myComponentName, 0, UserHandle.SYSTEM); } } ``` 但是,addRoleHolderAsUser是系统API,需要系统权限。而且,我们可以在系统启动时由系统应用(如SystemUI)来执行这个操作。 然而,在系统启动时,我们可能还没有用户交互,所以需要等待用户解锁?或者,我们可以直接在SystemServer中设置,但SystemServer没有Context。 实际上,我们可以通过SystemServer启动一个服务来执行这个操作。 考虑到这些复杂性,对于定制ROM,我们可以在SystemUI启动时设置默认Launcher(因为SystemUI具有系统权限)。 具体步骤(在SystemUI中设置): 1. 在SystemUI的某个初始化点(例如,在BootComplete之后),检查当默认Launcher是否是我们想要的,如果不是,则设置它。 2. 在SystemUI的代码中,添加以下代码: ```java // 在合适的时机,例如在SystemUIApplication的onCreate()的某个阶段 new Thread(() -> { // 等待系统稳定 SystemClock.sleep(10000); // 等待10秒 setDefaultLauncher(); }).start(); private void setDefaultLauncher() { RoleManager roleManager = (RoleManager) getSystemService(Context.ROLE_SERVICE); if (roleManager == null) return; ComponentName defaultLauncher = new ComponentName("com.example.my.launcher", "com.example.my.launcher.MainActivity"); // 检查当默认Launcher String currentDefaultHome = roleManager.getRoleHolders(RoleManager.ROLE_HOME).get(0); if (currentDefaultHome != null && currentDefaultHome.equals(defaultLauncher.getPackageName())) { return; } // 请求设置默认Launcher roleManager.addRoleHolderAsUser(RoleManager.ROLE_HOME, defaultLauncher, 0, UserHandle.SYSTEM); } ``` 注意:addRoleHolderAsUser需要系统权限,因此SystemUI必须具有系统签名。 但是,在Android 10及以上,RoleManager的addRoleHolderAsUser方法需要系统权限(android.permission.MANAGE_ROLE_HOLDERS),这个权限只授予系统签名应用。 因此,我们可以在SystemUI的AndroidManifest.xml中添加这个权限: ```xml <uses-permission android:name="android.permission.MANAGE_ROLE_HOLDERS" /> ``` 然后,在SystemUI的代码中执行上述操作。 但是,这种方法需要修改SystemUI,并且可能不是最标准的做法。 标准做法:在设备出厂时,通过预置默认Launcher,并在系统配置中指定默认Launcher。 在AOSP中,我们可以通过overlay来覆盖默认的配置。具体来说,在framework/base/core/res/res/values/config.xml中,有一个配置项: ```xml <string name="default_home" translatable="false">com.android.launcher3/com.android.launcher3.Launcher</string> ``` 我们可以通过overlay机制,将这个值覆盖为我们自定义Launcher的ComponentName。 例如,在overlay的config.xml中: ```xml <string name="default_home" translatable="false">com.example.my.launcher/com.example.my.launcher.MainActivity</string> ``` 这样,系统在启动时会自动将这个Launcher设置为默认。 因此,最推荐的方法是:使用overlay覆盖config_default_home的值。 步骤: 1. 在AOSP中创建一个overlay项目(例如packages/overlays/MyLauncherOverlay)。 2. 在该项目的res/values/config.xml中,添加: <resources> <string name="config_default_home" translatable="false">com.example.my.launcher/com.example.my.launcher.MainActivity</string> </resources> 3. 将该项目添加到产品配置中(例如在device.mk中添加PRODUCT_PACKAGE_OVERLAYS += packages/overlays/MyLauncherOverlay)。 这样,编译系统后,系统就会使用自定义的Launcher作为默认启动器。 总结: 推荐使用overlay覆盖config_default_home的方法,因为这是AOSP的标准做法,无需修改Java代码,且稳定可靠。 因此,用户的问题“设置Android 14自定义Launcher为默认启动器”的答案是: **通过overlay覆盖config_default_home配置项,将其值设置为自定义Launcher的ComponentName。** 具体步骤: 1. 创建overlay项目,目录结构如下: MyLauncherOverlay/ ├── Android.mk └── res/ └── values/ └── config.xml 2. 在config.xml中写入: <?xml version="1.0" encoding="utf-8"?> <resources> <string name="config_default_home">com.example.my.launcher/com.example.my.launcher.MainActivity</string> </resources> 3. Android.mk内容: LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := MyLauncherOverlay LOCAL_MODULE_TAGS := optional LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res LOCAL_PACKAGE_NAME := MyLauncherOverlay LOCAL_SDK_VERSION := current LOCAL_MODULE_PATH := $(TARGET_OUT)/overlay/MyLauncherOverlay include $(BUILD_PACKAGE) 4. 将overlay添加到产品配置中(在设备的device.mk或类似文件中): PRODUCT_PACKAGE_OVERLAYS += packages/overlays/MyLauncherOverlay 重新编译系统并刷机,即可生效。 注意:自定义Launcher必须已经预置到系统中(在/system/priv-app/下),并且具有系统签名。 如果用户需要动态切换,那么可能需要结合RoleManager,但出厂设置默认推荐overlay方式。 因此,我们最终推荐使用overlay方式。</think>### 在 Android 14 中设置自定义 Launcher 为默认启动器 #### 核心原理 Android 系统通过 `ResolverActivity` 处理多个 Launcher 的选择逻辑。当系统检测到多个应用声明了 `CATEGORY_HOME` 时,会弹出选择界面。要设置默认启动器,需修改系统源码过滤非默认 Launcher 的选项[^3][^5]。 #### 实现步骤 1. **修改 AndroidManifest.xml** 在自定义 Launcher 的 MainActivity 中添加必要的 intent-filter: ```xml <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> <!-- 关键声明 --> <category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> ``` [^4] 2. **修改 ResolverActivity.java** 在 `frameworks/base/core/java/com/android/internal/app/ResolverActivity.java` 的 `rebuildList()` 方法中添加过滤逻辑: ```java // 文件路径: // frameworks/base/core/java/com/android/internal/app/ResolverActivity.java private void rebuildList() { // ... 原有代码 final String DEFAULT_LAUNCHER = "com.example.mylauncher"; // 替换为你的包名 // 过滤非默认Launcher Iterator<ResolvedComponentInfo> iterator = rciList.iterator(); while (iterator.hasNext()) { ResolvedComponentInfo info = iterator.next(); ActivityInfo ai = info.getResolveInfoAt(0).activityInfo; if (!DEFAULT_LAUNCHER.equals(ai.packageName)) { iterator.remove(); // 移除非默认Launcher } } // 如果只剩一个Launcher,直接启动 if (rciList.size() == 1) { onTargetSelected(rciList.get(0), false); finish(); return; } // ... 后续原有代码 } ``` [^3][^5] 3. **预置自定义 Launcher 到系统分区** 将自定义 Launcher 编译进系统镜像: ```makefile # 在设备mk文件中添加 PRODUCT_PACKAGES += MyCustomLauncher ``` 4. **重新编译系统** ```bash mmm frameworks/base mmm packages/apps/MyCustomLauncher make snod ``` #### 验证方法 1. 设备重启后,按 Home 键应直接进入自定义 Launcher 2. 检查当默认 Launcher: ```bash adb shell cmd role get-default-holders --user 0 ``` 应输出你的 Launcher 包名 #### 注意事项 1. **签名要求**:自定义 Launcher 必须使用系统签名 2. **多用户支持**:需在 `rebuildList()` 中添加多用户处理逻辑 3. **系统更新**:修改系统核心文件需重新编译 ROM 4. **兼容性**:Android 14 的 RoleManager 机制可能影响设置,需确保在 `rebuildList()` 中优先处理[^4] > 此方法适用于 AOSP 或可定制 ROM,普通应用无法直接设置默认启动器。系统级修改需要完整编译环境支持[^1][^2]。
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值