1. 什么是ContentProvider 也即内容提供者,是对所有数据访问的一层抽象,为数据访问提供了统一的接口。它有以下优点: a. 对数据的抽象,为所有的组件提供统一的访问数据的方式,从而让组件不必关心具体数据的呈现形式(文件or数据库)。数据,也可以只关心自身的管理,而不用去管使用者的访问问题。这样就达到了很好的封装。 b. 接口更加方便,更加方便的让组件之间传送数据 ContentProvider的访问标识为Uri,通过统一的ContentResolver进行访问,而ContentResolver和Uri跟Application的上下文Context以及组件之间的信息传送工具Intent都是无缝接合,这就让组件之间进行数据共享和数据传递更加的方便和快捷。 所以,ContentProvider的最大好处在于它可以在不同组件之间方便的共享。所以,如果你的应用里面用到的数据需要在不同的组件之间共享,那么实现一个ContentProvider无疑是最佳方案。 2. 实现方式 ContentProvider的实现方式非常简单,只需要根据需求实现一些接口即可,比如:query, insert, delete, update, openFile等。但是具体的数据的呈现形式则是根据不同的目的进行自由选择,比如对于结构化数据,选择SQLiteDatabase可能是比较好的方案,大量的字节流可能文件是首选等等。 需要注意一点的是,虽然Android中百分之九十的ContentProvider内部都是用SQLiteDatabase来存储结构化数据,但这并不意味着ContentProvider只能从SQLiteDatabase来管理数据。ContentProvider定义了一些接口,你只需要按照需要返回正确的数据即可,具体 的实现方式则由你自由选择。 比如,Contacts的ContentProvider能提供以vCard的方式输出,也就是说当读取一个vCard的uri时,这个流是一个vCard形式的文件流,实现起来的思路就是这样: 复制代码 代码如下: Cursor query(Uri, ....) { if (uri is for vCard) { query the Contact"s infomation create a cursor with two columns name and size put contact"s name into cursor sum all Contact"s field and get size put that size into cursor return the cursor } }
这样通过Query就能得到这个vCard的相关信息文件名字和大小,再通过openInputStream就可以读取这个vCard文件流,但是实际上ContentProvider是没有vCard形式的数据,也没有一个vCard的文件,它只是在openFile的时候,识别出vCard的uri,把Contact数据转化成vCard形式写入输出流中: 复制代码 代码如下: ParcelFileDescriptor openFile(Uri...) { if (uri is for vcard) { generate vcard with VCardComposer write to output stream } }
3. 其他替代方案 ContentProvider不是必须的,每个应用必然用到数据,但是可以选择用创建一个ContentProvider来管理,也可以直接使用文件或数据库,如下面的例子: 复制代码 代码如下: package com.android.effective; import android.app.Activity; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.os.Bundle; import android.util.Log; public class SQLiteDatabaseDemo extends Activity { private static final String TAG = "SQLiteDatabaseDemo"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MyDatabase db = new MyDatabase(this);
int id = db.setName("Michael Jordan"); Log.e(TAG, "id of " + id + " is " + db.getName(id)); }
private class MyDatabase { private static final String name = "demo.db"; private static final String table = "demo"; private final String[] projection = new String[] {"_id", "name" }; private MyDatabaseHelper helper;
public MyDatabase(Context context) { helper = new MyDatabaseHelper(context, name, null, 1); }
public String getName(int id) { final Cursor c = helper.getReadableDatabase().query("demo", projection, "_id=" + id, null, null, null, null); if (c == null || !c.moveToFirst()) { return null; } return c.getString(1); }
public int setName(String name) { ContentValues cv = new ContentValues(); cv.put("name", name); return (int) helper.getWritableDatabase().insert(table, "name", cv); } }
private class MyDatabaseHelper extends SQLiteOpenHelper { public MyDatabaseHelper(Context context, String name, CursorFactory factory, int version) { super(context, name, factory, version); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE demo (_id INTEGER PRIMARY KEY, name TEXT);"); } @Override public void onUpgrade(SQLiteDatabase db, int old, int newver) {