如何检查活动是不是启用?

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.settingspm 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),但使用“包上下文”-“类”它可以工作。

以上是关于如何检查活动是不是启用?的主要内容,如果未能解决你的问题,请参考以下文章

提示后检查用户是不是启用了 GPS

检查是不是在android中启用了GPS

如何检查 iPhone 上是不是启用了通知

如何检查是不是启用了定位服务?

如何检查按钮形状是不是启用? [复制]

Android - 如何检查是不是启用了开发人员选项