ContentProvider介绍与使用

2018-04-02  本文已影响0人  帝王鲨kingcp

ContentProvider、ContentResolver、ContentObserver三者之间的关系

ContentProvider 内容提供者,用于对外提供数据 。
ContentResolver 内容解析者,用于获取内容提供者提供的数据 。
ContentObserver 内容监听器,可以监听数据的改变状态 。

ContentProvider 是如何实现数据共享的

第一步:需要创建一个类继承ContentProvider,实现相关方法。
public class ContentProviderTest extends ContentProvider {
    @Override
    public boolean onCreate() {
        return false;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        return null;
    }

     /**覆盖ContentProvider的getType方法对于用new Intent(String action, Uri uri)方法启动activity是很重要的,
        如果它返回的MIME type和activity在<intent filter>中定义的data的MIME type不一致,将造成activity无法启动。
      */
    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }
}
第二步:在AndroidManifest.xml中注册ContentProvider,申明权限。

android:exported="true",让外部应用可以访问
android:authorities="com.king.qingpiao.contentProvider",申明URI中的authority

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.king.qiangpiao">

    <!-- 声明一个权限  -->
    <permission
        android:name="com.king.qiangpiao.PRIVATE.all"
        android:label="provider permission"
        android:protectionLevel="normal" />
    <permission
        android:name="com.king.qiangpiao.PRIVATE.read"
        android:label="provider read permission"
        android:protectionLevel="normal" />
    <permission
        android:name="com.king.qiangpiao.PRIVATE.write"
        android:label="provider write permission"
        android:protectionLevel="normal" />

    <application
        android:name=".MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <provider
            android:authorities="com.king.qiangpiao.contentProvider"
            android:name=".Book_ContentProvider"
            android:readPermission="com.king.qiangpiao.PRIVATE.read"
            android:writePermission="com.king.qiangpiao.PRIVATE.write"
            android:permission="com.king.qiangpiao.PRIVATE.all"
            android:exported="true">
        </provider>     
    </application>

</manifest>
第三步:在其他应用中通过ContentResolver获取想要的数据。同时也需要给访问ContentProvider的权限。

需要使用ContentProvider的应用,必须在AndroidManifest.xml中添加ContentProvider中申明的权限。

<uses-permission android:name="com.king.qiangpiao.PRIVATE.all"/>
<uses-permission android:name="com.king.qiangpiao.PRIVATE.read"/>
<uses-permission android:name="com.king.qiangpiao.PRIVATE.write"/>

实例

在A应用中的Book_ContentProvider ,对外提供应用数据。在query中用SQLiteQueryBuilder进行映射查询。
其中getType方法对于用new Intent(String action, Uri uri)方法启动activity是很重要的,如果它返回的MIME type和activity在<intent filter>中定义的data的MIME type不一致,将造成activity无法启动。
具体的是注册内容就是第二步中配置。

public class Book_ContentProvider extends ContentProvider {

    private static final String TAG = "Book_ContentProvider";
    private static final String AUTHORITY = "com.king.qiangpiao.contentProvider";//在AndroidManifest.xml中注册authority
    private static final Uri BOOK_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/book");
    private static final Uri USER_CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/user");
    private static Map projectionMap;//查询映射,防止外部应用知道真正SQLite中的列表名
    private Context mContext;
    private SQLiteDatabase mDb;
    private static final int BOOK_URI_CODE = 0;
    private static final int USER_URI_CODE = 1;
    private static final String BOOK_TABLE_NAME = "book";
    private static final String USER_TABLE_NAME = "user";
    //对URI的配对
    private static final UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    static {
        mUriMatcher.addURI(AUTHORITY,BOOK_TABLE_NAME,BOOK_URI_CODE);
        mUriMatcher.addURI(AUTHORITY,USER_TABLE_NAME,USER_URI_CODE);
    }

    @Override
    public boolean onCreate() {
        Log.i(TAG, "onCreate: Thread name = " + Thread.currentThread().getName());
        mContext = getContext();
        mDb = new DBHelper(mContext).getReadableDatabase();
        projectionMap = new HashMap<String,String>();
        projectionMap.put("book_id","_id");//外部 book_id 对应内部 _id
        projectionMap.put("book_name","name");//外部 book_name 对应内部 name
        return false;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        //执行query , insert , delete , update是在不同的线程中的,有的操作需要注意这个问题
        Log.i(TAG, "query: Thread name = " + Thread.currentThread().getName());
        String table = getTableName(uri);
        if(table == null){
            throw new IllegalArgumentException("Unsupported URI: " + uri);
        }
        //通过映射查询
        SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
        queryBuilder.setTables(BOOK_TABLE_NAME);
        queryBuilder.setProjectionMap(projectionMap);
        return queryBuilder.query(mDb,projection,selection,selectionArgs,null,null,sortOrder);
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        Log.i(TAG, "getType: Thread name = " + Thread.currentThread().getName());
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        Log.i(TAG, "insert: Thread name = " + Thread.currentThread().getName());
        String table = getTableName(uri);
        if(table == null){
            throw new IllegalArgumentException("Unsupported URI: " + uri);
        }
        Log.i(TAG, "insert: table = " + table + " , values = " + values.toString());
        mDb.insert(table,null,values);
        mContext.getContentResolver().notifyChange(uri,null);//通知ContentObserver数据变化
        return uri;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        Log.i(TAG, "delete: Thread name = " + Thread.currentThread().getName());
        String table = getTableName(uri);
        if(table == null){
            throw new IllegalArgumentException("Unsupported URI: " + uri);
        }
        int count = mDb.delete(table,selection,selectionArgs);
        if(count > 0){
            mContext.getContentResolver().notifyChange(uri,null);//通知ContentObserver数据变化
        }
        return count;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        Log.i(TAG, "update: Thread name = " + Thread.currentThread().getName());
        String table = getTableName(uri);
        if(table == null){
            throw new IllegalArgumentException("Unsupported URI: " + uri);
        }
        int row = mDb.update(table,values,selection,selectionArgs);
        if(row > 0){
            mContext.getContentResolver().notifyChange(uri,null);//通知ContentObserver数据变化
        }
        return row;
    }

    //根据匹配返回数据库表名
    private String getTableName(Uri uri){
        int code = mUriMatcher.match(uri);
        String tableName = null;
        switch (code){
            case BOOK_URI_CODE:
                tableName =  BOOK_TABLE_NAME;
                break;
            case USER_URI_CODE:
                tableName = USER_TABLE_NAME;
                break;
        }
        return tableName;
    }
}

简单实现DBHelper,代码如下:

public class DBHelper extends SQLiteOpenHelper {

    private static final String DB_NAME = "database.db";
    private static final String BOOK_TABLE_NAME = "book";
    private static final String USER_TABLE_NAME = "user";
    private static final int DB_VERSION = 1;
    private static final String CREATE_BOOK_TABLE = "CREATE TABLE IF NOT EXISTS " + BOOK_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT)";
    private static final String CREATE_USER_TABLE = "CREATE TABLE IF NOT EXISTS " + USER_TABLE_NAME + "(_id INTEGER PRIMARY KEY," + "name TEXT)";

    public DBHelper(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) {

    }
}

应用B中,使用应用A的内容提供者,首先需要在应用B的AndroidManifest.xml中添加访问应用A的权限。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.chenpeng.contentresolverproject">

    <uses-permission android:name="com.king.qiangpiao.PRIVATE.all"/>
    <uses-permission android:name="com.king.qiangpiao.PRIVATE.read"/>
    <uses-permission android:name="com.king.qiangpiao.PRIVATE.write"/>
    .....
</manifest>

接下来就是在应用B中如何使用ContentResolver

public class MainActivity extends AppCompatActivity {

    private Button btn1,btn2,btn3,btn4,btn5;
    private ContentResolver mContentResolver;
    private Uri mBookUri,mUseUri;
    private static final String TAG = "MainActivity";
    private DatabaseContentObserver mContentObserver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn1 = findViewById(R.id.btn1);
        btn2 = findViewById(R.id.btn2);
        btn3 = findViewById(R.id.btn3);
        btn4 = findViewById(R.id.btn4);
        btn5 = findViewById(R.id.btn5);
        //内容监听器
        mContentObserver = new DatabaseContentObserver(new Handler());
        //内容解析者
        mContentResolver = getContentResolver();
        //Uri地址 对应应用A中定义的authorities值
        mBookUri = Uri.parse("content://com.king.qiangpiao.contentProvider/book");
        mUseUri = Uri.parse("content://com.king.qiangpiao.contentProvider/user");
        //注册内容监听器
        mContentResolver.registerContentObserver(mBookUri,true,mContentObserver);
       

        //insert 插入数据到应用A中
        btn1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ContentValues bookValues = new ContentValues();
                bookValues.put("_id",1);
                bookValues.put("name","NetEasy");
                mContentResolver.insert(mBookUri,bookValues);
                ContentValues userValues = new ContentValues();
                userValues.put("_id",1);
                userValues.put("name","DingLei");
                mContentResolver.insert(mUseUri,userValues);
            }
        });
        //update 更新数据
        btn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                ContentValues values = new ContentValues();
                values.put("_id",1);
                values.put("name","alibaba");
                String[] selectionArgs = {String.valueOf(1)};
                int count = mContentResolver.update(mBookUri,values,"_id = ?",selectionArgs);
                Log.i(TAG, "update : " + count );
            }
        });
        //delete 删除数据
        btn3.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int raw = mContentResolver.delete(mUseUri,"_id = ?" , new String[]{String.valueOf(1)});
                Log.i(TAG, "delete : " + raw );
            }
        });
        //query 查询
        btn4.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Cursor cursor = mContentResolver.query(mBookUri,new String[]{"book_id","book_name"},null,null,null);
                while(cursor.moveToNext()){
                    int id = cursor.getInt(0);
                    String name = cursor.getString(1);
                    Log.i(TAG, "query: id = " + id + " , name = " + name);
                }
                cursor.close();
            }
        });
    }

    @Override
    protected void onDestroy() {
        //注销内容监听者
        mContentResolver.unregisterContentObserver(mContentObserver);
        super.onDestroy();
    }


    class DatabaseContentObserver extends ContentObserver{

        public DatabaseContentObserver(Handler handler) {
            super(handler);
        }

        @Override
        public void onChange(boolean selfChange) {
            Log.i(TAG, "onChange: book 数据变化" );
            super.onChange(selfChange);
        }
    }
}

送大家一句名言,自己理解。“No amount of anxiety makes any difference to anything that is going to happen.”

上一篇下一篇

猜你喜欢

热点阅读