2010-07-22 76 views
27

我正在開發一組僅適用於某些品牌的應用程序(請考慮不同的運動隊);然而,我遇到了一個問題,我正在爲所有特定品牌的應用程序使用一個庫項目,並希望爲所有這些應用程序使用相同的ContentProvider。在創建ContentProvider時,我將AUTHORITY聲明爲類中的常量(根據開發示例代碼),並且我在清單文件中的每個特定應用程序中使用相同的權限。它看起來像我不能用跨越每一個應用程序一樣的權限,因爲我在嘗試安裝第二個應用程序時,這個錯誤(我安裝一個品牌一個就好了,但第二個安裝):多個應用程序使用相同的內容提供商

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

我已經嘗試了幾種方法,但他們似乎都沒有工作。我還沒有做的一個想法是創建一個庫jar,只是省略了我的Provider類並在每個特定的應用程序中對其進行了自定義。任何想法如何解決這個問題而不訴諸於此?

+0

你可以嘗試申請我的解決方案爲類似的任務:http:// stackoverflow。com/a/15964372/1220237 – Sash0k 2013-04-12 06:19:11

回答

17

ContentProviders由權威機構標識,因此它必須是唯一的。我不認爲這有什麼竅門。

此外,Android平臺中存在一個錯誤,即使它們具有不同的權限並且包含在單獨的APK中,也會阻止爲兩個不同的ContentProviders使用相同的類名。查看錯誤here

我建議你的解決方案是在你的庫項目中創建抽象提供者類,然後在每個單獨的應用程序中用一個唯一的名稱來擴展它。爲了實現這一點,您可能需要創建一個腳本來生成/修改個別清單和內容提供程序類。

希望這會有所幫助。

+0

無論何時您想要擴展android基本功能集,並且您認爲它會起作用(因爲它的直觀性),您就會知道它不會。 – havexz 2012-07-13 17:42:12

+1

這個答案不再適用於更新版本的Android,因爲Google在2014年解決了這個問題,因爲[https://code.google.com/p/android/issues/detail?id=7716#c12] – Keridano 2016-03-23 11:15:40

4

比方說你 庫包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); 

現在你應該可以使用兩個部門。

來源:伊恩Warick(代碼寫了) Android - Having Provider authority in the app project 免責聲明:我張貼在這裏還有:Android duplicate provider authority problem - 不知道是否允許使用同樣的答案回答同一個類型的問題。

1

可以使用以下方法將ContentProvider打包到庫中,並在運行時設置ContentProvider的權限,以便在沒有ContentProvider權限衝突的情況下將其包含到多個項目中。這是有效的,因爲真正的'權威'來自AndroidManifest ...而不是ContentProvider類。

先從基本的ContentProvider implementation..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首先被初始化,你的ContentProvider將被從AndroidManifest收集到的ProviderInfo調用。這將在進行任何可能的查詢之前發生,很可能在初始應用程序類設置期間進行。使用此機會將AUTHORITY,CONTENT_URI和UriMatcher重置爲其「真實」值,如使用ContentProvider庫的應用程序所提供的。

@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的實際上是與應用類一起實例化,所以將有機會獲得所有所需的程序包信息。 ProviderInfo對象將包含AndroidManifest中提供的信息...包含在最終應用程序中的列表。

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

管理局現將「com.foo.barapp.content」,而不是默認值改寫,而UriMatcher將被更新到應用程序的價值,而不是默認。依賴於「AUTHORITY」的類現在將訪問更新的值,並且UriMatcher將正確區分傳入的查詢「com.foo.barapp.content」。

我已經同時測試了這兩個示例應用程序和androidTest包,並發現它能正常工作。

9

這是一個古老的問題,但我最近在做類似的事情。建立口味,現在它非常簡單。

指定BuildConfigField在gradle這個文件:

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 
+0

這應該是新接受的答案。儘管可以避免在運行時使用getApplicationContext()。getPackageName()來創建'buildConfigField'。 – 2017-11-08 01:08:39

1

你可以!

this post說(至極解釋了火力地堡怎麼沒有給它從你的Application#onCreate()方法的情況下初始化其庫),你可以在你的清單使用佔位符,就像這樣:

<provider 
     android:authorities="${applicationId}.yourcontentprovider" 
     android:name=".YourContentProvider" /> 
+0

很好的回答!簡短,甜美,並重點!謝謝! – Sakiboy 2017-07-13 05:46:28

+0

其實...太短,除非你知道你在做什麼,否則不工作。我會建議這個答案:https://stackoverflow.com/a/43444164/2371425 – Sakiboy 2017-07-27 04:30:26