使用Android中的内容提供程序公开多个表的最佳实践
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用Android中的内容提供程序公开多个表的最佳实践相关的知识,希望对你有一定的参考价值。
我正在构建一个应用程序,我有一个事件表和一个场地表。我希望能够授予其他应用程序访问此数据的权限。我有几个与此类问题的最佳实践相关的问题。
- 我应该如何构建数据库类?我目前有EventsDbAdapter和VenuesDbAdapter的类,它们提供查询每个表的逻辑,同时有一个单独的DbManager(扩展SQLiteOpenHelper)来管理数据库版本,创建/升级数据库,提供对数据库的访问(getWriteable / ReadeableDatabase)。这是推荐的解决方案,还是我最好将所有内容整合到一个类(即DbManager)或分离所有内容并让每个适配器扩展SQLiteOpenHelper?
- 我应该如何为多个表设计内容提供程序?扩展上一个问题,我应该为整个应用程序使用一个内容提供程序,还是应该为事件和场地创建单独的提供程序?
我发现的大多数示例只涉及单表应用程序,所以我很感激这里的任何指针。
对你来说可能有点晚了,但其他人可能觉得这很有用。
首先,您需要创建多个CONTENT_URI
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_CREATE
添加到onCreate()
您将使用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;
}
您将需要分配delete
,update
,getType
等。无论您的提供商是否需要DATABASE_TABLE或CONTENT_URI,您都会添加一个案例并在其中添加DATABASE_TABLE1或CONTENT_URI1,在下一个中使用#2,依此类推。
我建议查看android 2.x ContactProvider的源代码。 (可以在网上找到)。它们通过提供专用视图来处理跨表查询,然后在后端运行查询。在前端,调用者可以通过单个内容提供者通过各种不同的URI访问它们。您可能还希望提供一个或两个类来保存表字段名称和URI字符串的常量。这些类可以作为API包含或作为类中的drop提供,并且将使消费应用程序更容易使用。
它有点复杂,所以你可能还想查看日历如何以及了解你做什么和不需要什么。
每个数据库(不是每个表)只需要一个数据库适配器和一个内容提供程序来完成大部分工作,但如果您真的想要,可以使用多个适配器/提供程序。它只是让事情变得复杂一些。
一个ContentProvider
可以服务多个表,但它们应该有些相关。如果您打算同步提供商,这将有所不同。如果你想要单独的同步,比如说联系人,邮件或日历,你需要为它们各自提供不同的提供者,即使它们最终在同一个数据库中或者与同一个服务同步,因为同步适配器直接绑定到特定的提供者。
据我所知,每个数据库只能使用一个SQLiteOpenHelper,因为它将元信息存储在数据库的表中。因此,如果你的ContentProviders
访问同一个数据库,你将不得不分享Helper。
注意:这是对Opy提供的答案的澄清/修改。
这种方法使用switch语句细分insert
,delete
,update
和getType
方法中的每一个,以便处理每个表。您将使用CASE来标识要引用的每个表(或uri)。然后每个CASE映射到您的一个表或URI。例如,对于您的应用程序使用的所有表,在CASE#1等中选择TABLE1或URI1。
这是该方法的一个例子。这适用于插入方法。它与Opy的实现略有不同,但执行相同的功能。您可以选择您喜欢的样式。我还想确保即使表插入失败,insert也会返回一个值。在这种情况下,它返回一个-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]
我找到了ContentProvider的最佳演示和解释,我认为它遵循Android标准。
合同类
/**
* The Content Authority is a name for the entire content provider, similar to the relationship
* between a domain name and its website. A convenient string to use for content authority is
* the package name for the app, since it is guaranteed to be unique on the device.
*/
public static final String CONTENT_AUTHORITY = "com.androidessence.moviedatabase";
/**
* The content authority is used to create the base of all URIs which apps will use to
* contact this content provider.
*/
private static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);
/**
* A list of possible paths that will be appended to the base URI for each of the different
* tables.
*/
public static final String PATH_MOVIE = "movie";
public static final String PATH_GENRE = "genre";
和内在类:
/**
* Create one class for each table that handles all information regarding the table schema and
* the URIs related to it.
*/
public static final class MovieEntry implements BaseColumns {
// Content URI represents the base location for the table
public static final Uri CONTENT_URI =
BASE_CONTENT_URI.buildUpon().appendPath(PATH_MOVIE).build();
// These are special type prefixes that specify if a URI returns a list or a specific item
public static final String CONTENT_TYPE =
"vnd.android.cursor.dir/" + CONTENT_URI + "/" + PATH_MOVIE;
public static final String CONTENT_ITEM_TYPE =
"vnd.android.cursor.item/" + CONTENT_URI + "/" + PATH_MOVIE;
// Define the table schema
public static final String TABLE_NAME = "movieTable";
public static final String COLUMN_NAME = "movieName";
public static final String COLUMN_RELEASE_DATE = "movieReleaseDate";
public static final String COLUMN_GENRE = "movieGenre";
// Define a function to build a URI to find a specific movie by it's identifier
public static Uri buildMovieUri(long id){
return ContentUris.withAppendedId(CONTENT_URI, id);
}
}
public static final class GenreEntry implements BaseColumns{
public static final Uri CONTENT_URI =
BASE_CONTENT_URI.buildUpon().appendPath(PATH_GENRE).build();
public static final String CONTENT_TYPE =
"vnd.android.cursor.dir/" + CONTENT_URI + "/" + PATH_GENRE;
public static final String CONTENT_ITEM_TYPE =
"vnd.android.cursor.item/" + CONTENT_URI + "/" + PATH_GENRE;
public static final String TABLE_NAME = "genreTable";
public static final String COLUMN_NAME = "genreName";
public static Uri buildGenreUri(long id){
return ContentUris.withAppendedId(CONTENT_URI, id);
}
}
现在使用SQLiteOpenHelper创建数据库:
public class MovieDBHelper extends SQLiteOpenHelper{
/**
* Defines the database version. This variable must be incremented in order for onUpdate to
* be called when necessary.
*/
private static final int DATABASE_VERSION = 1;
/**
* The name of the database on the device.
*/
private static final String DATABASE_NAME = "movieList.db";
/**
* Default constructor.
* @param context The application context using this database.
*/
public MovieDBHelper(Context conte以上是关于使用Android中的内容提供程序公开多个表的最佳实践的主要内容,如果未能解决你的问题,请参考以下文章