2010-09-28 52 views
87

我正在构建一个应用程序,其中有一个事件表和场地表。我希望能够授予其他应用程序访问此数据。我有几个与这类问题的最佳实践有关的问题。在Android中使用内容提供者公开多个表的最佳做法

  1. 我应该如何构造数据库类? 我现在有用于EventsDbAdapter和VenuesDbAdapter类,它们用于查询每个表提供逻辑,而具有单独的DbManager(延伸SQLiteOpenHelper),用于管理数据库的版本,创建/升级数据库,给访问数据库(getWriteable/ReadeableDatabase)。这是推荐的解决方案吗,还是将整件事整合到一个类(即DbManager)或分离所有内容并让每个适配器扩展SQLiteOpenHelper?

  2. 我应该如何设计内容提供商的多个表? 扩展前面的问题,我应该为整个应用程序使用一个Content Provider,还是应该为事件和场所创建单独的提供程序?

我发现的大多数例子只处理单个表格的应用程序,所以我会很感激这里的任何指针。

回答

10

我建议检查出为Android 2.X ContactProvider的源代码。 (可以在网上找到)。他们通过提供专门的视图处理交叉表查询,然后在后端运行查询。在前端,通过单个内容提供者可以通过各种不同的URI访问调用者。您可能还需要提供一个或两个持有表字段名称和URI字符串的常量。这些类可以作为一个API包含或作为类的放置提供,并且使消费应用程序更容易使用。

它有点复杂,所以你可能也想看看日历,以及如何让你做什么的想法,并不需要。

你应该只需要一个DB适配器和每个数据库的单一内容提供商(不是每个表)做的大部分工作,但您可以使用多个适配器/供应商,如果你真的想。它只是让事情更复杂一点。

+5

com.android.providers.contacts.ContactsProvider2.java https://github.com/android/platform_packages_providers_contactsprovider/blob/master/src/com/android/providers/contacts/ContactsProvider2.java – Alex 2013-02-27 19:29:43

+0

@ Marloke谢谢。好吧,我明白即使是Android团队使用'switch'解决方案,但是您提到的这个部分:'他们通过提供专业视图来处理跨表查询,然后您在后端运行查询。在前端,通过单个内容提供者可以通过各种不同的URI访问调用者。你认为你可以更详细地解释一下吗? – eddy 2014-12-01 12:50:11

112

这可能对你有点晚了,但其他人可能会发现这很有用。

首先,你需要创建多个CONTENT_URIs

public static final Uri CONTENT_URI1 = 
    Uri.parse("content://"+ PROVIDER_NAME + "/sampleuri1"); 
public static final Uri CONTENT_URI2 = 
    Uri.parse("content://"+ PROVIDER_NAME + "/sampleuri2"); 

然后你扩大你的URI匹配器

private static final UriMatcher uriMatcher; 
static { 
    uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 
    uriMatcher.addURI(PROVIDER_NAME, "sampleuri1", SAMPLE1); 
    uriMatcher.addURI(PROVIDER_NAME, "sampleuri1/#", SAMPLE1_ID);  
    uriMatcher.addURI(PROVIDER_NAME, "sampleuri2", SAMPLE2); 
    uriMatcher.addURI(PROVIDER_NAME, "sampleuri2/#", SAMPLE2_ID);  
} 

然后创建表

private static final String DATABASE_NAME = "sample.db"; 
private static final String DATABASE_TABLE1 = "sample1"; 
private static final String DATABASE_TABLE2 = "sample2"; 
private static final int DATABASE_VERSION = 1; 
private static final String DATABASE_CREATE1 = 
    "CREATE TABLE IF NOT EXISTS " + DATABASE_TABLE1 + 
    " (" + _ID1 + " INTEGER PRIMARY KEY AUTOINCREMENT," + 
    "data text, stuff text);"; 
private static final String DATABASE_CREATE2 = 
    "CREATE TABLE IF NOT EXISTS " + DATABASE_TABLE2 + 
    " (" + _ID2 + " INTEGER PRIMARY KEY AUTOINCREMENT," + 
    "data text, stuff text);"; 

不要忘了添加第二个DATABASE_CREATEonCreate()

你要使用的switch-case块来确定使用什么表。这是我的插入代码

@Override 
public Uri insert(Uri uri, ContentValues values) { 
    Uri _uri = null; 
    switch (uriMatcher.match(uri)){ 
    case SAMPLE1: 
     long _ID1 = db.insert(DATABASE_TABLE1, "", values); 
     //---if added successfully--- 
     if (_ID1 > 0) { 
      _uri = ContentUris.withAppendedId(CONTENT_URI1, _ID1); 
      getContext().getContentResolver().notifyChange(_uri, null);  
     } 
     break; 
    case SAMPLE2: 
     long _ID2 = db.insert(DATABASE_TABLE2, "", values); 
     //---if added successfully--- 
     if (_ID2 > 0) { 
      _uri = ContentUris.withAppendedId(CONTENT_URI2, _ID2); 
      getContext().getContentResolver().notifyChange(_uri, null);  
     } 
     break; 
    default: throw new SQLException("Failed to insert row into " + uri); 
    } 
    return _uri;     
} 

您需要devide了deleteupdategetType等无论您的供应商要求DATABASE_TABLE或CONTENT_URI你会在一个和#2增加的情况下,并有DATABASE_TABLE1或CONTENT_URI1在下一个等等,尽可能多的你想要的。

+1

感谢您的回答,这非常接近我最终使用的解决方案。我发现使用多个表的复杂提供者会得到大量的switch语句,这似乎并不那么优雅。但我明白这是大多数人这样做的方式。 – 2010-12-09 10:28:08

+0

是否notifyChange真的应该使用_uri而不是原来的uri? – span 2012-08-29 08:40:08

+16

这是Android公认的标准吗?它很有效,但显得有点“笨重”。 – prolink007 2012-09-03 15:28:59

7

一个ContentProvider可以服务多个表,但它们应该有些相关。如果您打算同步您的提供商,这将会有所帮助。如果您想单独进行同步,比如说联系人,邮件或日历,即使它们最终位于同一个数据库中或与同一服务同步,也需要不同的提供程序,因为同步适配器直接绑定到特定的提供者。

据我所知,每个数据库只能使用一个SQLiteOpenHelper,因为它将元信息存储在数据库的表中。因此,如果你的ContentProviders访问同一个数据库,你将不得不分享助手。

6

注意:这是Opy提供的答案的澄清/修改。

这种方法每个细分的insertdeleteupdategetType方法与switch语句,以便处理每个单独的表。您将使用CASE来标识要引用的每个表(或uri)。然后,每个CASE映射到您的一个表或URI。例如,TABLE1URI1在CASE#1等中为您的应用所用的所有表格选定。

下面是该方法的一个示例。这是为插入方法。它的实现与Opy的实现有些不同,但是执行相同的功能。你可以选择你喜欢的风格。我也想确保插入返回一个值,即使表插入失败。在这种情况下,它返回一个-1

@Override 
    public Uri insert(Uri uri, ContentValues values) { 
    int uriType = sURIMatcher.match(uri); 
    SQLiteDatabase sqlDB; 

    long id = 0; 
    switch (uriType){ 
     case TABLE1: 
      sqlDB = Table1Database.getWritableDatabase(); 
      id = sqlDB.insert(Table1.TABLE_NAME, null, values); 
      getContext().getContentResolver().notifyChange(uri, null); 
      return Uri.parse(BASE_PATH1 + "/" + id); 
     case TABLE2: 
      sqlDB = Table2Database.getWritableDatabase(); 
      id = sqlDB.insert(Table2.TABLE_NAME, null, values); 
      getContext().getContentResolver().notifyChange(uri, null); 
      return Uri.parse(BASE_PATH2 + "/" + id); 
     default: 
      throw new SQLException("Failed to insert row into " + uri); 
      return -1; 
    }  
    } // [END insert]