ContentProvider跨APP读取数据失败

一、问题场景

     现在有两个应用,一个是应用A,另一个是应用B,B是做为插件的形式存在,服务于A,当应用A需要数据时,则通过ContentProvider去拿数据回来。但是现在有个问题,就是在Android版本5.x上,有一个是否允许自启动权限,这个权限会影响到应用A向应用B获取数据。我们先来看看下面几个场景:

  • 场景一:安装应用A,再安装应用B,打开应用B的自启动权限,这个时候激活应用B,这个时候A再去应用B拿数据,则应用A可以顺利拿到数据。这个时候用DDMS去查看B的进程,B肯定是打开的(不是逗你,你都激活了,还能是关闭么。。。)
  • 场景二:安装应用A,再安装应用B,关闭应用B的自启动权限,这个时候激活应用B,这个时候A再去应用B拿数据,则应用A也可以顺利拿到数据,这个时候用DDMS去查看B的进程,肯定是打开的(这也不是逗你。。。)
  • 场景三:安装应用A,再安装应用B,打开应用B的自启动权限,这个时候不去激活B,查看DDMS,确保B没有被激活,应用A向B拿数据,发现可以取到数据,这个时候再去查看DDMS,可以发现B的进程已经被激活了。
  • 场景四:安装应用A,再安装应用B,关闭应用B的自启动权限,这个时候不去激活B,查看DDMS,确保B没有被激活,应用A向B拿数据,发现现在取不到数据了,这个时候在去查看DDMS,发现B的进程没有被激活。此时后台会发现Log:ActivityThread(22430): Failed to find providerinfo for xxx.
由此可以得出,Android中A应用向B应用去通过ContentProvider去获取数据时,是需要先唤醒B应用,在通过B的ContentProvider去获取资源数据,如果此时应用B的自启动权限被取消,则A应用是无法完成读取资源的操作的。

二、问题研究

首先我们先来看一下android源码:

public ProviderInforesolveContentProvider(String name, int flags, int userId) {
       if (!sUserManager.exists(userId)) return null;
       // reader
       synchronized (mPackages) {
           final PackageParser.Providerprovider = mProvidersByAuthority.get(name);
            PackageSetting ps = provider !=null
                    ?mSettings.mPackages.get(provider.owner.packageName)
                    : null;
            return ps != null
                    && <span style="color:#ff0000;">mSettings.isEnabledLPr(provider.info,flags, userId)</span>
                    && (!mSafeMode ||(provider.info.applicationInfo.flags
                            &ApplicationInfo.FLAG_SYSTEM)!= 0)
                    ? PackageParser.generateProviderInfo(provider,flags,
                            ps.readUserState(userId),userId)
                    : null;
       }
   }
 
 
   boolean isEnabledLPr(ComponentInfo componentInfo, int flags, int userId){
       if ((flags&PackageManager.GET_DISABLED_COMPONENTS) != 0) {
            return true;
       }
       final String pkgName = componentInfo.packageName;
       final PackageSetting packageSettings = mPackages.get(pkgName);
       if (PackageManagerService.DEBUG_SETTINGS) {
           Log.v(PackageManagerService.TAG,"isEnabledLock - packageName = "
                    + componentInfo.packageName+ " componentName = " + componentInfo.name);
            Log.v(PackageManagerService.TAG,"enabledComponents: "
                    + compToString(packageSettings.getEnabledComponents(userId)));
            Log.v(PackageManagerService.TAG,"disabledComponents: "
                    +compToString(packageSettings.getDisabledComponents(userId)));
       }
       if (packageSettings == null) {
            return false;
       }
       PackageUserState ustate = packageSettings.readUserState(userId);
       if ((flags&PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS) != 0){
            if (ustate.enabled ==COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
                return true;
            }
       }
       <span style="color:#ff0000;">if (ustate.enabled ==COMPONENT_ENABLED_STATE_DISABLED
                || ustate.enabled ==COMPONENT_ENABLED_STATE_DISABLED_USER
                || ustate.enabled ==COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
                || (packageSettings.pkg != null&& !packageSettings.pkg.applicationInfo.enabled
                    && ustate.enabled== COMPONENT_ENABLED_STATE_DEFAULT)) {
            return false;
       }</span>
       if (ustate.enabledComponents != null
                &&ustate.enabledComponents.contains(componentInfo.name)) {
            return true;
       }
       if (ustate.disabledComponents != null
                &&ustate.disabledComponents.contains(componentInfo.name)) {
            return false;
       }
       return componentInfo.enabled;
   }

也就是说,ActivityThread.acquried->ActivityManagerService.getprovider->PackageManagerService.resolveContentProvider的方法里会判断是否有对应的provider,如果有的话,还要判断provider的是否可以自动运行。因为自启动权限被关闭了,所以也就无法从应用B读取到数据了


三、问题解决

  1. 打开应用B的自启动权限,这是用户自己的决定,如果有必要的话,如果发现当前读不到B数据,则A主动弹出窗口提示用户把B的应用自启动权限打开。当然,不是所有用户都会这么做。
  2. 用反射的方法代替ContentProvider去B应用中获取数据资源ID,也就是说,插件包要对外公开出一个资源文件的名字,而这个资源文件的名字保函了你需要读取的资源的ID,我们通过读取这个资源文件,解析出我们要的资源ID,在通过这个包的上下文去取到这个资源即可。

四、总结

由以上的研究可以看出,这个是Android机制上的问题,应用Android 5.0之后才将自启动权限交给用户去管理,因此,至少在应用层上面,我们是没有办法绕过这个权限,去关联启动这个应用的,因此,只能绕过ContentProvider去读取数据。从一个应用读取另一个应用的数据,还要不启动目标应用,这里想到的方法就是反射了。



### 回答1: 一般情况下,安卓 app 无法直接读取其他 app数据。但是,如果其他 app 提供了 API 或者内容提供器,那么它可以通过 API 或者内容提供器读取数据。此外,如果两个 app 都是由同一个开发者开发,那么它们可以通过内部存储或共享数据的方式读取数据。 ### 回答2: 安卓系统通过应用程序间的权限机制保护用户数据的隐私和安全,禁止应用程序直接读取其他应用程序的数据。这样的设计可以避免潜在的信息泄露和滥用。然而,有时候确实需要应用程序之间进行数据共享。 在一些特定的情况下,安卓系统提供了一些方式用于应用程序之间的数据交互。例如,应用程序可以使用ContentProvider机制来共享数据ContentProvider允许一个应用程序提供一组标准化的接口,供其他应用程序进行数据检索和修改。通过在Manifest文件中声明权限,只有经过授权的应用程序才能访问ContentProvider。 另外,安卓系统还提供了Intent机制来实现应用程序之间的数据传递。通过发送和接收Intent,应用程序可以在启动其他应用程序时传递需要共享的数据。 总的来说,安卓系统限制了应用程序之间的数据访问权限,以保护用户数据的安全。然而,在有特殊需求的情况下,安卓系统提供了一些机制用于应用程序之间的数据共享,如ContentProvider和Intent。需要注意的是,在进行数据共享时需要保证数据的安全性和权限控制,以保护用户的隐私。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值