如何检查活动是不是启用?
Posted
技术标签:
【中文标题】如何检查活动是不是启用?【英文标题】:How to check if an Activity is enabled?如何检查活动是否启用? 【发布时间】:2015-01-13 09:38:40 【问题描述】:背景
我正在尝试检查是否在运行时启用/禁用了活动(或任何其他应用组件类型)。
问题
可以使用下一个代码:
final ComponentName componentName = new ComponentName(context, activityClass);
final PackageManager pm = context.getPackageManager();
final int result = pm.getComponentEnabledSetting(componentName);
但是返回的结果,写在the documentation是:
返回组件的当前启用状态。可能是其中之一 COMPONENT_ENABLED_STATE_ENABLED、COMPONENT_ENABLED_STATE_DISABLED 或 COMPONENT_ENABLED_STATE_DEFAULT。最后一个表示组件的 启用状态基于清单中的原始信息,如 在 ComponentInfo 中找到。
所以它不仅仅是启用/禁用,而且是“默认”。
问题
如果返回“COMPONENT_ENABLED_STATE_DEFAULT”,我如何知道它是默认启用还是禁用(在运行时)?
这个问题的原因是无论人们在清单中放入什么代码都应该工作(对于“启用”属性)。
是否可以使用意图解析?
【问题讨论】:
您想到了哪些“其他应用程序组件”?默认情况下禁用其中的任何一个? @user3249477 API 声明您可以对其他应用程序组件执行此检查:“活动、接收者、服务、提供者”。检查此链接:developer.android.com/reference/android/content/pm/… 您找到解决方案了吗?还有enabled
字段似乎代表清单值。但是,如果封装应用程序被禁用,那么它会将字段修改为 false。
@CoryCharlton 不。也许我应该悬赏?
你有没有试过getActivityInfo()
之类的,给他们打电话ComponentInfo.isEnabled()
?它说返回此组件及其封闭应用程序是否已启用。
【参考方案1】:
如果返回
COMPONENT_ENABLED_STATE_DEFAULT
,我如何知道它是默认启用还是禁用?
您需要使用 PackageManager 加载所有组件并检查匹配 ComponentInfo 的启用状态。以下代码应该可以工作:
public static boolean isComponentEnabled(PackageManager pm, String pkgName, String clsName)
ComponentName componentName = new ComponentName(pkgName, clsName);
int componentEnabledSetting = pm.getComponentEnabledSetting(componentName);
switch (componentEnabledSetting)
case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
return false;
case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
return true;
case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
default:
// We need to get the application info to get the component's default state
try
PackageInfo packageInfo = pm.getPackageInfo(pkgName, PackageManager.GET_ACTIVITIES
| PackageManager.GET_RECEIVERS
| PackageManager.GET_SERVICES
| PackageManager.GET_PROVIDERS
| PackageManager.GET_DISABLED_COMPONENTS);
List<ComponentInfo> components = new ArrayList<>();
if (packageInfo.activities != null) Collections.addAll(components, packageInfo.activities);
if (packageInfo.services != null) Collections.addAll(components, packageInfo.services);
if (packageInfo.providers != null) Collections.addAll(components, packageInfo.providers);
for (ComponentInfo componentInfo : components)
if (componentInfo.name.equals(clsName))
return componentInfo.isEnabled();
// the component is not declared in the AndroidManifest
return false;
catch (PackageManager.NameNotFoundException e)
// the package isn't installed on the device
return false;
在我的设备上测试上述代码:
System.out.println(isComponentEnabled(getPackageManager(),
"com.android.systemui",
"com.android.systemui.DessertCaseDream"));
System.out.println(isComponentEnabled(getPackageManager(),
"com.android.settings",
"com.android.settings.DevelopmentSettings"));
假
是的
【讨论】:
这与我之前使用的代码很接近。问题是componentInfo.enabled
不准确。请参阅android.googlesource.com/platform/frameworks/base/+/ee0b3e9/… 上的enabled
字段的注释对我来说,问题是当我禁用应用程序时,某些ComponentInfo
对象的enabled
字段被设置为false(特别是ServiceInfo
)。从技术上讲,这是一个准确的答案,因为如果应用程序被禁用,组件将无法运行,但不是我们(或我)提出的问题。
@CoryCharlton 这对我来说是准确的。我一直在一个拥有数百万用户的应用程序中使用非常相似的东西。你试过了吗?如果您可以重现该错误,那么我将对其进行调查并更新我的答案。
刚刚尝试了您的代码,它为“com.android.settings.DevelopmentSettings”返回 true/true,但为“com.android.settings.bluetooth.DockService”返回 true/false。第二个结果是在有根的 Android 4.4.4、API 19 设备上从 shell 执行 pm disable com.android.settings
或 pm disable-user com.android.settings
之后。
@CoryCharlton 你的意思是它还应该检查应用程序本身是否被禁用?
@androiddeveloper 不,我是说在某些情况下,enabled
字段似乎考虑了应用程序的启用状态,这不是我期望或想要的行为。【参考方案2】:
我认为字段ComponentInfo.enabled
表示AndroidManifest.xml 文件中设置的值。如果未指定,则默认值为 true。所以,在下面的例子中:
<receiver android:name=".BroadcastReceiver1"
android:enabled="false" />
<receiver android:name=".BroadcastReceiver2"
android:enabled="true" />
<receiver android:name=".BroadcastReceiver3" />
enabled
字段在 BroadcastReceiver1 中为 false,在 BroadcastReceiver2 中为 true,在 BroadcastReceiver3 中为 true,而 pm.getComponentEnabledSetting(componentName)
将返回 ENABLED如果 AndroidManifest 中的值被覆盖,则为 DISABLED;如果未覆盖,则为 DEFAULT
然后,根据@Jared Rummler 的回答,如果pm.getComponentEnabledSetting(componentName)
返回PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
,实际值将是ComponentInfo.enabled
字段中设置的值,这将与AndroidManifest 中的设置相同,而ComponentInfo.isEnabled()
将取决于整个应用状态
编辑:总结一下:
public static boolean isComponentEnabled(PackageManager pm, String pkgName, String clsName)
ComponentName componentName = new ComponentName(pkgName, clsName);
int componentEnabledSetting = pm.getComponentEnabledSetting(componentName);
switch (componentEnabledSetting)
case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
return false;
case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
return true;
case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
default:
// We need to get the application info to get the component's default state
try
PackageInfo packageInfo = pm.getPackageInfo(pkgName, PackageManager.GET_ACTIVITIES
| PackageManager.GET_RECEIVERS
| PackageManager.GET_SERVICES
| PackageManager.GET_PROVIDERS
| PackageManager.GET_DISABLED_COMPONENTS);
List<ComponentInfo> components = new ArrayList<>();
if (packageInfo.activities != null) Collections.addAll(components, packageInfo.activities);
if (packageInfo.services != null) Collections.addAll(components, packageInfo.services);
if (packageInfo.providers != null) Collections.addAll(components, packageInfo.providers);
for (ComponentInfo componentInfo : components)
if (componentInfo.name.equals(clsName))
return componentInfo.enabled; //This is the default value (set in AndroidManifest.xml)
//return componentInfo.isEnabled(); //Whole package dependant
// the component is not declared in the AndroidManifest
return false;
catch (PackageManager.NameNotFoundException e)
// the package isn't installed on the device
return false;
【讨论】:
@androiddeveloper 我会建议(我实际上正在使用它)Jared Rummler 发布的代码,但有一个小改动:而不是 'componentInfo.isEnabled();' 使用 'componentInfo.enabled;'。查看编辑... 等等,isEnabled 在他的回答中被错误地使用了?在这种情况下不应该使用它吗? @androiddeveloper 这取决于您认为组件(启用/禁用)的功能。当应用程序本身被禁用时,如果您认为所有应用程序组件都被禁用,请使用 isEnabled() 哦,所以 componentInfo.enabled 仅适用于应用程序中的单个组件,而 componentInfo.isEnabled() 则适用于整个应用程序?!多么奇怪! @androiddeveloper 通过使用 PackageManager 获取应用程序的 ApplicationInfo 并读取 ApplicationInfo.enabled 字段【参考方案3】:Jared Rummler 的代码很好,它可以工作,但它需要清单中的状态。
要在我的应用程序中获得组件的当前状态,我需要通过“包上下文”和“组件类”而不是“包名称”和“类名称”来创建“组件名称”。
我在清单中将 BroadcastReceiver 设置为“enabled = false”,之后我使用包管理器在我的应用程序中启用它,但 Jared Rummler 的代码总是返回我“STATE_ENABLED_DEFAULT”和“启用和 isEnabled()”总是返回假。 使用“Package Context”和“Component Class”我直接得到“ENABLED_STATE_DISABLED”和“ENABLED_STATE_ENABLED”而不使用默认部分中的代码,如果我已经开始,“enabled and isEnabled()”也会返回 FALSE接收者使用包管理器。
希望对你有用,看你
public static void enableDisableComponent(Context pckg, Class componentClass, boolean enable)
ComponentName component = new ComponentName(pckg, componentClass);
if(enable == !checkIfComponentIsEnabled(pckg, componentClass))
pckg.getPackageManager().setComponentEnabledSetting(
component,
enable ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP
);
public static boolean checkIfComponentIsEnabled(Context pckg, Class componentClass)
boolean ret = false;
ComponentName componentName = new ComponentName(pckg, componentClass);
int componentEnabled = pckg.getPackageManager().getComponentEnabledSetting(componentName);
switch(componentEnabled)
case PackageManager.COMPONENT_ENABLED_STATE_DISABLED:
break;
case PackageManager.COMPONENT_ENABLED_STATE_ENABLED:
ret = true;
break;
case PackageManager.COMPONENT_ENABLED_STATE_DEFAULT:
default:
ret = checkEnabledComponentsInfo(pckg, componentClass);
break;
return ret;
private static boolean checkEnabledComponentsInfo(Context pckg, Class componentClass)
boolean ret = false;
try
PackageInfo packageInfo = pckg.getPackageManager().getPackageInfo(
pckg.getPackageName(),
PackageManager.GET_ACTIVITIES | PackageManager.GET_RECEIVERS |
PackageManager.GET_SERVICES | PackageManager.GET_PROVIDERS |
PackageManager.GET_DISABLED_COMPONENTS
);
List<ComponentInfo> componentsInfo = new ArrayList<>();
if(packageInfo.activities != null && packageInfo.activities.length > 0)
Collections.addAll(componentsInfo, packageInfo.activities);
if(packageInfo.services != null && packageInfo.services.length > 0)
Collections.addAll(componentsInfo, packageInfo.services);
if(packageInfo.providers != null && packageInfo.providers.length > 0)
Collections.addAll(componentsInfo, packageInfo.providers);
if(packageInfo.receivers != null && packageInfo.receivers.length > 0)
Collections.addAll(componentsInfo, packageInfo.receivers);
if(componentsInfo.size() > 0)
for(ComponentInfo info : componentsInfo)
if(info.name.equals(componentClass.getName()))
ret = info.isEnabled();
break;
catch(PackageManager.NameNotFoundException nnfE)
onException(nnfE);
return ret;
【讨论】:
添加,如果我通过 PackageManager 启用我的接收器,“启用”和“isEnabled”仍然返回“false”,但如果我启用/禁用第一个开关返回我“STATE_ENABLED”或“STATE_DISABLED”它通过 PackageManager。 谢谢。如果我们对不是当前应用的应用执行此操作,您能否还举例说明如何使用它? 我认为如果您需要在不是当前应用程序的应用程序中使用它,您需要使用 Jared 所写的“包名”和“类名”。 我不明白。所以一个好的做法是将他的功能用于其他应用程序,如果它是当前应用程序,你的? 我想是的,如果在另一个应用程序中,我没有尝试使用上下文案例,但我认为它不会工作,因为对其他应用程序上下文的访问受限(来自你的应用程序的上下文看不到另一个应用程序包的组件)。在我的情况下,当我的应用程序关闭以启动服务以扫描 BLE 信标时,我使用 PackageManager 启用 BluetoothAdapterReceiver 以捕获“BLUETOOTH_ENABLED”事件。在我的情况下,使用“包名称”-“类名称”检查对我不起作用(始终转到开关的默认值并启用 false),但使用“包上下文”-“类”它可以工作。以上是关于如何检查活动是不是启用?的主要内容,如果未能解决你的问题,请参考以下文章