Android四大組件之ContentProvider

前言

Hi,你們好,咱們又雙叒叕見面啦,爲了讓你們快速的學習Android知識,咱們天天都在更新文章,相信小夥伴們已經開始眼熟咱們了!這一期咱們講解ContentProvider(內容提供者)相關知識,他也是咱們近期更新的Android四大組件中最後一個。話很少說,讓咱們趕忙開始學習吧~java

簡介

ContentProvider是Android系統中爲開發者專門提供的不一樣應用間進行數據共享的組件,其提供了一套標準的接口用來獲取以及操做數據,准許開發者把本身的應用數據根據需求開放給其餘應用進行增刪改查,而無須擔憂直接開放數據庫權限而帶來的安全問題。系統預置了許多ContentProvider用於獲取用戶數據,例如消息、聯繫人、日程表等。android

具體形式以下圖所示:數據庫

使用方式

1 ContentResolver

在ContentProvider的使用過程當中,須要借用ContentResolver來控制ContentProvider所暴露處理的接口,做爲代理來間接操做ContentProvider以獲取數據。安全

在 Context.java 的源碼中以下抽象方法app

/** Return a ContentResolver instance for your application's package. */
    public abstract ContentResolver getContentResolver();

因此能夠在全部繼承Context的類中經過 getContentResovler() 方法獲取ContentResolveride

ContentResolver contentResolver = getContentResovler();
2 ContentProvider

ContentProvider做爲Android四大組件之一,並無Activity那樣複雜的生命週期,只有簡單地onCreate過程。學習

建立一個自定義ContentProvider的方式是繼承ContentProvider類並實現其六個抽象方法:ui

/** 
* @author: 下碼看花 
* date: 2019/8/8 
* description: ContentProvider例子 
*/
public class MyContentProvider extends ContentProvider {

    /**     
    * 執行初始化工做     
    * @return     
    */    
    @Override    
    public boolean onCreate() {
          return false;    
    }    

    /**     
    * 查詢數據     
    * @param uri     
    * @param projection     
    * @param selection     
    * @param selectionArgs     
    * @param sortOrder     
    * @return     
    */    
    @Override    
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String         selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
          return null;    
    }    

    /**     
    * 經過Uri返回對應的MIME類型     
    * @param uri     
    * @return     
    */    
    @Override    
    public String getType(@NonNull Uri uri) {
          return null;    
    }    

    /**     
    * 插入新數據     
    * @param uri     
    * @param values     
    * @return     
    */    
    @Override    
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        return null;   
    } 
    
    /**     
    * 刪除已有數據    
    * @param uri     
    * @param selection     
    * @param selectionArgs     
    * @return     
    */   
    @Override    
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[]         selectionArgs) {
        return 0;    
    }

    /**     
    * 更新數據     
    * @param uri     
    * @param values     
    * @param selection     
    * @param selectionArgs     
    * @return     
    */    
    @Override    
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String         selection, @Nullable String[] selectionArgs) {
        return 0;    
    }
}

以後,須要在AdnroidManifest.xml中對ContentProvider進行註冊spa

<!-- xxx爲上一層包名 -->
<provider
            android:name=".MyContentProvider"
            android:authorities="com.xmkh.MyContentProvider"
            android:exported="true"/>

name:自定義的ContentProvider的全稱類名。3d

authorities:自定義ContentProvider的惟一標識,外部應用經過該屬性值來訪問咱們的ContentProvider。所以該屬性值必須是惟一的,建議在命名時以包名爲前綴。

exported:代表是否容許其餘應用調用ContentProvider,true表示支持,false表示不支持。默認值根據開發者的屬性設置而會有所不一樣,若是包含 Intent-Filter 則默認值爲true,不然爲false。

3 Uri

觀察MyContentProvider中的幾個方法,能夠發現除了 onCreate() 方法外,其它五個抽象方法都包含了一個Uri(統一資源標識符)參數,經過這個對象能夠來匹配對應的請求。那麼從ContentProvider的數據操做方法能夠看出都依賴於Uri,而對於Uri有其固定的數據格式,例如:

content://com.xmkh.contentproviderdemo.MyContentProvider/article

好比,ContentProvider中操做的數據能夠都是從SQLite數據庫中獲取的,而數據庫中可能存在許多張表,這時候就須要用到Uri來代表是要操做哪一個數據庫、操做數據庫的哪張表了

/** 
* @author: 下碼看花 
* date: 2019/8/8 
* description: URI組裝代碼示例
*/
public class TestContract {

    public static final String CONTENT_AUTHORITY = "com.xmkh.contentproviderdemo.MyContentProvider";

    public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);

    public static final String PATH_ARTICLE = "article";

    public static final class TestEntry implements BaseColumns {

        public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon().appendPath(PATH_ARTICLE).build();

        protected static Uri buildUri(long id) {
            return ContentUris.withAppendedId(CONTENT_URI, id);
        }

        protected static final String TABLE_NAME = "article";

        public static final String COLUMN_NAME = "name";
    }
}

從上面代碼咱們能夠看到,咱們建立了一個
content://com.xmkh.contentproviderdemo.MyContentProvider/article的uri,而且開了一個靜態方法,用以在有新數據產生時根據id生成新的Uri。

那麼如何肯定一個Uri是要執行哪項操做呢?這裏須要用到UriMatcher來幫助ContentProvider匹配Uri,它僅包含了兩個方法:

addURI(String authority, String path, int code):用於向UriMatcher對象註冊Uri。authtity是在AndroidManifest.xml中註冊的ContentProvider的authority屬性值;path表示一個路徑,能夠設置爲通配符,#表示任意數字,*表示任意字符;二者組合成一個Uri,而code則表明該Uri對應的標識碼

match(Uri uri):匹配傳入的Uri。返回addURI()方法中傳遞的code參數,若是找不到匹配的標識碼則返回-1

實戰

講了這麼多,相信你們已經有一個初步的瞭解,爲了讓咱們加深記憶,跟我一塊兒寫一個demo吧!

首先,自定義一個ContentProvider,而後向其寫入和讀取數據,使用SQLite做爲ContentProvider的數據存儲地址和數據來源,所以須要先創建一個SQLiteOpenHelper,建立一個名爲"article.db"的數據庫,包含「article」和「author」兩張表:

/** 
* @author: 下碼看花 
* date: 2019/8/8 
* description: 演示demo數據庫
*/
public class DbOpenHelper extends SQLiteOpenHelper {
    
    /**
     * 數據庫名
     */
    private static final String DATA_BASE_NAME = "article.db";

    /**
     * 數據庫版本號
     */
    private static final int DATE_BASE_VERSION = 1;

    /**
     * 表名-文章
     */
    public static final String ARTICLE_TABLE_NAME = "article";

    /**
     * 表名-做者
     */
    public static final String AUTHOR_TABLE_NAME = "author";

    /**
     * 建立表-文章(兩列:主鍵自增加、文章名稱)
     */
    private final String CREATE_ARTICLE_TABLE = "create table " + ARTICLE_TABLE_NAME
            + "(_id integer primary key autoincrement, articleName text)";

    /**
     * 建立表-做者(三列:主鍵自增加、做者名、性別)
     */
    private final String CREATE_AUTHOR_TABLE = "create table " + AUTHOR_TABLE_NAME
            + "(_id integer primary key autoincrement, authorName text, sex text)";

    public DbOpenHelper(Context context) {
        super(context, DATA_BASE_NAME, null, DATE_BASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_ARTICLE_TABLE);
        db.execSQL(CREATE_AUTHOR_TABLE);
    }

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

    }


}

自定義ContentProvider
當中,getTableName(Uri uri)方法用於斷定Uri指向的數據庫表名
而後在initProviderData()方法中向數據庫插入一些原始數據

爲了方便你們理解,咱們將上述出現的代碼進行修改,展現給你們:

/** 
* @author: 下碼看花 
* date: 2019/8/8 
* description: ContentProvider例子 
*/
public class MyContentProvider extends ContentProvider {

    
    private Context mContext;

    private SQLiteDatabase sqLiteDatabase;

    public static final String AUTHORITY = "com.xmkh.MyContentProvider";

    public static final int ARTICLE_URI_CODE = 0;

    public static final int AUTHOR_URI_CODE = 1;

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

    static {
        uriMatcher.addURI(AUTHORITY, DbOpenHelper.ARTICLE_TABLE_NAME, ARTICLE_URI_CODE);
        uriMatcher.addURI(AUTHORITY, DbOpenHelper.AUTHOR_TABLE_NAME, AUTHOR_URI_CODE);
    }


    private String getTableName(Uri uri) {
        String tableName = null;
        switch (uriMatcher.match(uri)) {
            case ARTICLE_URI_CODE:
                tableName = DbOpenHelper.ARTICLE_TABLE_NAME;
                break;
            case AUTHOR_URI_CODE:
                tableName = DbOpenHelper.AUTHOR_TABLE_NAME;
                break;
        }
        return tableName;
    }

    public MyContentProvider() {

    }


    /**
     * 執行初始化工做
     *
     * @return
     */
    @Override
    public boolean onCreate() {
        mContext = getContext();
        initArticleProviderData();
        return false;
    }


    //初始化原始數據
    private void initArticleProviderData() {
        sqLiteDatabase = new DbOpenHelper(mContext).getWritableDatabase();
        sqLiteDatabase.beginTransaction();
        ContentValues contentValues = new ContentValues();
        contentValues.put("articleName", "Android四大組件之Activity");
        sqLiteDatabase.insert(DbOpenHelper.ARTICLE_TABLE_NAME, null, contentValues);
        contentValues.put("articleName", "Android四大組件之BroadcastReceiver");
        sqLiteDatabase.insert(DbOpenHelper.ARTICLE_TABLE_NAME, null, contentValues);
        contentValues.put("articleName", "Android四大組件之Service");
        sqLiteDatabase.insert(DbOpenHelper.ARTICLE_TABLE_NAME, null, contentValues);
        contentValues.clear();

        contentValues.put("authorName", "ptt");
        contentValues.put("sex", "女");
        sqLiteDatabase.insert(DbOpenHelper.AUTHOR_TABLE_NAME, null, contentValues);
        contentValues.put("authorName", "HiYoung");
        contentValues.put("sex", "男");
        sqLiteDatabase.insert(DbOpenHelper.AUTHOR_TABLE_NAME, null, contentValues);
        contentValues.put("authorName", "gy");
        contentValues.put("sex", "男");
        sqLiteDatabase.insert(DbOpenHelper.AUTHOR_TABLE_NAME, null, contentValues);
        sqLiteDatabase.setTransactionSuccessful();
        sqLiteDatabase.endTransaction();
    }


    /**
     * 查詢數據
     *
     * @param uri
     * @param projection
     * @param selection
     * @param selectionArgs
     * @param sortOrder
     * @return
     */
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        String tableName = getTableName(uri);
        if (tableName == null) {
            throw new IllegalArgumentException("Unsupported URI:" + uri);
        }
        return sqLiteDatabase.query(tableName, projection, selection, selectionArgs, null, null, sortOrder, null);
    }

    /**
     * 經過Uri返回對應的MIME類型
     *
     * @param uri
     * @return
     */
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }

    /**
     * 插入新數據
     *
     * @param uri
     * @param values
     * @return
     */
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        String tableName = getTableName(uri);
        if (tableName == null) {
            throw new IllegalArgumentException("Unsupported URI:" + uri);
        }
        sqLiteDatabase.insert(tableName, null, values);
        mContext.getContentResolver().notifyChange(uri, null);
        return uri;
    }

    /**
     * 刪除已有數據
     *
     * @param uri
     * @param selection
     * @param selectionArgs
     * @return
     */
    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        String tableName = getTableName(uri);
        if (tableName == null) {
            throw new IllegalArgumentException("Unsupported URI:" + uri);
        }
        int count = sqLiteDatabase.delete(tableName, selection, selectionArgs);
        if (count > 0) {
            mContext.getContentResolver().notifyChange(uri, null);
        }
        return count;
    }

    /**
     * 更新數據
     *
     * @param uri
     * @param values
     * @param selection
     * @param selectionArgs
     * @return
     */
    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        String tableName = getTableName(uri);
        if (tableName == null) {
            throw new IllegalArgumentException("Unsupported URI:" + uri);
        }
        int row = sqLiteDatabase.update(tableName, values, selection, selectionArgs);
        if (row > 0) {
            mContext.getContentResolver().notifyChange(uri, null);
        }
        return row;
    }

}

ContentProvider建立好切記必定要在AndroidManifest.xml中註冊!(前文已經提到了如何註冊,我就再也不復述啦~)

而後分別操做article和author兩張表,向其插入一條數據後Log輸出全部的數據

/** 
* @author: 下碼看花 
* date: 2019/8/8 
* description: ContentProvider實戰 
*/
public class MainActivity extends AppCompatActivity {
    
      private final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Uri articleUri = Uri.parse("content://com.xmkh.MyContentProvider/article");
        ContentValues contentValues = new ContentValues();
        contentValues.put("articleName", "Android四大組件之ContentProvider");
        getContentResolver().insert(articleUri, contentValues);
        Cursor articleCursor = getContentResolver().query(articleUri, new String[]{"_id", "articleName"}, null, null, null);
        if (articleCursor != null) {
            while (articleCursor.moveToNext()) {
                Log.e(TAG, "ID:" + articleCursor.getInt(articleCursor.getColumnIndex("_id"))
                        + "  ArticleName:" + articleCursor.getString(articleCursor.getColumnIndex("articleName")));
            }
            articleCursor.close();
        }

        Uri authorUri = Uri.parse("content://com.xmkh.MyContentProvider/author");
        contentValues.clear();
        contentValues.put("authorName", "Austen");
        contentValues.put("sex", "男");
        getContentResolver().insert(authorUri, contentValues);
        Cursor authorCursor = getContentResolver().query(authorUri, new String[]{"_id", "authorName", "sex"}, null, null, null);
        if (authorCursor != null) {
            while (authorCursor.moveToNext()) {
                Log.e(TAG, "ID:" + authorCursor.getInt(authorCursor.getColumnIndex("_id"))
                        + "  AuthorName:" + authorCursor.getString(authorCursor.getColumnIndex("authorName"))
                        + "  Sex:" + authorCursor.getString(authorCursor.getColumnIndex("sex")));
            }
            authorCursor.close();
        }
    }

}

獲得的輸出結果是:

結語

ContentProvider做爲Android的四大組件之一,雖然說咱們平時用的並很少,可是做爲安卓四大組件之一,其地位不容忽視。 小夥伴們趕忙上手實操,把它靈活的運用到項目中,讓咱們天天一塊兒快樂的進步吧~

PS:若是還有未看懂的小夥伴,歡迎加入咱們的QQ技術交流羣:892271582,裏面有各類大神回答小夥伴們遇到的問題哦~

相關文章
相關標籤/搜索