Android 浅析 ContentProvider (一) 使用
前言
Linus Benedict Torvalds : RTFSC – Read The Fucking Source Code
概括
Content providers are one of the primary building blocks of Android applications, providing content to applications. They encapsulate data and provide it to applications through the single ContentResolver interface. A content provider is only required if you need to share data between multiple applications. For example, the contacts data is used by multiple applications and must be stored in a content provider. If you don't need to share data amongst multiple applications you can use a database directly via SQLiteDatabase.
我们先来看一张图片让我们对Content Provider有一个直观的了解:
ContentProvider提供了在应用程序之前共享数据的一种机制。
1、存储和获取数据提供了统一的接口。
2、对数据进行封装,不用关心数据存储的细节。
3、Android为常见的一些数据提供了默认的ContentProvider(包括音频、视频、图片和通讯录等)。
最主要的是ContentProvider对外共享数据统一了数据的访问方式。
组件使用
首先来看下如何实现一个ContentProvider功能:
功能简述
服务端:
有六个最主要的方法需要重写:
1、onCreate() which is called to initialize the provider.
2、query(Uri, String[], String, String[], String) which returns data to the caller.
3、insert(Uri, ContentValues) which inserts new data into the content provider.
4、update(Uri, ContentValues, String, String[]) which updates existing data in the content provider.
5、delete(Uri, String, String[]) which deletes data from the content provider.
6、getType(Uri) which returns the MIME type of data in the content provider.
注意:
insert()和update()方法有可能被多线程调用,一定要是线程安全的。
onCreate()方法只会调用一次,但要避免冗长的操作。
客户端:
通过ContentResolver可以自动获取Provider的实例,不必担心跨进程调用的细节。
代码例子
服务端:
AndroidManifest.xml
浅析:
AndroidManifest添加provider服务,在安装App的时候会自动注册这个Provider服务到AMS里作备份,这个服务最关键的点是authorities,作为Provider唯一标识,让其它程序可以在AMS里面查询到此Provider服务。
ps.更多关于Provider的权限可以看http://developer.android.com/guide/topics/manifest/provider-element.html。
DatabaseProvider.java
public class DatabaseProvider extends ContentProvider {
@Override
public boolean onCreate() {
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public String getType(Uri uri) {
return null;
}
}
浅析:
Provider最主要的六个方法。
getType():
Implement this to handle requests for the MIME type of the data at the given URI. The returned MIME type should start with vnd.android.cursor.item for a single record, or vnd.android.cursor.dir/ for multiple items.
注意: 这方法有可能用于多线程。用于访问此信息的应用程序不需要权限;如果您的内容提供者需要读和/或写权限,或不导出,所有应用程序都可以调用此方法,不管其访问权限。这使他们能够检索URI的MIME类型时,调度意图。
insert():
Implement this to handle requests to insert a new row. As a courtesy, call notifyChange() after inserting.
注意: 这方法有可能用于多线程。
onCreate():
Implement this to initialize your content provider on startup. This method is called for all registered content providers on the application main thread at application launch time. It must not perform lengthy operations, or application startup will be delayed.
注意: 如果你使用SQLite进行数据库操作,切勿在此方法中调用getReadableDatabase() or getWritableDatabase()方法,作为代替,可以使用onOpen(SQLiteDatabase)作为第一次初始数据库。
query():
Implement this to handle query requests from clients.
注意: 这方法有可能用于多线程。
delete():
Implement this to handle requests to delete one or more rows. The implementation should apply the selection clause when performing deletion, allowing the operation to affect multiple rows in a directory. As a courtesy, call notifyChange() after deleting.
注意: 这方法有可能用于多线程。如果一个特定的行要被删除,它会在URI的末尾解析出这一行的ID。
update():
Implement this to handle requests to update one or more rows. The implementation should update all rows matching the selection to set the columns according to the provided values map. As a courtesy, call notifyChange() after updating.
注意: 这方法有可能用于多线程。
sqlhelper.java
public class SqlHelper extends SQLiteOpenHelper {
public static final String CREATE_BOOK = "create table Book ("
+ "id integer primary key autoincrement, "
+ "author text, "
+ "price real, "
+ "pages integer, "
+ "name text)";
public static final String CREATE_CATEGORY = "create table Category ("
+ "id integer primary key autoincrement, "
+ "category_name text)";
public SqlHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("drop table if exists Book");
onCreate(db);
}
}
客户端:
Uri uri = Uri.parse("content://com.unknow.jason.testdatabaseex.provider/book");
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex("name"));
}
}
浅析:
getContentResolver().query():
Query the given URI, returning a Cursor over the result set with optional support for cancellation.
为了更佳的性能,调用者应该遵循两点:
1、提供一个明确的projection,防止从存储中读取不需要的数据。
2、使用问号参数标记,如“电话=?”而不是在选择参数中的显式值,因此,不同的值的查询将被确认为缓存的目的相同的。