1. 程式人生 > >小談Android四大元件之ContentProvider

小談Android四大元件之ContentProvider

內容提供者的作用

1.應用程式建立的資料庫預設都是私有的,別的應用程式不可以訪問裡面的資料.

2.如果需要把自己應用程式私有的資料庫暴露給別的使用者增刪改查,就需要使用內容提供者.

3.作用: 一個應用程式訪問另外一個應用程式在硬碟上儲存的資料

注:ContentProvider可以理解為:應用程式資料庫的後門.

編寫ContentProvider的流程

建立一個自定義內容提供者的Android專案:

1.寫一個類繼承ContentProvider, 重寫的所有方法(主要是增刪改查)預設都是空實現.

2.在清單檔案下宣告provider. 必須指定主機名!!!

<provider 
    android:name="內容提供者的包名"
    android:authorities="自定義主機名"><provider/>
注:主機名也就是對外提供的uri,可以任意自定義,一般的規則是應用程式包名.

3.在內容提供者程式碼內部定義UriMatcher -用於判斷uri是否匹配

//使用靜態變數和程式碼塊,便於自動載入.
static UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
mUriMatcher.addURI("在清單檔案裡面定義的authorities", "自定義匹配字串", 成功返回的標識);
}

注:1.自定義匹配字串規則: 一般使用被操作的資料庫中的表名.
   2.成功返回標識:SUCCESS, ERROR

4.在內容提供者執行增刪改查方法時,判斷uri是否合法

int code=matcher.match(uri);
if(code==SUCCESS){
    //資料庫增刪改查的邏輯
}else{
    throw new IllegalArgumentException("口令不正確,")
}   

5.例項化資料

MyDBHelper helper = new MyDBHelper(this);
helper.getWritableDatabase();

再建立另一個Android專案訪問內容提供者:

在MainActivity裡寫對資料庫增刪改查的邏輯方法

1.建立內容提供者解析器
ContentResolver resolver=上下文.getContentResolver();
2.定義要訪問的Uri路徑
Uri uri = Uri.parse("content://自定義主機名/自定義匹配字串")
//注意:content://是標準寫法
3.通過內容解析器執行操作:增,刪,改,查
如新增:resolver.insert(uri, values);

小結:

    1.寫一個類繼承ContentProvider,實現增刪改查的方法
    2.在清單檔案中配置內容提供者,指定android:authorities="com.yashiro.database"
    3.在內容提供者程式碼的內部,宣告uriMatcher
    4.通過uriMatcher檢查uri的路徑是否正確
    5.在另外一個應用程式裡面,通過contentResolver 增刪改查

案例

<案例一: 用內容提供者作業系統簡訊>

思路: 直接訪問系統簡訊應用的內容提供者,實現對簡訊應用資料庫的增加和刪除操作.

步驟:

1.通過查詢系統原始碼,可以確定簡訊息內容提供者的Uri 應該為:content://sms
2.檢視Android 模擬器下的/data/data/com.android.providers.telephony/databases/目錄,檢視其mmssms.db檔案,確定sms表儲存的資料格式:
    *其中,address 儲存的是聯絡人號碼,date 是傳送日期,type 對應簡訊的型別(傳送是1/接收是2),body是簡訊的主體內容。
3.建立一個Android專案,在MainActivity裡寫刪除和新增簡訊的業務邏輯
    1).配置清單檔案,新增簡訊的讀寫許可權
    <uses-permission android:name="android.permission.READ_SMS"/>
    <uses-permission android:name="android.permission.WRITE_SMS"/>

    2).實現新增和刪除業務邏輯

        /**
     * 利用內容提供者,新增簡訊
     * 
     * @param view
     */
    public void add(View view) {
        ContentResolver resolver = getContentResolver();
        Uri uri = Uri.parse("content://sms");
        ContentValues values = new ContentValues();
        values.put("address", "110");
        values.put("type", 1);
        values.put("date", System.currentTimeMillis());
        values.put("body", "小子誒....你小心了,我們盯上你le~~");
        resolver.insert(uri, values);
        Toast.makeText(this, "新增成功", 0).show();
    }

    /**
     * 利用內容提供者,刪除簡訊
     * 
     * @param view
     */
    public void delete(View view) {
        ContentResolver resolver = getContentResolver();
        Uri uri = Uri.parse("content://sms");
        resolver.delete(uri, "address=?", new String[] { "110" });
        Toast.makeText(this, "刪除成功", 0).show();
    }

<案例二: 用內容提供者作業系統聯絡人>

開發現狀:

現在很多App 都可以對系統聯絡人進行操作,比如微信就可以直接將號碼新增到系統聯絡人中,比如QQ 可以關聯/備份/恢復系統聯絡人。

完成這些操作,需要能夠訪問並修改系統聯絡人的資料庫.

思路: 直接訪問系統聯絡人應用的內容提供者,實現對聯絡人資料的增加和刪除操作.

步驟:

1.開啟Android 原始碼,檢視packages\providers\路徑下的檔案,其中ContactsProvider 就是聯絡人的內容提供者.找到清單檔案,發現內容提供者的類名為ContactsProvider2.java.

這裡寫圖片描述

2.根據原碼確定Uri資訊如下:

操作raw_contacts 表的Uri:
content://com.android.contacts/raw_contacts
操作data 表的Uri:
content://com.android.contacts/data

3.注意事項:

*由於contacts2.db 資料庫使用了檢視,所以操作資料庫表時,看到的表字段名稱和真實操作的有所不同。
*比如:data 表在查詢的時候沒有mimetype_id 欄位,取代的是mimetype 欄位。
*使用指南:
    1.查詢raw_contact表 獲取聯絡人的contact_id.
    2.根據contact_id查詢data表,獲取聯絡人的資料
    3.根據mimetype確定資料的型別

4.查詢手機聯絡人

步驟:

1).建立一個專案,配置清單檔案,新增讀取聯絡人的許可權

android:readPermission="android.permission.READ_CONTACTS"

2).建立一個聯絡人bean,有資訊name,phone,Email,QQ.

3).建立一個聯絡人工具類,獲取所有的聯絡人資訊,放到一個List集合中返回.

    public class ContactInfoUtils {
    /**
     * 獲取所有的聯絡人資訊
     * 
     * @param context
     *            上下文
     * @return
     */
    public static List<ContactInfo> getAllContactInfos(Context context) {
        List<ContactInfo> infos = new ArrayList<ContactInfo>();
        ContentResolver resolver = context.getContentResolver();
        // 查詢raw_contact表
        Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
        Uri datauri = Uri.parse("content://com.android.contacts/data");
        Cursor cursor = resolver.query(uri, new String[] { "contact_id" },
                null, null, null);
        //根據遊標判斷是否有資料
        while (cursor.moveToNext()) {
            String id = cursor.getString(0);
            System.out.println("Id:" + id);
            if (id != null) {
                ContactInfo info = new ContactInfo();
                // 查詢data表
                Cursor datacursor = resolver.query(datauri, new String[] {
                        "data1", "mimetype" }, "raw_contact_id=?",
                        new String[] { id }, null);
                //根據遊標判斷是否有資料
                while (datacursor.moveToNext()) {
                    String data1 = datacursor.getString(0);
                    String mimetype = datacursor.getString(1);
                    if ("vnd.android.cursor.item/name".equals(mimetype)) {
                        info.setName(data1);
                    } else if ("vnd.android.cursor.item/im".equals(mimetype)) {
                        info.setQq(data1);
                    } else if ("vnd.android.cursor.item/email_v2"
                            .equals(mimetype)) {
                        info.setEmail(data1);
                    } else if ("vnd.android.cursor.item/phone_v2"
                            .equals(mimetype)) {
                        info.setPhone(data1);
                    }
                }
                datacursor.close();
                infos.add(info);
            }
        }
        cursor.close();
        return infos;
    }

}

4).在MainActiviry裡獲取聯絡人資訊的集合,並顯示到介面上

infos = ContactInfoUtils.getAllContactInfos(this);
ListView lv = (ListView) findViewById(R.id.lv);
//設定介面卡
lv.setAdapter(new BaseAdapter() {
    @Override
    public int getCount() {
        return infos.size();
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        TextView tv = new TextView(MainActivity.this);
        tv.setText(infos.get(position).toString());
        return tv;
    }
    ...

5.新增手機聯絡人

步驟:

1).新建一個Android專案,配置清單檔案

 <!-- 讀取聯絡人 寫入聯絡人 -->
    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    <uses-permission android:name="android.permission.WRITE_CONTACTS"/>

2).設定佈局檔案,獲取使用者錄入的姓名,電話,郵箱資訊

<EditText
        android:id="@+id/et_name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="姓名" />

    <EditText
        android:id="@+id/et_phone"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="電話" />

    <EditText
        android:id="@+id/et_email"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="郵箱" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="save"
        android:text="儲存資料" />'

3).在MainActivity裡寫實現新增的業務邏輯

    public class MainActivity extends Activity {

    private EditText et_name;
    private EditText et_phone;
    private EditText et_email;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //關心控制元件
        et_name = (EditText) findViewById(R.id.et_name);
        et_phone = (EditText) findViewById(R.id.et_phone);
        et_email = (EditText) findViewById(R.id.et_email);
    }

    public void save(View view) {
        //獲取輸入資訊
        String name = et_name.getText().toString().trim();
        String phone = et_phone.getText().toString().trim();
        String email = et_email.getText().toString().trim();
        //定義要訪問的Uri路徑
        Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
        Uri datauri = Uri.parse("content://com.android.contacts/data");
        //獲取遊標
        Cursor cursor = getContentResolver().query(uri, new String[] { "_id" },
                null, null, "_id desc");
        //遊標初始化到開始位置
        cursor.moveToFirst();
        int _id = cursor.getInt(0);
        int newId = _id + 1;
        //文字資料
        ContentValues values = new ContentValues();
        values.put("contact_id", newId);
        //根據uri新增資料
        getContentResolver().insert(uri, values);

        // 往data表裡面新增資料.
        // 1.新增name
        ContentValues nameValue = new ContentValues();
        nameValue.put("raw_contact_id", newId);
        nameValue.put("data1", name);
        nameValue.put("mimetype", "vnd.android.cursor.item/name");
        getContentResolver().insert(datauri, nameValue);

        // 2.新增email
        ContentValues emailValue = new ContentValues();
        emailValue.put("raw_contact_id", newId);
        emailValue.put("data1", email);
        emailValue.put("mimetype", "vnd.android.cursor.item/email_v2");
        getContentResolver().insert(datauri, emailValue);

        // 3.新增phone
        ContentValues phoneValue = new ContentValues();
        phoneValue.put("raw_contact_id", newId);
        phoneValue.put("data1", phone);
        phoneValue.put("mimetype", "vnd.android.cursor.item/phone_v2");
        getContentResolver().insert(datauri, phoneValue);

        Toast.makeText(this, "新增資料成功", 0).show();
    }
}

內容觀察者 ContentObserver

•觀察資料庫內容是否發生改變,如果改變,通知觀察者

使用步驟:

1.在內容提供者ContentProvider的增刪改查方法中,增加通知的方法:

getContext().getContentResolver().notifyChange(uri, null);

2.在觀察者類註冊觀察者,監控內容提供者的方法

//定義觀察的uri
Uri uri = Uri.parse("content://被觀察的主機名/資料庫表名");
//註冊觀察者
getContentResolver().registerContentObserver(uri, true, new ContentObserver(new Handler()) {

            @Override
            public void onChange(boolean selfChange) {
                System.out.println("我是觀察者,我發現銀行的資料庫變化了.");
                super.onChange(selfChange);
            }

        });

小結:

內容觀察者一般配合內容提供者一起使用.讓內容提供者即向另外的一個程式暴露了私有的資料庫,又將資料庫的變化情況告訴給了觀察者.

未完待續…