多种方式实现动态替换Android默认桌面Launcher

文章目录

  • 多种方式实现动态替换Android默认桌面Launcher
    • 背景简介
    • 技术方案
      • 三种方案
        • 方案一
        • 方案二
        • 方案三
    • 风险

背景简介

Launcher-是安卓系统中的桌面启动器,安卓系统的桌面UI统称为Launcher。Launcher是安卓系统中的主要程序组件之一,安卓系统中如果没有Launcher就无法启动Android

  • 当前场景
    现在安卓设备应用越来越广泛,有些出厂前可能没有预留系统OTA升级的入口,但是有app的升级通道,而又在一些情况下需要更改已经出厂的系统默认桌面,所以针对这种情况需要实现一种方案,在不进行系统升级更改的情况下,只通过升级App的方式,实现动态替换默认Launcher的方案

技术方案

三种方案

  • 用代码设置系统配置,保留两个Launcher应用进行动态切换

  • 动态删除添加系统默认/system/priv-app/目录下的默认Launcher.apk文件

  • 不设置Launcher,通过获取系统底层对Activity状态的监控,拦截Launcher

前三种方案都需要系统权限,并在AndroidManifest.xml 中配置Launcher属性

  • 系统权限,配置shareUserId,并且用系统签名文件进行签名
      
  • 新增Launcher属性
                                                                                                      

方案一

使用代码配置动态Launcher,需要额外的添加一个系统级别的权限

    

该权限允许应用程序,进行系统属性的配置更改,有了该权限后,才可以进行动态的属性配置。


清除默认Launcher:

  1. 安装运行自定义Launcher应用
  2. 遍历所有配置Launcher属性的应用
  3. 清除除所有应用Launcher属性
  4. 添加自定义桌面应用Launcher属性配置
    private void clear(Context context){              ArrayList intentList = new ArrayList<>();        ArrayList cnList = new ArrayList<>();        context.getPackageManager().getPreferredActivities(intentList, cnList, null);        for(int i = 0; i < cnList.size(); i++) {            IntentFilter dhIF = intentList.get(i);            if(dhIF.hasAction(Intent.ACTION_MAIN) && dhIF.hasCategory(Intent.CATEGORY_HOME)) {//遍历过滤所有launcher                try {                    String name = cnList.get(i).getPackageName();             //清除原有的默认launcher                              context.getPackageManager().clearPackagePreferredActivities(name;                }catch (Exception ex){                }            }        }        IntentFilter filter = new IntentFilter();        filter.addAction(Intent.ACTION_MAIN);        filter.addCategory(Intent.CATEGORY_HOME);        filter.addCategory(Intent.CATEGORY_DEFAULT);        final int N = mHome.size();        ComponentName[] set = new ComponentName[N];        for (int i = 0; i < N; i++) {            ResolveInfo r = mHome.get(i);            set[i] = new ComponentName(r.activityInfo.packageName,r.activityInfo.name);        }        //设置自定义launcher        ComponentName launcher = new ComponentName(hiBoxLauncher, hiBoxActivity);        context.getPackageManager().addPreferredActivity(filter, 1081344, set, launcher);    }

恢复默认Launcher方法同上一样,清除完所有Launcher相关的意图配置,最后添加上系统Launcher包名。

方案二

通过文件删除的方式进行Launcher替换,需要文件的读写权限,而且系统应用删除后需要重启才能生效,所以还需要reboot的权限

            

清除默认Launcher:

  1. 安装运行自定义Launcher应用
  2. 复制/system/priv-app/Launcher2.apk保存到sdcrad
  3. 删除/system/priv-app/Launcher2.apk
  4. 重启安卓系统,只留下自定义Launcher应用生效

恢复默认launcher只需要将事先保存的apk,拷贝到系统目录即可,当前系统会存在两个Launcher,需要手动选择一次默认Launcher

方案三

不需要替换Launcher,通过反射的方式注册系统android.app.
IActivityController的隐藏aidl回调。监听每个应用的Activity界面的状态,当回调系统显示Launcher的时候拦截回调,打开自定义的应用界面,覆盖系统Launcher,需要系统权限,还需要额外的Activity设置权限

    

1.声明aidl文件
在工程中声明一个android.app.IActivityController的aidl,用于注册系统的回调,可以直接从Android源码中拷贝

    interface IActivityController{      boolean activityStarting(in Intent intent, String pkg);        boolean activityResuming(String pkg);        boolean appCrashed(String processName, int pid,            String shortMsg, String longMsg,            long timeMillis, String stackTrace);    int appEarlyNotResponding(String processName, int pid, String annotation);    int appNotResponding(String processName, int pid, String processStats);}

2.反射注册aidl回调代码:

     //在进入页面时调用setActivityController()方法,注册aidl回调,当界面有变化时会触发activityStarting(),activityResuming()事件    public void setActivityController() {        Log.d(TAG,">>>>>>0519,");        try {            Class<?> cActivityManagerNative = Class.forName("android.app.ActivityManagerNative");            Method mGetDefault = cActivityManagerNative.getMethod("getDefault", new Class[]{});            Object oActivityManagerNative = mGetDefault.invoke(null, new Object[]{});            Method mSetActivityController = cActivityManagerNative.getMethod("setActivityController",                    Class.forName("android.app.IActivityController"));            mSetActivityController.invoke(oActivityManagerNative, new ActivityController());        } catch (ClassNotFoundException e) {            exceptionWhenSet(e);        }

3.拦截Launcher
绑定服务,获取系统回调的Activity状态接口,进行应用打开操作

    private class ActivityController extends android.app.IActivityController.Stub {        public boolean activityStarting(Intent intent, String pkg) {                        if(launcher2.equals(pkg)&&isFilter){            startHiBoxLauncher();            }            return true;        }        public boolean activityResuming(String pkg) {            Log.d(TAG,">>>>>>0519,pkg=" + pkg);            if(launcher2.equals(pkg)&&isFilter){                startHiBoxLauncher();            }            return true;        }    }

如果是管理员进行操作,需要恢复Launcher,可以用过标志位Boolen值来控制是否拦截系统Launcher

风险

  • 1.重启安卓设备Launcher配置重置,方案一代码设置生效后,不需要手动进行选择,可以同时在两个launcher中切换,但是测试发现,安卓设备重启后,会清除配置,需要重新选择默认Launcher
    多种方式实现动态替换Android默认桌面Launcher_第1张图片

  • 2.Android系统应用无法删除,在一些设备上测试发现,即使有了系统权限,只能对/system/priv-app/Launcher2.apk进行拷贝操作,无法进行删除操作。

  • 3.签名不一致覆盖安装风险,所有的方案都需要重新系统签名,获取系统权限,跟之前Android应用签名不一致,可能会出现覆盖安装失败的问题

更多相关文章

  1. Android震动系统结构 移植与调试的要点
  2. Android 输入系统之EventHub篇
  3. android 系统核心机制binder(01)C语言简谈binder
  4. Android 6.0+新的运行时权限 开发者需要知道的一切
  5. 【Android】Android输入子系统
  6. android电池管理系统
  7. 打造android ORM框架opendroid(七)——数据库升级方案

随机推荐

  1. 最近下载了ADT Version 22发现里面多了个
  2. android studio 新建class文件报错Unable
  3. Android(安卓)结束进程的方法
  4. Android(安卓)多次点击事件的触发方法
  5. android 常用第三方插件收藏
  6. Android的冷启动优化
  7. android存取数据(基础)
  8. 从屏幕底部弹出PopupWindow
  9. Android自定义弹窗效果
  10. Android(安卓)Design Support Library之T