Android 10-12系统下隐藏APP或阻止卸载的技术方案咨询
Android 10-12 隐藏式启动器完全隐藏&防卸载解决方案思路
一、彻底隐藏Launcher图标(解决拖拽卸载入口问题)
Android 10+ 对Launcher图标的加载逻辑做了调整,仅禁用主Activity组件已无法完全隐藏图标,需从以下方向处理:
- 禁用所有带LAUNCHER意图过滤器的组件:检查你的
AndroidManifest.xml,凡是声明了<intent-filter>包含android.intent.action.MAIN和android.intent.category.LAUNCHER的组件(包括主Activity、Activity别名<activity-alias>),都要逐个禁用。示例代码:private void hideAllLauncherComponents(Context context) { PackageManager pm = context.getPackageManager(); String packageName = context.getPackageName(); try { PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES | PackageManager.GET_ACTIVITY_ALIASES); // 处理主Activity if (packageInfo.activities != null) { for (ActivityInfo activity : packageInfo.activities) { if (isLauncherComponent(activity)) { ComponentName component = new ComponentName(packageName, activity.name); pm.setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); } } } // 处理Activity别名 if (packageInfo.activityAliases != null) { for (ActivityInfo alias : packageInfo.activityAliases) { if (isLauncherComponent(alias)) { ComponentName component = new ComponentName(packageName, alias.name); pm.setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); } } } // 通知Launcher刷新图标缓存 context.sendBroadcast(new Intent(Intent.ACTION_PACKAGE_CHANGED).setData(Uri.parse("package:" + packageName))); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } } private boolean isLauncherComponent(ActivityInfo info) { if (info.intentFilters == null) return false; for (IntentFilter filter : info.intentFilters) { boolean hasMainAction = filter.hasAction(Intent.ACTION_MAIN); boolean hasLauncherCategory = filter.hasCategory(Intent.CATEGORY_LAUNCHER); if (hasMainAction && hasLauncherCategory) { return true; } } return false; } - 强制刷新Launcher缓存:部分第三方Launcher会缓存图标数据,禁用组件后发送
ACTION_PACKAGE_CHANGED广播,能触发Launcher重新加载应用信息。
二、阻止所有卸载途径
1. 成为默认Launcher并锁定默认设置
- 将你的APP注册为Launcher(在Manifest中声明
android.intent.category.HOME和android.intent.category.DEFAULT的意图过滤器),引导用户设置为默认启动器。当APP是唯一默认Launcher时,系统会阻止普通卸载操作;结合设备所有者权限,调用DevicePolicyManager.setHomePackage()锁定默认Launcher,防止用户切换。
2. 获取设备所有者权限(Device Owner)
设备所有者权限可直接阻止应用卸载,无需root:
- 调用
DevicePolicyManager.setUninstallBlocked()方法,将你的APP包名设为不可卸载:private void blockUninstall(Context context) { DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE); ComponentName adminComponent = new ComponentName(context, YourDeviceAdminReceiver.class); if (dpm.isAdminActive(adminComponent)) { dpm.setUninstallBlocked(adminComponent, context.getPackageName(), true); } } - 注意:设备所有者权限需要用户通过NFC、二维码或企业MDM方式激活,无法在普通安装流程中自动获取。
3. 转为系统级应用(最彻底)
将APP安装到设备的/system/app或/priv-app目录(需设备root或厂商签名),系统级应用默认无法被普通用户卸载,只能通过root权限或恢复出厂设置移除。
三、Android版本适配注意事项
- Android 10:禁用组件后必须发送
ACTION_PACKAGE_CHANGED广播,否则部分原生Launcher仍会显示缓存图标。 - Android 11/12:需声明
android.permission.QUERY_ALL_PACKAGES权限(Android 11)或android.permission.GET_ALL_PACKAGES权限(Android 12),确保Launcher能正确感知组件状态变化;组件启用状态的修改必须在主线程执行。
内容的提问来源于stack exchange,提问作者dsx
相关产品推荐
相关产品推荐

