他的底层也是Binder


系统预置了许多ContentProvider,比如通讯录信息,日程表信息等。要跨进程访问这些信息,只需要通过ContentProvider的query、update、insert、delete方法即可。getType用来返回一个Uri请求所对应的MIME类型(媒体类型),比如图片视频等。如果不关注这个,返回null或者*/*即可。根据Binder原理可以得知,这6个方法都运行在ContentProvider进程中。在这个进程中,onCreate由系统回调运行在主线程中,另外5个方法由外界回调,运行在Binder的线程池中。


ContentProvider以表格的形式来组织数据,并且可以包含多个表。对于每个表,他们都具有行和列的层次性,和数据库类似。除了表格的形式,ContentProvider还支持文件数据,比如图片、视频等。文件数据和表格数据的结构不同,因此处理处理这类数据时,可以在ContentProvider中返回文件的句柄给外界从而让文件来访问ContentProvider中的文件信息。Android系统所提供的MediaStore功能就是功能就是文件类型的MediaStore。另外,虽然ContentProvider的底层数据看起来很像一个SQLite数据库,但是ContentProvider对底层的数据存储方式没有任何要求,我们既可以使用SQLite数据库,也可以使用普通的文件,甚至可以采用内存中的一个对象来进行数据存储。

ContentProvider的权限还可以细分为读权限和写权限。


数据库工具类

public class DbOpenHelper extends SQLiteOpenHelper {
    private static final String DB_NAME = "book_provider.db";
    public static final String BOOK_TABLE_NAME = "book";
    public static final String USER_TABLE_NAME = "user";

    private static final int DB_VERSION = 1;

    private String CREATE_BOOK_TABLE = "CREATE TABLE IF NOT EXISTS" + BOOK_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT)";

    private String CREATE_USER_TABLE = "CREATE TABLE IF NOT EXISTS" + USER_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT," + "sex INT)";

    public DbOpenHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK_TABLE);
        db.execSQL(CREATE_USER_TABLE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}



provider,uri和数据库表映射通过UriMatcher。通过ContentProvider访问数据库表

public class BookProvider extends ContentProvider {
    private static final String TAG = "xbh";

    public static final String AUTHORITY = "com.example.test";

    public static final Uri BOOK_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/book");

    public static final Uri USER_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/user");

    public static final int BOOK_URI_CODE = 0;

    public static final int USER_URI_CODE = 1;

    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

    static {
        sUriMatcher.addURI(AUTHORITY, "book", BOOK_URI_CODE);
        sUriMatcher.addURI(AUTHORITY, "user", USER_URI_CODE);
    }

    private Context mContext;
    private SQLiteDatabase mDb;

    public BookProvider() {
    }

    @Override
    public boolean onCreate() {
        Log.i(TAG, "onCreate: ");
        mContext = getContext();
        initProviderData();
        return true;
    }

    private void initProviderData() {
        mDb = new DbOpenHelper(mContext).getWritableDatabase();
        mDb.execSQL("delete from " + DbOpenHelper.BOOK_TABLE_NAME);
        mDb.execSQL("delete from " + DbOpenHelper.USER_TABLE_NAME);
        mDb.execSQL("insert into book values(3, 'Android');");
        mDb.execSQL("insert into book values(4, 'IOS');");
        mDb.execSQL("insert into book values(5, 'HTML5');");
        mDb.execSQL("insert into book values(1, 'Jake', 1);");
        mDb.execSQL("insert into book values(2, 'Jasmine', 0);");
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        String table = getTableName(uri);
        if (table == null){
            throw new IllegalArgumentException("no this uri");
        }
        return mDb.query(table, projection, selection, selectionArgs, null, null, sortOrder, null);
    }

    @Override
    public String getType(Uri uri) {
        Log.i(TAG, "getType: ");
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        Log.i(TAG, "insert: ");
        String table = getTableName(uri);
        if (table == null) {
            throw new IllegalArgumentException("no this uri");
        }
        mDb.insert(table, null, values);
        mContext.getContentResolver().notifyChange(uri, null);
        return uri;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        Log.i(TAG, "delete: ");
        String table = getTableName(uri);
        if (table == null) {
            throw new IllegalArgumentException("no this uri");
        }
        int count = mDb.delete(table, selection, selectionArgs);
        if (count > 0) {
            getContext().getContentResolver().notifyChange(uri, null);
        }
        return count;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        Log.i(TAG, "update: ");
        String table = getTableName(uri);
        if (table == null) {
            throw new IllegalArgumentException("no this uri");
        }
        int row = mDb.update(table, values, selection, selectionArgs);
        if (row > 0) {
            getContext().getContentResolver().notifyChange(uri, null);
        }
        return row;
    }

    private String getTableName(Uri uri) {
        String tableName = null;
        switch (sUriMatcher.match(uri)) {
            case BOOK_URI_CODE:
                tableName = DbOpenHelper.BOOK_TABLE_NAME;
                break;
            case USER_URI_CODE:
                tableName = DbOpenHelper.USER_TABLE_NAME;
                break;
            default:
                break;
        }
        return tableName;
    }
}



注意点:query、update、insert、delete是可以多线程并发访问的,所以需要做好线程同步。具体原因是SQLiteDatabase内部对数据库的操作是由同步处理的。但是如果通过多个SQLiteDatabase对象来操作数据库就无法进行同步了。如果ContentProvider底层是一块内存的话,比如是List,在这种情况下同List的遍历、插入、删除操作就需要进行线程同步,否则就会引起并发错误。


使用代码

public class ProviderActivity extends AppCompatActivity {

    private static final String TAG = "xbh";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_provider);
        Uri uri = Uri.parse("content://com.example.test");
        getContentResolver().query(uri, null, null, null, null);
        getContentResolver().query(uri, null, null, null, null);
        getContentResolver().query(uri, null, null, null, null);

        Uri bookUri = Uri.parse("content://com.example.test/book");
        ContentValues values = new ContentValues();
        values.put("_id", 6);
        values.put("name", "程序设计的艺术");
        getContentResolver().insert(bookUri, values);
        Cursor bookCursor = getContentResolver().query(bookUri, new String[]{"_id", "name"}, null, null, null);
        while (bookCursor.moveToNext()) {
            Book book = new Book();
            book.bookId = bookCursor.getInt(0);
            book.bookName = bookCursor.getString(1);
            Log.i(TAG, book.toString());
        }
        bookCursor.close();
    }
}