Android四大組件之ContentProvide(內容提供者)

1. 訪問私有數據庫

建立一個項目,在項目中利用SQLiteOpenHelper建立一個名稱爲account的數據庫,並在數據庫中建立一張名爲info的表。android

public class MyOpenHelper extends SQLiteOpenHelper {
    public MyOpenHelper(Context context) {
        super(context, "account.db", null, 1);
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table info (_id integer primary key autoincrement,name varchar(20),money varchar(20))");
        db.execSQL("insert into info('name','money') values ('張三','2000')");
        db.execSQL("insert into info ('name','money') values ('李四','5000')");   
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
    }
}

在MainActivity中須要調用如下代碼才能建立數據庫:數據庫

private MyOpenHelper myOpenHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    myOpenHelper = new MyOpenHelper(this);
    myOpenHelper.getReadableDatabase();
}

運行程序,咱們利用DDMS中的FileExplorer查看咱們的數據庫文件: 
這裏寫圖片描述api

從上圖能夠看到建立了一個account.db的數據庫,查看文件權限能夠看到,對其餘用戶沒有權限。安全

SQLiteDatabase有一個靜態的方法,能夠直接加載某個路徑下的數據庫文件。咱們再建立一個項目工程,在這個工程中用這個靜態方法訪問account.db。服務器

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    SQLiteDatabase db = SQLiteDatabase.openDatabase("/data/data/com.itheima.transation/databases/account.db", null, SQLiteDatabase.OPEN_READWRITE);
    Cursor cursor = db.query("info", null, null, null, null, null, null);
       if (cursor!=null && cursor.getCount()>0){        
            while (cursor.moveToNext()) {
                String name=cursor.getString(cursor.getColumnIndex("name"));
                String money=cursor.getString(cursor.getColumnIndex("money"));
                System.out.println("name"+name + "money"+money);
        }           
    } 
}

運行結果: 
這裏寫圖片描述 
這裏寫圖片描述ide

以上結果說明咱們使用SQLiteDatabase.openDatabase()方法打開數據庫須要有權限纔可以訪問。咱們改變account.db的訪問權限,使其餘用戶也能訪問該文件:工具

這裏寫圖片描述

再次運行程序,訪問account.db數據庫,這時候就可以訪問到數據庫中的數據了。查看日誌輸出以下:佈局

這裏寫圖片描述

這種方式雖然可以訪問到其餘應用程序的數據庫,可是這種方式須要手動改變其餘應用程序數據庫的訪問權限,而且這是一種很是不安全的操做,若是改變應用數據庫的訪問權限,其餘程序很容易修改數據庫的內容。那麼如何才能訪問其餘應用程序的數據庫呢?Google給咱們提供了Android中另外一個組件ContentProvider內容提供者來解決這個問題。學習

2. 內容提供者

ContentProvider(內容提供者)是Android中的四大組件之一,在通常的開發中,可能使用的比較少。this

ContentProvider爲不一樣的軟件之間數據共享,提供統了一套接口。也就是說,若是咱們想讓其餘的應用使用咱們本身程序內的數據,就可使用ContentProvider定義一組對外開放的接口,從而使得其餘的應用可使用我們應用的文件、數據庫內存儲的信息。

固然,本身開發的應用須要給其餘應用共享信息的需求可能比較少見,可是在Android系統中,不少系統自帶應用,好比聯繫人信息,圖片庫,音頻庫等應用,爲了對其餘應用暴露數據,因此就使用了ContentProvider機制。因此,學習ContentProvider的基本使用,在遇到獲取聯繫人信息,圖片庫,音頻庫等需求的時候,才能更好的開發。

2.1. 內容提供者的原理

內容提供者定義了一組對外開放的接口,使其餘應用能夠訪問本身的應用的數據庫內容,下圖是外部應用訪問系統聯繫人應用數據庫的原理圖: 
這裏寫圖片描述

從上圖能夠看出,普通外部應用不能夠直接訪問私有的數據庫,只能經過內容提供者訪問私有數據庫,內容提供者處於應用內部,在內容提供者中定義了一些訪問路徑匹配等操做,當外部應用經過路徑訪問私有數據庫時,內容提供者根據路徑匹配出具體的操做,將私有數據返回給外部應用。

2.2. 內容提供者的使用

(a)建立一個類繼承ContentProvider,實現其中的方法

public class AccountProvider extends ContentProvider {
    //當內容提供者建立的時候調用
    @Override
    public boolean onCreate() {
        return true;
    }
    //用於查詢數據庫數據,返回值是Cursor即結果的數據集
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
      String[] selectionArgs, String sortOrder) {
    }
    //返回MIME類型的字符串,若是返回null,說明沒有數據類型
    @Override
    public String getType(Uri uri) {
        return null;
    }
    //用於向數據庫插入記錄
    @Override
    public Uri insert(Uri uri, ContentValues values) {  
    }
    //用於刪除數據庫記錄
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
    }
    //用於更新數據庫記錄
    @Override
    public int update(Uri uri, ContentValues values, String selection,
    String[] selectionArgs) {   
    }
}

(b)清單文件中配置內容提供者

<provider
     android:name="com.itheima.transation.AccountProvider"
     //設置主機名
     android:authorities="com.itheima.account.provider" >
</provider>

(c)定義路徑匹配規則

//定義當路徑匹配成功後對指定的組件返回的返回碼
private static final int QUERYSUCESS = 1;
private static final int ADDSUCESS = 2;
private static final int DELSUCESS = 3;
private static final int UPDATESUCESS = 4;
//建立Uri的匹配對象
static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
    //增長匹配規則,參數1爲主機名,參數2爲路徑,參數3爲當路徑匹配成功後對指定的組件返回的返回碼,必須是正數。該主機名必須和清單文件中配置的主機名一致。
    matcher.addURI("com.itheima.account.provider", "query", QUERYSUCESS);
    matcher.addURI("com.itheima.account.provider", "add", ADDSUCESS); 
    matcher.addURI("com.itheima.account.provider", "delete", DELSUCESS);
    matcher.addURI("com.itheima.account.provider", "update", UPDATESUCESS);
}

注意:主機名必須和清單文件中配置的主機名一致。

Uri的組成規則:協議名://主機名或authority/路徑/ID。Uri的組成能夠參考下圖: 
這裏寫圖片描述

(d)建立SQLiteOpenHelper

public class MyOpenHelper extends SQLiteOpenHelper {    
    public MyOpenHelper(Context context) {
        super(context, "account.db", null, 1);
    }
    @Override
    public void onCreate(SQLiteDatabase db){        
        db.execSQL("create table info (_id integer primary key autoincrement,name varchar(20),money varchar(20))");
        db.execSQL("insert into info ('name','money') values ('張三','2000')");
        db.execSQL("insert into info ('name','money') values ('李四','5000')");   
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
    {       
    }
}

獲取數據庫,在ContentProvider中實現覆蓋的增刪改查方法:

@Override
public boolean onCreate() {
    //在onCreate()方法中建立SQLiteOpenHelper對象
    helper = new MyOpenHelper(getContext());
    return true;
}

//內容提供者的查詢方法
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[]
 selectionArgs, String sortOrder) {
     //匹配路徑,返回值是一個int類型的返回碼,若是匹配不成功,返回-1
    int code = matcher.match(uri);
    if (code == QUERYSUCESS) {
        SQLiteDatabase db = helper.getReadableDatabase();
        //調用SQLiteDatabase對象的query()查詢數據
        Cursor cursor = db.query("info", projection, selection, selectionArgs, null, null, sortOrder);
        return cursor;
    } else {
        throw new IllegalArgumentException("路徑不匹配 請檢查");
    }
}

@Override
public Uri insert(Uri uri, ContentValues values) {
    int code = matcher.match(uri);
    if (code == ADDSUCESS) {
        SQLiteDatabase db = helper.getReadableDatabase();
        long insert = db.insert("info", null, values);
        return Uri.parse("com.itheima.account.provider" + insert);
    } else {
        throw new IllegalArgumentException("路徑不匹配 請檢查");
    }
}

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
    int code = matcher.match(uri);
    if (code == DELSUCESS) {
        SQLiteDatabase db = helper.getReadableDatabase();
        int delete = db.delete("info", selection, selectionArgs);
        return delete;
    } else {
        throw new IllegalArgumentException("路徑不匹配 請檢查");
    }
}

@Override
public int update(Uri uri, ContentValues values, String selection, String[]
 selectionArgs) {
    int code = matcher.match(uri);
    if (code == UPDATESUCESS) {
        SQLiteDatabase db = helper.getReadableDatabase();
        int update = db.update("info", values, selection, selectionArgs);
        return update;
    } else {
        throw new IllegalArgumentException("路徑不匹配 請檢查");
    }
}

2.3. 訪問自定義內容提供者

內容提供者定義好了,那麼如何在其餘應用中訪問內容提供者提供的數據呢?這時候須要用到ContentResolver來訪問。 
建立另一個項目,在項目中利用ContentResolver來訪問其餘應用的內容提供者。 
界面佈局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="add"
        android:text="add" />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="del"
        android:text="del" />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="update"
        android:text="update" />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="find"
        android:text="find" />
</LinearLayout>

在MainActivity中經過Context獲取ContentResolver來訪問私有數據庫:

public class MainActivity extends Activity {

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

    public void add(View v){
        //定義訪問路徑
        Uri uri = Uri.parse("content://com.itheima.account.provider/add");
        //建立一系列ContentResolver可以處理的數據,其值爲key-value的結構,key必須和數據庫字段一致
        ContentValues values = new ContentValues();
        values.put("name", "zhaoqi"); 
        values.put("money", "1000"); 
        //經過Context對象獲取ContentResolver對象,調用ContentResolver對象的insert()方法插入數據,參數1爲訪問uri,參數2爲插入的數據 
        Uri insert = getContentResolver().insert(uri, values);
        System.out.println("uri--"+insert.toString());      
    }

    public void del(View v){
        Uri uri = Uri.parse("content://com.itheima.account.provider/delete");
        int delete = getContentResolver().delete(uri, "name=?", new String[]{"zhaoqi"});    
        System.out.println("delete=="+delete);      
    }

    public void update(View v){
        Uri uri = Uri.parse("content://com.itheima.account.provider/update");
        ContentValues values = new ContentValues();
        values.put("money", "20000");
        int update = getContentResolver().update(uri, values, "name=?", new String[]{"zhaoqi"});
        System.out.println("update--"+update);      
    }

    public void find(View v){
        Uri uri = Uri.parse("content://com.itheima.account.provider/query");
        Cursor cursor = getContentResolver().query(uri, null, null, null, null);
        if (cursor!=null && cursor.getCount()>0){   
                while (cursor.moveToNext()) {
                    String name = cursor.getString(cursor.getColumnIndex("name")); 
                    String money = cursor.getString(cursor.getColumnIndex("money"));    
                    System.out.println("name"+name + "money"+money);
                }               
        }       
    }
}

運行後,發現其餘應用就能夠經過內容提供者來訪問私有數據庫了。

注意:匹配路徑後面是能夠攜帶數字的,即Uri中的ID,Android中有一個方便的api來獲取這個值:

int id = (int) ContentUris.parseId(uri);

3. 短信備份

本案例經過內容提供者獲取系統短信數據庫中的短信數據,將這些短信數據經過XML序列化到文件中。 
系統短信數據的位置存放在系統短信應用包名下的databases下,以下圖: 
這裏寫圖片描述

導出數據庫,利用SQLiteStudio工具查看數據中的數據: 
這裏寫圖片描述

要備份短信數據,須要備份短信數據庫中sms表中的短信的address、date、sms三個字段的數據。此外當經過ContentResolver來查詢的時候須要提供訪問的Uri,那麼這個Uri如何獲取呢?能夠查看系統短信的源碼來獲得訪問Uri,由於短信源碼中確定定義了供其餘應用訪問的ContentProvider。

首先咱們找到源碼目錄,在清單文件中查找: 
這裏寫圖片描述

從上圖能夠得知短信Provider的主機名。 
接着找到源碼中SmsProvider這個類,查看Uri後面的path: 
這裏寫圖片描述

從上圖能夠得知訪問短信數據庫的Uri的權限是sms,查看匹配規則,第一個匹配規則的路徑爲null,表明查詢全部數據,這樣咱們就知道查詢短信數據庫的Uri爲content://sms/。 
接下來咱們經過ContentResolver來查詢短信數據庫並將數據序列化到文件中:

public void click(View v){      
    try {
        //建立一個xml序列化對象
        XmlSerializer serializer = Xml.newSerializer();
        //備份文件存儲位置
        File file = new File(Environment.getExternalStorageDirectory().getPath(),"smsback.xml");
        FileOutputStream fos = new FileOutputStream(file);
        serializer.setOutput(fos, "utf-8");
        serializer.startDocument("utf-8", true);
        serializer.startTag(null, "smss");
        Uri uri = Uri.parse("content://sms/");
        //經過ContentResolver獲取數據庫中的內容
        Cursor cursor = getContentResolver().query(uri, new String[]{"address","date","body"}, null, null, null);
        while(cursor.moveToNext()){             
            String address = cursor.getString(0);
            String date = cursor.getString(1);
            String body = cursor.getString(2);
            serializer.startTag(null,"sms");
            serializer.startTag(null, "address");
            serializer.text(address);
            serializer.endTag(null, "address"); 
            serializer.startTag(null, "date");
            serializer.text(date);
            serializer.endTag(null, "date");
            serializer.startTag(null, "body");
            serializer.text(body);
            serializer.endTag(null, "body");
            serializer.endTag(null, "sms");
        }
        cursor.close(); 
        serializer.endTag(null, "smss");
        serializer.endDocument();
        fos.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

運行結果: 
這裏寫圖片描述 
這裏寫圖片描述

導出到電腦查看: 
這裏寫圖片描述

4. 還原短信

利用ContentResolver向數據庫中插入一條短信。

public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    public void click(View v){
        Uri uri = Uri.parse("content://sms/");
        ContentValues values = new ContentValues();
        values.put("address", "95553"); 
        values.put("body", "您的餘額爲0.00元.....");
        values.put("date", System.currentTimeMillis());
        getContentResolver().insert(uri, values); 
    }
}

加入權限:

<uses-permission android:name="android.permission.WRITE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />

運行結果: 
這裏寫圖片描述

這裏寫圖片描述

5. 獲取聯繫人

首先咱們查看手機系統聯繫人和聯繫人數據庫。 
這裏寫圖片描述

這裏寫圖片描述

將contact2.db導出到電腦,使用SQLiteStudio查看數據庫,查看data表: 
這裏寫圖片描述

經過data表,能夠發現data1字段存儲的是聯繫人的信息,那麼如何區這些信息是哪一個聯繫人的呢?經過觀察能夠發現raw_contact_id表示的就是屬於某個聯繫人,而後查看raw_contacts表: 
這裏寫圖片描述

經過查看raw_contacts表,發現contact_id就與剛纔data表中的raw_contact_id是對應的。接下來須要知道data表中data1字段的信息是姓名呢,仍是電話或者其餘的類型,這時候就須要經過minetype_id來區分信息的類型,以下圖: 
這裏寫圖片描述

接着,到mimetypes表中查找對應的mimetype_id是什麼類型,以下圖: 
這裏寫圖片描述

從上圖的mimetypes表能夠看出,id爲5表示手機號碼,6表示姓名,1表示郵箱。 
接下來咱們就經過這幾張表查詢出聯繫人信息: 
(a)raw_contacts表:能夠獲得全部聯繫人的id 
contact_id:聯繫人id 
(b)data表:聯繫人的具體信息,一個信息佔一行 
data1:信息的具體內容 
raw_contact_id:聯繫人id,描述信息屬於哪一個聯繫人 
mimetype_id:描述信息是屬於什麼類型 
(c)mimetypes表:經過mimetype_id到該表查看具體類型

查詢聯繫人的步驟: 
1. 經過raw_contacts獲取聯繫人id,表查詢一共有多少個聯繫人; 
2. 經過聯繫人id獲取data表中的data1字段和mimetypes字段; 
3. 經過mimetypes表查詢類型。 
獲得瞭如何查詢到聯繫人後,接下來使用ContentResolver來查詢聯繫人信息,那麼問題來了,如何知道訪問的Uri呢?這又須要經過查看聯繫人源碼才能獲得Uri。查看ContactsProvider源碼清單文件以下圖:

這裏寫圖片描述

在Activity經過raw_contacts表查詢全部的contact_id即一共多少聯繫人:

Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
Cursor cursor = getContentResolver().query(uri, new String[]{"contact_id"}, null, null, null);
while (cursor.moveToNext()) {
    String contact_id = cursor.getString(0);
    System.out.println("contact_id====="+ contact_id);
}

運行結果: 
這裏寫圖片描述

運行出錯,查看日誌提示須要在清單文件中加入權限:

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

再次運行程序,運行結果獲取到了聯繫人id: 
這裏寫圖片描述

經過data表查詢data一、mimetype_id字段:

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[]{"contact_id"}, null, null, null);
while (cursor.moveToNext()) {
    String contact_id = cursor.getString(0);
    System.out.println("contact_id====="+ contact_id);
    Cursor dataCursor = getContentResolver().query(datauri, new String[] {"data1","mimetype_id"}, "raw_contact_id=?", new String[]{contact_id}, null);
    while (dataCursor.moveToNext()) {
        String data1 = dataCursor.getString(0);
        String mimetype_id = dataCursor.getString(1);
        System.out.println("data1 = "+data1+"-----mimetype_id = "+mimetype_id);
    }
}

運行結果報錯,提示無效的列mimetype_id: 
這裏寫圖片描述

爲何會報無效的列mimetype_id呢?這是由於系統的ContentProvider在作查詢的時候不是直接查詢的mimetype_id這個字段,而是查詢view_data這個視圖,這個視圖將data表和mimetypes表聯繫起來,因此查詢的字段應該是mimetypes表中的mimetype字段,以下圖: 
這裏寫圖片描述 
這裏寫圖片描述

咱們將查詢的字段改爲mimetype,再次查詢,運行結果以下: 
這裏寫圖片描述

拿到聯繫人信息後,判斷mimetype的類型:

if ("vnd.android.cursor.item/email_v2".equals(mimetype)) {
    System.out.println("郵件data:"+data1);
}else if("vnd.android.cursor.item/name".equals(mimetype)){
    System.out.println("姓名data:"+data1);
}else if("vnd.android.cursor.item/phone_v2".equals(mimetype)){
    System.out.println("電話號碼data:"+data1);
}

運行結果以下: 
這裏寫圖片描述

將數據封裝成對象,定義聯繫人實體bean:

public class Contact {
    private String id;
    private String name;
    private String phone;
    private String email;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    @Override
    public String toString() {
        return "Contact [id=" + id + ", name=" + name + ", phone=" + phone + ", email=" + email + "]";
    }
}

查詢聯繫人數據而且將數據封裝,而且建立聯繫人對象集合用來存儲聯繫人:

//建立保存聯繫人的集合
List<Contact> contactLists = new ArrayList<Contact>();              
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[]{"contact_id"}, null, null, null);
while(cursor.moveToNext()){
    String contact_id = cursor.getString(0); 
    //必定要判斷contact_id是否爲空,由於刪除聯繫人後,聯繫人的數據還會存在數據庫中,可是contact_id爲null
    if (contact_id != null) {
        //建立聯繫人對象
        Contact contact = new Contact(); 
        //設置聯繫人id
        contact.setId(contact_id);
        Cursor dataCursor = getContentResolver().query(datauri, new String[]{"data1","mimetype"}, "raw_contact_id=?", new String[]{contact_id}, null);
        while(dataCursor.moveToNext()){
            String data1 = dataCursor.getString(0);   
            String mimetype = dataCursor.getString(1); 
            if ("vnd.android.cursor.item/email_v2".equals(mimetype)) {
                //設置聯繫人email
                contact.setEmail(data1);
            }else if("vnd.android.cursor.item/name".equals(mimetype)){
                //設置聯繫人的姓名
                contact.setName(data1);
            }else 
              if("vnd.android.cursor.item/phone_v2".equals(mimetype)){
                //設置聯繫人電話
                contact.setPhone(data1);
            }   
        }
        //將聯繫人添加到集合中
        contactLists.add(contact);
    }
}

注意:第9行,判斷contact_id是否爲空,由於刪除聯繫人後,聯繫人的數據還會存在數據庫中,可是contact_id爲null; 
谷歌爲什麼這樣設計呢,是因爲,在國外,聯繫人的數據都會上傳到服務器,若是刪除聯繫人,那麼聯繫人的數據data1字段都須要刪除,那麼當同步的時候,就很是的麻煩。

6. 插入聯繫人

輸入姓名,電話,郵箱,點擊「還原」按鈕,將聯繫人數據插入到數據庫中。 
界面佈局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity" >
    <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="請輸入email" />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="click"
        android:text="還原" />
</LinearLayout>

MainActivity中實現插入聯繫人,插入數據以前先查詢raw_contacts表,查詢一共多少聯繫人數據,肯定新的聯繫人的id(查詢值+1),而後向raw_contacts表中插入新的聯繫人id,最後向data表中插入數據(raw_contact_id,mimetype_id,data1等)。

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 click(View v) {
        String email = et_email.getText().toString().trim();
        String name = et_name.getText().toString().trim();
        String phone = et_phone.getText().toString().trim();
        Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
        Uri datauri = Uri.parse("content://com.android.contacts/data");
        Cursor cursor = getContentResolver().query(uri, null, null, null, null);
        int count = cursor.getCount();
        int contact_id = count + 1; 
        ContentValues values = new ContentValues();
        values.put("contact_id", contact_id);
        getContentResolver().insert(uri, values);
        ContentValues nameValues = new ContentValues();
        nameValues.put("raw_contact_id", contact_id);
        nameValues.put("mimetype", "vnd.android.cursor.item/name");
        nameValues.put("data1", name);
        getContentResolver().insert(datauri, nameValues);
        ContentValues emailValues = new ContentValues();
        emailValues.put("raw_contact_id", contact_id);
        emailValues.put("mimetype", "vnd.android.cursor.item/email_v2");
        emailValues.put("data1", email);
        getContentResolver().insert(datauri, emailValues);
        ContentValues phoneValues = new ContentValues();
        phoneValues.put("raw_contact_id", contact_id);
        phoneValues.put("mimetype", "vnd.android.cursor.item/phone_v2");
        phoneValues.put("data1", phone);
        getContentResolver().insert(datauri, phoneValues);
    }
}

7. 內容觀察者

利用ContentResolver能夠獲取內容提供者提供的其餘應用是私有數據庫信息,可是若是有這樣的需求,當其餘應用的私有數據庫發生改變時,咱們的應用可以收到數據變化的通知,這裏就用到了ContentObserver內容觀察者來實現。 
接下來,利用內容觀察者實現短信數據庫變化:

public class MainActivity extends Activity {
    private Uri uri;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);         
        uri = Uri.parse("content://sms/");
        //調用getContentResolver().registerContentObserver()方法註冊內容觀察者,參數1表示須要觀察的Uri,參數2表當爲false表示觀察精確的Uri,true表示指定Uri或者指定Uri下的全部Uri都能匹配
        getContentResolver().registerContentObserver(uri, true, new MyObserver(new Handler())); 
    }

    //定義內容觀察者,該類繼承ContentObserver
    private class MyObserver extends ContentObserver{
        public MyObserver(Handler handler) {
            super(handler);
        }   

        //當內容發生變化的時候調用
        @Override
        public void onChange(boolean selfChange) {
            Cursor cursor = getContentResolver().query(uri, new String[]{"address","body"}, null, null, "date desc");
            cursor.moveToFirst();
            String address = cursor.getString(0);
            String body = cursor.getString(1);
            System.out.println("body:"+body+"address:"+address);
            super.onChange(selfChange);
        }   
    }
}

模擬器中模擬接收一條短信: 
這裏寫圖片描述

查看日誌打印,獲取到了最新的短信: 
這裏寫圖片描述

注意,在自定義的內容提供者中,咱們須要在改變數據庫數據後通知內容觀察者數據發生改變:

ContentResolver cr = getContext().getContentResolver();
//發出通知,全部註冊在這個uri上的內容觀察者均可以收到通知
cr.notifyChange(uri, null);
相關文章
相關標籤/搜索