【问题标题】:Dynamically set the authority of a ContentProvider动态设置 ContentProvider 的权限
【发布时间】:2013-04-28 21:30:13
【问题描述】:

也许标题有点误导。我的问题是我有一个 Android 库项目,它在两个标准 Android 项目之间共享:一个用于应用程序的免费版本,另一个用于付费版本。该库当前具有 ContentProvider 的代码,包括一个带有几个静态字符串变量的合同类,用于诸如 URI 和列名之类的内容。现在我希望 URI 的“权限”根据使用库的应用程序进行更改。想到的一种解决方案是将权限存储为字符串资源,并在运行时将该字符串加载到静态最终字符串变量中。但是,我不确定如何执行此操作,因为合同类有一个私有构造函数并且没有 Context 对象来加载字符串资源。还有哪些其他方法可以解决我的问题?

【问题讨论】:

  • 您是否愿意检查我的回答并让我知道它是否回答了您的问题或是否有问题?
  • @EmanuelMoecklin 我升级了你的 A,但自从我问了这个 Q 以来,我还没有时间玩我的应用程序。这是一个爱好应用程序,工作一直在阻碍;-( 我一旦我有机会找出哪个最有帮助,我会接受答案。

标签: java android android-contentprovider


【解决方案1】:

对于那些使用较新版本的构建工具的人来说,这是一个更好的解决方案:使权限与您的应用程序 ID 相关。您可以使用${applicationId} 自动执行此操作,它会在构建过程中扩展为您应用的应用程序 ID。

<provider
    android:name=".MyContentProvider"
    android:authorities="${applicationId}.provider"/>

假设您的应用程序 ID 是 com.example.app.paidcom.example.app.free。当您构建您的应用程序时,权限将分别变为com.example.app.paid.providercom.example.app.free.provider

要在代码中引用提供者权限,请使用BuildConfig.APPLICATION_ID + ".provider"

【讨论】:

  • 感谢 Android Studio 和 gradle 构建链提供的功能更新。
  • 另外,我如何从付费应用中引用免费应用的提供者。这是两个不同提供商的主要原因之一。当用户购买付费应用时,我需要能够从免费应用中导入数据。
  • @Code-Apprentice 标准做法是让付费升级成为应用内购买,这样您就可以继续使用同一个提供商。或者,您可以将您的提供程序标记为android:exported="true",这允许其他应用程序读取您的提供程序数据(请记住添加权限,以便只有您的应用程序可以读取它!)
  • 从这个问题的时间戳可以看出,我已经研究了一段时间。我找到了一个当时似乎可行的解决方案,但我不知道应用内购买,否则我可能会走那条路。我发现使用相同密钥签名的任何应用程序都可以自动使用提供程序。由于我只想在自己的应用程序之间使用它们,因此不需要导出提供程序。
  • 当前的问题是在 付费 应用中提供免费应用提供者的名称。当然,我可以对其进行硬编码,但如果有一些更动态的东西会很好。我有一个“合同”类,我想在其中使用提供者名称的字符串常量。
【解决方案2】:

在用户尝试安装这两个版本的情况下,对免费和付费版本使用不同的权限是有意义的。 我正在为清单中的两个版本定义不同的权限,如下所示:

<provider
    android:name="MyApp.MyProvider"
    android:authorities="MyApp.MyProvider.free"
    android:grantUriPermissions="true"/>

然后我在一个xml文件中配置provider(我使用了一个特殊的config.xml文件,因为我有更多的provider权限之类的配置数据,不过你当然可以使用strings.xml):

<string name="my_provider_authority">MyApp.MyProvider.free</string>

代码检索提供者权限作为任何其他字符串资源。要在没有上下文的情况下访问字符串资源,请使用应用程序上下文。我正在使用应用程序类从我的应用程序中的任何位置访问应用程序上下文(但有两个例外):

public class MyApplication extends Application {
    private static Context sContext;

    @Override
    public void onCreate() {
        super.onCreate();
        sContext = this;
    }

    public static Context getContext() {
        return sContext;
    }
}

当然,您需要在清单中定义 MyApplication。 这允许您从应用程序中的任何位置访问字符串和其他资源。 不过有两个例外:

  1. 内容提供者。 ContentProviders 可以在应用程序启动之前启动,因此您将没有可用的应用程序上下文。不过这没问题,因为 ContentProviders 通过 getContext() 获取自己的上下文。
  2. 静态代码:上下文可能在 Android 组件(活动、片段、广播接收器、服务等)的生命周期之外不可用。因此,依赖于应用程序上下文的静态初始化器不是一个好主意。但这也不是一个真正的问题,因为无论如何都不允许在 Android 组件的生命周期之外使用上下文,并且总是会在该生命周期内调用访问上下文的静态方法。例如。如果 Activity 需要知道 ContentProvider 的权限,它将调用您的合约类中的静态方法,并且该调用将来自 Activity 的 onXYZ() 方法之一,例如 onCreate() 或 onStart() ,这将确保上下文已初始化.因此,您需要做的就是延迟初始化合约类中的变量,并确保调用者只有在明确之前调用过 Application.onCreate() 时才检索变量。当然,您可以从活动中直接检索字符串资源。当您需要其他类/对象中的资源时,我的方法的真正优势将变得显而易见。这些对象仍将与某些 Android 组件的生命周期相关联,但您不必将上下文传递给所有这些对象,这 1)非常麻烦,2)在泄漏上下文时非常容易出错可能会导致内存使用问题(Android 应用最常见的问题之一)。

【讨论】:

  • 感谢您的建议。我认为创建一个 Application 子类听起来像是要走的路。当我从“合同”类中的静态初始化程序调用 MyApplication.getContext() 时,我需要注意哪些问题?另外,您可以在android:authorities 属性中使用@string/my_provider_authority 以减少重复吗?
  • 我修改了我的答案以解决您对在静态初始化程序中使用应用程序上下文的担忧。答案基本上是延迟初始化从字符串资源读取的变量,并确保您不会从应用程序生命周期之外调用 getter 方法。
  • 根据这个答案:stackoverflow.com/q/6461776/534471 你不应该使用@string 引用来定义提供者的权限,但你可以(至少在 Android 版本更高的 2.1 中)。但我同意那里的答案,即它很危险,可能应该省略。
【解决方案3】:

为什么要更改权限?您不需要导出提供程序,这意味着除了解构应用程序之外,没有人可以看到授权名称。即使那样,他们也无法访问提供程序。

如果是为了您自己的内部方便,那么我会使用相同的权限,但在 URI 上设置不同的安全性。

简而言之,你的想法很有趣,但我不会那样做。太乱了。

【讨论】:

  • 如果用户同时安装了这两个版本会怎样?
  • 免费版还公开了一个只读提供程序,允许付费版导入现有数据。最终,免费版和付费版的提供者都将由 CursorLoader 在内部使用。如果我理解正确,以后的用例不需要 标记,因为它是每个应用程序的内部。
  • 我的测试表明,即使没有导出提供程序,权限也必须是全球唯一的(在电话上)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-17
  • 2019-03-28
  • 1970-01-01
  • 2020-03-28
  • 2018-12-06
相关资源
最近更新 更多