web-dev-qa-db-ja.com

複数のアプリが同じコンテンツプロバイダーを使用している

私は特定のブランドでのみ区別される一連のアプリを開発しています(さまざまなスポーツチームを考えてください)。しかし、特定のブランドのアプリすべてに1つのライブラリプロジェクトを使用していて、それらすべてに同じContentProviderを使用したいという問題が発生しています。 ContentProviderを作成したとき、(devサンプルコードに従って)クラス内の定数としてAUTHORITYを宣言し、マニフェストファイル内のすべての特定のアプリで同じ権限を使用しています。 2番目のアプリをインストールしようとするとこのエラーが発生するため、すべてのアプリで同じ権限を使用できないようです(1つのブランドのアプリを問題なくインストールしますが、2番目のアプリをインストールします)。

WARN/PackageManager(66): Can't install because provider name com.xxx.Provider (in package com.xxx) is already used by com.zzz

私はいくつかの方法を試しましたが、どれもうまくいかないようです。私がまだ行っていないアイデアの1つは、ライブラリjarを作成し、所有しているProviderクラスを省略して、特定のアプリごとにカスタマイズすることでした。これに頼らずにこの問題を回避する方法に関するアイデアはありますか?

38
dougzor

ContentProviderは権限によって識別されるため、一意である必要があります。私はその周りにトリックはないと思います。

さらに、Androidプラットフォームにはバグがあり、権限が異なり、別々のAPKに含まれている場合でも、2つの異なるContentProviderに同じクラス名を使用できません。バグを参照してください ここ

私がアドバイスする解決策は、ライブラリプロジェクトで抽象プロバイダークラスを作成し、それを個々のアプリケーションごとに一意の名前で拡張することです。これを実用的にするには、個々のマニフェストとコンテンツプロバイダークラスを生成/変更するスクリプトを作成する必要があります。

お役に立てれば。

21
hvuoltee

古い質問ですが、最近似たようなことをすることを考えていました。ビルドフレーバーを使用すると、今では本当に簡単です。

GradleファイルでBuildConfigFieldを指定します。

    productFlavors {
    free {
        applicationId "com.example.free"
        buildConfigField 'String', 'AUTHORITY', '"com.example.free.contentprovider"'
    }

    paid {
        applicationId "com.example.paid"
        buildConfigField 'String', 'AUTHORITY', '"com.example.paid.contentprovider"'
    }

マニフェストでプロバイダー権限を指定します。

    <provider
        Android:name=".ContentProvider"
        Android:authorities="${applicationId}.contentprovider" />

BuildConfigField変数を使用して、プロバイダーに権限を設定します。

    public static final String AUTHORITY = BuildConfig.AUTHORITY
26
Gaurav Gupta

あなたはできる!

この投稿Application#onCreate()メソッドからコンテキストを与えずにFirebaseがライブラリを初期化する方法を説明しています)で述べたように、次のようにマニフェストでプレースホルダーを使用できます:

    <provider
         Android:authorities="${applicationId}.yourcontentprovider"
         Android:name=".YourContentProvider" />
6
Yairopro

ライブラリパッケージがcom.Android.app.library無料パッケージがcom.Android.app.free有料パッケージがcom.Android.app.paidであるとしましょう

無料プロジェクトと有料プロジェクトで、同じファイルをパッケージに作成します。これは何でもかまいませんが、同じである必要があります。

例:

  1. Com.Android.app.dataを使用して無料バージョンで新しいパッケージを作成します

  2. Authority.Javaという名前のファイルを作成し、その中に(Authority.Java)を配置します。

    public class Authority {

    `public static final String CONTENT_AUTHORITY = "YOUR PROVIDER";`
    

    }

  3. 有料版でもこれを繰り返します。パッケージ名とクラス名は同じにしてください。

これで、契約ファイルで、ライブラリで次を使用します。

public static String AUTHORITY = initAuthority();

    private static String initAuthority() {
        String authority = "something.went.wrong.if.this.is.used";

        try {

            ClassLoader loader = Contract.class.getClassLoader();

            Class<?> clz = loader.loadClass("com.Android.app.data.Authority");
            Field declaredField = clz.getDeclaredField("CONTENT_AUTHORITY");

            authority = declaredField.get(null).toString();
        } catch (ClassNotFoundException e) {} 
        catch (NoSuchFieldException e) {} 
        catch (IllegalArgumentException e) {
        } catch (IllegalAccessException e) {
        }

        return authority;
    }

    public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);

これで、2つの権限を使用できるようになります。

クレジット:Ian Warick(コードの記述) Android-アプリプロジェクトにプロバイダー権限がある 免責事項:ここにも投稿しました: Android重複プロバイダー権限の問題 -かどうかわからない同じ種類の質問に同じ答えで答えることができます。

4
Jerryl15

次の方法を使用して、ライブラリ内にContentProviderをパッケージ化し、実行時にContentProviderの権限を設定して、ContentProvider権限の競合なしに複数のプロジェクトに含めることができます。これが機能するのは、実際の「権限」がContentProviderクラスではなくAndroidManifestに由来するためです。

基本的なContentProvider実装から始めます。AUTHORITY、CONTENT_URI、およびUriMatcherは静的ですが、「最終」ではありません。

public class MyContentProvider extends ContentProvider {
    public static String  AUTHORITY = "com.foo.bar.content";
    public static Uri     CONTENT_URI = Uri.parse("content://" + AUTHORITY);
    protected static UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

次に、「attachInfo」メソッドをオーバーライドして、ContentProviderが最初に初期化されるときに、AndroidManifestから収集されたProviderInfoを使用してContentProviderが呼び出されるようにします。これは、可能性のあるクエリが実行される前に発生します。ほとんどの場合、最初のアプリケーションクラスのセットアップ中に発生します。この機会を利用して、CONTENTProviderライブラリを使用しているアプリケーションによって提供されるように、AUTHORITY、CONTENT_URI、およびUriMatcherを「実際の」値にリセットします。

    @Override
public void attachInfo(Context context, ProviderInfo info) {
    super.attachInfo(context, info);
    AUTHORITY = info.authority;
    CONTENT_URI = Uri.parse("content://" + AUTHORITY);
    uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    uriMatcher.addURI(AUTHORITY, AlarmTable.TABLENAME, ALARMS);
    uriMatcher.addURI(AUTHORITY, AttributeTable.TABLENAME, ATTRIBUTES);
    uriMatcher.addURI(AUTHORITY, DeepLinkTable.TABLENAME, DEEPLINKS);
    uriMatcher.addURI(AUTHORITY, NotificationTable.TABLENAME, NOTIFICATIONS);
    uriMatcher.addURI(AUTHORITY, MetaDataTable.TABLENAME, RESOURCE_METADATA);
    uriMatcher.addURI(AUTHORITY, ResourceTable.TABLENAME, RESOURCES);
    uriMatcher.addURI(AUTHORITY, ResourceAttributeTable.TABLENAME, RESOURCES_ATTRIBUTES);
    uriMatcher.addURI(AUTHORITY, ResourceTagTable.TABLENAME, RESOURCES_TAGS);
    uriMatcher.addURI(AUTHORITY, TagTable.TABLENAME, TAGS);
    uriMatcher.addURI(AUTHORITY, UserTagTable.TABLENAME, USER_TAGS);
    uriMatcher.addURI(AUTHORITY, UserTable.TABLENAME, USERS);
    uriMatcher.addURI(AUTHORITY, CUSTOM, RAW);
}

アプリケーションが開始されると、ContentProviderは実際にはApplicationクラスとともにインスタンス化されるため、必要なすべてのパッケージ情報にアクセスできます。 ProviderInfoオブジェクトには、AndroidManifestで提供される情報が含まれます...最終的なアプリケーションに含まれるリスト。

        <provider Android:authorities="com.foo.barapp.content"
              Android:name="com.foo.bar.MyContentProvider"/>

Authorityは、デフォルト値ではなく「com.foo.barapp.content」で書き換えられ、UriMatcherはデフォルト値ではなくアプリケーションの値に更新されます。 「AUTHORITY」に依存するクラスは、更新された値にアクセスするようになり、UriMatcherは「com.foo.barapp.content」の着信クエリを適切に区別します。

これをサンプルアプリケーションとandroidTestパッケージの両方で同時にテストし、正しく動作することを確認しました。

1
Dan Devine