1. 程式人生 > >Android ContentProvider基本用法

Android ContentProvider基本用法

truct 數據共享 implement notify username 資源 per mime類型 exc

轉自:https://www.jianshu.com/p/601086916c8f

一、基本概念

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

二、ContentResolver

在ContentProvider的使用過程中,需要借用ContentResolver來控制ContentProvider所暴露處理的接口,作為代理來間接操作ContentProvider以獲取數據。

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

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

所以可以在所有繼承Context的類中通過 getContentResovler() 方法獲取ContentResolver

ContentResolver contentResolver = getContentResovler();

ContentProvider中的幾個抽象方法在ContentResolver中均有著一一對應的同名方法

三、繼承ContentProvider

創建一個自定義ContentProvider的方式是繼承ContentProvider類並實現其六個抽象方法
例如

public class MyContentProvider extends ContentProvider {

    public MyContentProvider() {
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // Implement this to handle requests to delete one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public String getType(Uri uri) {
        // TODO: Implement this to handle requests for the MIME type of the data
        // at the given URI.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO: Implement this to handle requests to insert a new row.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public boolean onCreate() {
        // TODO: Implement this to initialize your content provider on startup.
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        // TODO: Implement this to handle query requests from clients.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        // TODO: Implement this to handle requests to update one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }
    
}

之後,需要在AdnroidManifest.xml中對ContentProvider進行註冊

        <provider
            android:name=".MyContentProvider"
            android:authorities="com.czy.contentprovideremo.MyContentProvider"
            android:exported="true" />
  • name:ContentProvider的全稱類名
  • authorities:唯一標識了一個ContentProvider,外部應用通過該屬性值來訪問我們的ContentProvider。因此該屬性值必須是唯一的,建議在命名時以包名為前綴
  • exported:表明是否允許其他應用調用ContentProvider,true表示支持,false表示不支持。默認值根據開發者的屬性設置而會有所不同,如果包含 Intent-Filter 則默認值為true,否則為false

ContentProvider的六個抽象方法的含義分別是:

  • onCreate():代表ContentProvider的創建,可以用來進行一些初始化操作
  • getType(Uri uri):用來返回一個Uri請求所對應的MIME類型
  • insert(Uri uri, ContentValues values):插入數據
  • query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder):查詢數據
  • update(Uri uri, ContentValues values, String selection, String[] selectionArgs):更新數據
  • delete(Uri uri, String selection, String[] selectionArgs):刪除數據

四、URI

觀察MyContentProvider中的幾個方法,可以發現除了 onCreate() 方法外,其它五個抽象方法都包含了一個Uri(統一資源標識符)參數,通過這個對象可以來匹配對應的請求。
URI 代表了要操作的數據對象,主要包含了兩部分信息:

  • 需要操作的哪個ContentProvider
  • 對ContentProvider中的哪些數據進行操作

一個URI由以下幾個部分組成:

  • Scheme:ContentProvider的Scheme被規定為“content://”
  • Authority:用於唯一標識某個ContentProvider,外部調用者根據這個標識來指定要操作的ContentProvider
  • Path:用來標識要操作的具體數據

比如,ContentProvider中操作的數據可以都是從SQLite數據庫中獲取的,而數據庫中可能存在許多張表,這時候就需要用到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

這裏來做一個小Demo,自定義一個ContentProvider,然後向其寫入和讀取數據
使用SQLite作為ContentProvider的數據存儲地址和數據來源,因此需要先建立一個SQLiteOpenHelper
創建一個名為"book.db"的數據庫,包含“book”和“user”兩張表

/**
 * 作者: 葉應是葉
 * 時間: 2017/4/3 12:07
 * 描述:
 */
public class DbOpenHelper extends SQLiteOpenHelper {

    //數據庫名
    private static final String DATA_BASE_NAME = "book.db";

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

    //表名-書
    public static final String BOOK_TABLE_NAME = "book";

    //表名-用戶
    public static final String USER_TABLE_NAME = "user";

    //創建表-書(兩列:主鍵自增長、書名)
    private final String CREATE_BOOK_TABLE = "create table " + BOOK_TABLE_NAME
            + "(_id integer primary key autoincrement, bookName text)";

    //創建表-用戶(三列:主鍵自增長、用戶名、性別)
    private final String CREATE_USER_TABLE = "create table " + USER_TABLE_NAME
            + "(_id integer primary key autoincrement, userName 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_BOOK_TABLE);
        db.execSQL(CREATE_USER_TABLE);
    }

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

    }

}

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

public class BookProvider extends ContentProvider {

    private Context context;

    private SQLiteDatabase sqLiteDatabase;

    public static final String AUTHORITY = "com.czy.contentproviderdemo.BookProvider";

    public static final int BOOK_URI_CODE = 0;

    public static final int USER_URI_CODE = 1;

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

    static {
        uriMatcher.addURI(AUTHORITY, DbOpenHelper.BOOK_TABLE_NAME, BOOK_URI_CODE);
        uriMatcher.addURI(AUTHORITY, DbOpenHelper.USER_TABLE_NAME, USER_URI_CODE);
    }

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

    public BookProvider() {

    }

    @Override
    public boolean onCreate() {
        context = getContext();
        initProviderData();
        return false;
    }

    //初始化原始數據
    private void initProviderData() {
        sqLiteDatabase = new DbOpenHelper(context).getWritableDatabase();
        sqLiteDatabase.beginTransaction();
        ContentValues contentValues = new ContentValues();
        contentValues.put("bookName", "數據結構");
        sqLiteDatabase.insert(DbOpenHelper.BOOK_TABLE_NAME, null, contentValues);
        contentValues.put("bookName", "編譯原理");
        sqLiteDatabase.insert(DbOpenHelper.BOOK_TABLE_NAME, null, contentValues);
        contentValues.put("bookName", "網絡原理");
        sqLiteDatabase.insert(DbOpenHelper.BOOK_TABLE_NAME, null, contentValues);
        contentValues.clear();

        contentValues.put("userName", "葉");
        contentValues.put("sex", "女");
        sqLiteDatabase.insert(DbOpenHelper.USER_TABLE_NAME, null, contentValues);
        contentValues.put("userName", "葉葉");
        contentValues.put("sex", "男");
        sqLiteDatabase.insert(DbOpenHelper.USER_TABLE_NAME, null, contentValues);
        contentValues.put("userName", "葉應是葉");
        contentValues.put("sex", "男");
        sqLiteDatabase.insert(DbOpenHelper.USER_TABLE_NAME, null, contentValues);
        sqLiteDatabase.setTransactionSuccessful();
        sqLiteDatabase.endTransaction();
    }

    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        String tableName = getTableName(uri);
        if (tableName == null) {
            throw new IllegalArgumentException("Unsupported URI:" + uri);
        }
        sqLiteDatabase.insert(tableName, null, values);
        context.getContentResolver().notifyChange(uri, null);
        return uri;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, 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);
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, 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) {
            context.getContentResolver().notifyChange(uri, null);
        }
        return row;
    }

    @Override
    public int delete(Uri uri, String selection, 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) {
            context.getContentResolver().notifyChange(uri, null);
        }
        return count;
    }

}

註冊ContentProvider

        <provider
            android:name=".BookProvider"
            android:authorities="com.czy.contentproviderdemo.BookProvider" />

然後分別操作book和user兩張表,向其插入一條數據後Log輸出所有的數據

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 bookUri = Uri.parse("content://com.czy.contentproviderdemo.BookProvider/book");
        ContentValues contentValues = new ContentValues();
        contentValues.put("bookName", "叫什麽名字好呢");
        getContentResolver().insert(bookUri, contentValues);
        Cursor bookCursor = getContentResolver().query(bookUri, new String[]{"_id", "bookName"}, null, null, null);
        if (bookCursor != null) {
            while (bookCursor.moveToNext()) {
                Log.e(TAG, "ID:" + bookCursor.getInt(bookCursor.getColumnIndex("_id"))
                        + "  BookName:" + bookCursor.getString(bookCursor.getColumnIndex("bookName")));
            }
            bookCursor.close();
        }

        Uri userUri = Uri.parse("content://com.czy.contentproviderdemo.BookProvider/user");
        contentValues.clear();
        contentValues.put("userName", "葉葉葉");
        contentValues.put("sex", "男");
        getContentResolver().insert(userUri, contentValues);
        Cursor userCursor = getContentResolver().query(userUri, new String[]{"_id", "userName", "sex"}, null, null, null);
        if (userCursor != null) {
            while (userCursor.moveToNext()) {
                Log.e(TAG, "ID:" + userCursor.getInt(userCursor.getColumnIndex("_id"))
                        + "  UserName:" + userCursor.getString(userCursor.getColumnIndex("userName"))
                        + "  Sex:" + userCursor.getString(userCursor.getColumnIndex("sex")));
            }
            userCursor.close();
        }
    }

}

得到的輸出結果是:

04-03 16:54:34.010 25151-25151/com.czy.contentproviderdemo E/MainActivity: ID:1  BookName:數據結構
04-03 16:54:34.010 25151-25151/com.czy.contentproviderdemo E/MainActivity: ID:2  BookName:編譯原理
04-03 16:54:34.010 25151-25151/com.czy.contentproviderdemo E/MainActivity: ID:3  BookName:網絡原理
04-03 16:54:34.010 25151-25151/com.czy.contentproviderdemo E/MainActivity: ID:4  BookName:叫什麽名字好呢
04-03 16:54:34.018 25151-25151/com.czy.contentproviderdemo E/MainActivity: ID:1  UserName:葉  Sex:女
04-03 16:54:34.018 25151-25151/com.czy.contentproviderdemo E/MainActivity: ID:2  UserName:葉葉  Sex:男
04-03 16:54:34.018 25151-25151/com.czy.contentproviderdemo E/MainActivity: ID:3  UserName:葉應是葉  Sex:男
04-03 16:54:34.018 25151-25151/com.czy.contentproviderdemo E/MainActivity: ID:4  UserName:葉葉葉  Sex:男

六、獲取聯系人信息

獲取聯系人列表

public void showContacts(View view) {
        ContentResolver contentResolver = getContentResolver();
        Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
        if (cursor == null) {
            tv_contacts.setText("顯示聯系人錯誤");
            return;
        }
        StringBuilder stringBuilder = new StringBuilder();
        while (cursor.moveToNext()) {
            stringBuilder.append("ID:");
            String contactId = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
            stringBuilder.append(contactId);

            stringBuilder.append("\t\t");
            stringBuilder.append("名字:");
            String contactName = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
            stringBuilder.append(contactName);

            // 根據聯系人ID查詢對應的電話號碼
            Cursor phonesCursor = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                    null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + contactId, null, null);
            // 取得電話號碼(可能會存在多個號碼)
            if (phonesCursor != null) {
                stringBuilder.append("\t\t");
                stringBuilder.append("號碼:");
                while (phonesCursor.moveToNext()) {
                    String phoneNumber = phonesCursor.getString(phonesCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                    stringBuilder.append(phoneNumber);
                    stringBuilder.append("\n");
                }
                phonesCursor.close();
            }
        }
        cursor.close();
        tv_contacts.setText(stringBuilder.toString());
    }

添加指定名字的聯系人

 public void addContact(View view) {
        ContentValues values = new ContentValues();
        //首先向RawContacts.CONTENT_URI執行一個空值插入,目的是獲取系統返回的rawContactId
        Uri rawContactUri = getContentResolver().insert(ContactsContract.RawContacts.CONTENT_URI, values);
        long rawContactId = ContentUris.parseId(rawContactUri);
        //往data表入姓名數據
        values.clear();
        values.put(ContactsContract.Data.RAW_CONTACT_ID, rawContactId);
        values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
        values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, "葉應是葉");
        getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI, values);
        //往data表入電話數據
        values.clear();
        values.put(android.provider.ContactsContract.Contacts.Data.RAW_CONTACT_ID, rawContactId);
        values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
        values.put(ContactsContract.CommonDataKinds.Phone.NUMBER, "19950724");
        values.put(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE);
        getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI, values);
        //往data表入Email數據
        values.clear();
        values.put(android.provider.ContactsContract.Contacts.Data.RAW_CONTACT_ID, rawContactId);
        values.put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
        values.put(ContactsContract.CommonDataKinds.Email.DATA, "[email protected]");
        values.put(ContactsContract.CommonDataKinds.Email.TYPE, ContactsContract.CommonDataKinds.Email.TYPE_WORK);
        getContentResolver().insert(android.provider.ContactsContract.Data.CONTENT_URI, values);
    }

需要添加聯系人讀寫權限

    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.WRITE_CONTACTS" />

這裏提供示例代碼下載:Android ContentProvider基本用法



作者:葉應是葉
鏈接:https://www.jianshu.com/p/601086916c8f
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請註明出處。

Android ContentProvider基本用法