android基礎-contentProvider

知識點

  1. uri類
  2. ContentProvider,ContentResolver
  3. 實現跨進程訪問數據

一、Uri類

  1. uri定義:Uniform Resource Identifier,即統一資源標識符
  2. uri作用:外界進程通過 uri找到對應的contentProvider& 其中的數據,再進行數據操作
  3. uri詳解:
    uri分爲 系統預置 & 自定義,分別對應系統內置的數據(如通訊錄、日程表等等)和自定義數據庫
    自定義的一般格式如下圖:



    自定義uri的栗子

設置URI
Uri uri = Uri.parse("content://com.returnTolife.provider/test/1") ;

上述URI指向的資源是:名爲 `com.returnTolife.provider`的`ContentProvider` 中表名 爲`test` 中的 `id`爲1的數據

特別注意:URI模式存在匹配通配符* & #

*:匹配任意長度的任何有效字符的字符串
以下的URI 表示 匹配provider的任何內容
"content://com.returnTolife.provider/* "

#:匹配任意長度的數字字符的字符串
以下的URI 表示 匹配provider中的test表的所有行
"content://com.returnTolife.provider/test/# "

系統常用URI

二、ContentProvider和ContentResolver

  1. 關於ContentProvider:由於進程間共享數據的本質是:添加、刪除、獲取 & 修改(更新)數據,所以ContentProvider 其中的核心方法主要如下:
該方法在ContentProvider被創建後調用,當其他應用程序第一次訪問ContentProvider時,該ContentProvider被創建。
public boolean onCreate():

根據Uri查詢出selection條件所匹配的全部記錄
public Cursor query():

該方法返回當前Uri所代表的數據的MIME類型。如果該Uri對應的數據可能包括多條記錄,那麼MIME類型字符串應該以vnd.android.cursor.dir/開頭,單條記錄則爲vnd.android.cursor.item/開頭;
個人理解該方法返回的MIME在android主要是用來做頁面跳轉
public String getType()

根據該Uri插入values對應的數據。
public Uri insert():

根據Uri刪除selection條件所匹配的全部記錄。
public int delete():

根據Uri修改selection條件所匹配的全部記錄
public int update():

1.1 關於getType()的作用可以參考ContentProvider數據庫共享之——MIME類型與getType() ,文章中寫得很詳細,而且清晰易懂.
1.2 ContentProvider類並不會直接與外部進程交互,而是通過ContentResolver 類

  1. 關於ContentResolver :外部進程通過 ContentResolver類 從而與ContentProvider類進行交互,原因是:如果一款應用要使用多個ContentProvider,若需要了解每個ContentProvider的不同實現從而再完成數據交互,操作成本高並且難度會比較大所以再ContentProvider類上加多了一個 ContentResolver類對所有的ContentProvider進行統一管理。
    使用栗子
ContentResolver resolver =  getContentResolver(); 

Uri uri = Uri.parse("content://com.returnTolife.provider/test/"); 

Cursor cursor = resolver.query(uri, null, null, null, null); 
//其他方法類似,不重複
  1. UriMatcher類:該類爲contentProvider的輔助類,主要用於將一個uri地址與provider進行綁定
    栗子
  public static final int TABLE_STUDENT_CODE = 1;
  public static final int TABLE_BOOKE_CODE = 2;

static{
     //參數表示初始化時不匹配任何東西
     UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); 

      //matcher .addURI的作用是在後面使用matcher.match(string s)匹配的時候,如果匹配到/student"時返回TABLE_STUDENT_CODE即數值1,匹配“/book”時返回MATCH_SECOND
     matcher .addURI(AUTHORITY, "student", TABLE_STUDENT_CODE);
     matcher .addURI(AUTHORITY, "book", TABLE_BOOKE_CODE)
}


三、實例

  1. 數據準備,先創建一個數據庫
public class MyDbHelper extends SQLiteOpenHelper {


    private static final String DB_NAME="myprovider.db";

    public static final String TABLE_SUTEMT="student";
    public static final String TABLE_BOOK="book";

    private static final int VERSION=1;

    public MyDbHelper(Context context) {
        super(context, DB_NAME, null, VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_SUTEMT + "(_id INTEGER PRIMARY KEY AUTOINCREMENT," + " name TEXT)");
        db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_BOOK + "(_id INTEGER PRIMARY KEY AUTOINCREMENT," + " name TEXT)");
    }

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

    }
}
  1. 新建一個MyContentProvider類,繼承ContentProvider類
public class MyContentProvider extends ContentProvider {

    private Context mContext;

    private MyDbHelper mMyDbHelper;

    // 設置ContentProvider的唯一標識
    public static final String AUTOHORITY = "com.returntolife.myprovider";
   

    public static final int TABLE_STUDENT_CODE = 1;
    public static final int TABLE_BOOKE_CODE = 2;

    // UriMatcher類使用:在ContentProvider 中註冊URI
    private static final UriMatcher mMatcher;

    SQLiteDatabase db = null;

    //重要的一步,將uri和provider綁定
    static {
        mMatcher=new UriMatcher(UriMatcher.NO_MATCH);

        mMatcher.addURI(AUTOHORITY,MyDbHelper.TABLE_SUTEMT, TABLE_STUDENT_CODE);
        mMatcher.addURI(AUTOHORITY, MyDbHelper.TABLE_BOOK, TABLE_BOOKE_CODE);

    }

    @Override
    public boolean onCreate() {
        mContext=getContext();

        mMyDbHelper=new MyDbHelper(mContext);
        db=mMyDbHelper.getWritableDatabase();
        // 初始化兩個表的數據(先清空兩個表,再各加入一個記錄)
        db.execSQL("delete from student");
        db.execSQL("insert into student values(1,'張三');");
        db.execSQL("insert into student values(2,'李四');");

        db.execSQL("delete from book");
        db.execSQL("insert into book values(1,'Android');");
        db.execSQL("insert into book values(2,'iOS');");

        return true;
    }

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

        String table=getTableName(uri);

        return db.query(table,null,null,null,null,null,null);
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        LogUtil.d("getType uri="+uri);

        return "vnd.android.cursor.dir/harvic.first";
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {

        String table=getTableName(uri);

        db.insert(table,null,values);
        // 當該URI的ContentProvider數據發生變化時,通知外界(即訪問該ContentProvider數據的訪問者)
        mContext.getContentResolver().notifyChange(uri, null);

        return uri;
    }

    @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;
    }


    /**
     * 根據URI匹配 URI_CODE,從而匹配ContentProvider中相應的表名
     */
    private String getTableName(Uri uri){
        String tableName = null;
        switch (mMatcher.match(uri)) {
            case TABLE_STUDENT_CODE:
                tableName = MyDbHelper.TABLE_SUTEMT;
                break;
            case TABLE_BOOKE_CODE:
                tableName = MyDbHelper.TABLE_BOOK;
                break;
            default:
                break;
        }
        return tableName;
    }
}
  1. 在AndroidManifest.xml文件註冊這個ContentProvider,綁定一個Uri
     <provider
            android:authorities="com.returntolife.myprovider"
            android:name=".mycontentprovider.MyContentProvider"/>
  1. 自身內部訪問直接通過ContentResolver就可以訪問,不用添加什麼權限
 private void getData() {
        ContentResolver contentResolver=getContentResolver();

        Uri uri=Uri.parse("content://com.returntolife.myprovider/student");

        Cursor cursor=contentResolver.query(uri,new String[]{"_id,name"},null,null,null);
        if(cursor!=null){
            while(cursor.moveToNext()){
                LogUtil.d("id="+cursor.getInt(0)+"--name="+cursor.getString(1));
                tvTest.append("id="+cursor.getInt(0)+"--name="+cursor.getString(1)+"\n");
            }
            cursor.close();
        }

        Uri uriBook=Uri.parse("content://com.returntolife.myprovider/book");

        Cursor cursorBook=contentResolver.query(uriBook,new String[]{"_id,name"},null,null,null);
        if(cursorBook!=null){
            while(cursorBook.moveToNext()){
                LogUtil.d("id="+cursorBook.getInt(0)+"--name="+cursorBook.getString(1));
            }
            cursorBook.close();
        }
    }
  1. 外部進程訪問需要定義和添加權限
    5.1 提供數據的進程(進程1)中自定義一個訪問權限,並設置到對應的provider中,如下
   <permission
        android:name="com.returntolife.jjconde.providertestservice.provider"
        android:protectionLevel="normal" />

 <provider
            android:name=".mycontentprovider.MyContentProvider"
            android:authorities="com.returntolife.myprovider"
            android:exported="true"
            android:permission="com.returntolife.myprovider.permission" />

5.2 需要訪問數據的進程(進程2)中添加上面定義的訪問權限,然後其餘獲取數據基本和應用自身獲取數據一致

    <uses-permission android:name="com.returntolife.myprovider.permission"/>
        // 設置URI
        Uri uri=Uri.parse("content://com.returntolife.myprovider/student");

        // 插入表中數據
//        ContentValues values = new ContentValues();
//        values.put("_id", 10);
//        values.put("name", "Jordan");


        // 獲取ContentResolver
        ContentResolver resolver =  getContentResolver();
        Cursor cursor = resolver.query(uri, new String[]{"_id","name"}, null, null, null);
        if(cursor!=null){
            while (cursor.moveToNext()){
                Log.d("MainActivity","id="+cursor.getInt(0)+"--name="+cursor.getString(1)+"\n");
            }
            cursor.close();
        }

總結

  1. contentProvider的難點主要是在理解uri的定義,以及如何將uri和provider綁定,訪問方式其實不難
  2. contentProvider實現跨進程通信相對於AIDL的通信方式,會簡潔方便很多

參考文章

Android:關於ContentProvider的知識都在這裏了!
ContentProvider數據庫共享之——MIME類型與getType()

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章