android菜鳥學習筆記23----ContentProvider(三)利用內建ContentProvider監聽簡訊及查看連絡人,contentprovider
要使用一個ContentProvider,必須要知道的是它所能匹配的Uri及其資料存放區的表的結構。
首先想辦法找到訪問簡訊及連絡人資料的ContentProvider能接受的Uri:
到github上找對應ContentProvider的源碼:https://github.com/android
有好多個,哪一個才是簡訊資料的ContentProvider呢?
在filters輸入框:輸入telephony。
現在只有一個了,開啟:
裝有git的話,可以選擇clone到本地,沒有的話,就選擇下載zip包就行了。
進入src目錄中,SmsProvider.java就是簡訊資料的ContentProvider了。查看原始碼,搜尋UriMatcher:
1 private static final UriMatcher sURLMatcher = 2 3 new UriMatcher(UriMatcher.NO_MATCH); 4 5 6 7 static { 8 9 sURLMatcher.addURI("sms", null, SMS_ALL);10 11 sURLMatcher.addURI("sms", "#", SMS_ALL_ID);12 13 sURLMatcher.addURI("sms", "inbox", SMS_INBOX);14 15 sURLMatcher.addURI("sms", "inbox/#", SMS_INBOX_ID);16 17 sURLMatcher.addURI("sms", "sent", SMS_SENT);18 19 sURLMatcher.addURI("sms", "sent/#", SMS_SENT_ID);20 21 sURLMatcher.addURI("sms", "draft", SMS_DRAFT);22 23 sURLMatcher.addURI("sms", "draft/#", SMS_DRAFT_ID);24 25 sURLMatcher.addURI("sms", "outbox", SMS_OUTBOX);26 27 sURLMatcher.addURI("sms", "outbox/#", SMS_OUTBOX_ID);
可以看到跟之前自己定義ContentProvider的類似的靜態代碼塊,其實是模仿它的源碼寫的。
從上面的addURI()方法,可以知道擷取全部簡訊資料的URI就是:”content://sms”
這樣,我們就可以訪問到所有的簡訊內容了。
為了有簡訊給我們看,需要在模擬器上收發下簡訊:
在window->show view->other->找到Emulator Control,類比110傳送簡訊:
hello, you are wanted.
在模擬器中可以回複簡訊,現在模擬器中,有如下三條簡訊:
查看簡訊代碼如下:
1 protected void onCreate(Bundle savedInstanceState) { 2 3 super.onCreate(savedInstanceState); 4 5 setContentView(R.layout.main_layout); 6 7 Uri uri = Uri.parse("content://sms"); 8 9 Cursor cursor = getContentResolver().query(uri, null, null, null, null);10 11 if(cursor == null){12 13 return;14 15 }16 17 while(cursor.moveToNext()){18 19 Log.i(TAG,cursor.getString(cursor.getColumnIndex("body")));20 21 }22 23 Log.i(TAG,cursor.getCount()+"");24 25 }
要查看簡訊內容是需要許可權的:
在Manifest.xml聲明使用許可權:
<uses-permission android:name="android.permission.READ_SMS"/>
運行結果:
可見,取出了簡訊的總數及每條簡訊的內容。
但是,怎樣才能知道簡訊實際儲存呢?
可以在File Explorer中,找到/data/data/com.android.providers.telephony/databases/mmssms.db,該資料庫存放的就由簡訊的資料表,將其匯出到電腦,可以用Sqlite Expert查看sms表:
可知,address欄位為收件者或寄件者號碼,date為簡訊接收或發送的時間戳記,type是簡訊的類型,1表示接收的簡訊,2表示發送的簡訊,body是簡訊的內容,所以,我們就可以根據這些個欄位擷取我們比較關心的資訊了。
修改代碼如下:
1 protected void onCreate(Bundle savedInstanceState) { 2 3 super.onCreate(savedInstanceState); 4 5 6 7 setContentView(R.layout.main_layout); 8 9 Uri uri = Uri.parse("content://sms");10 11 Cursor cursor = getContentResolver().query(uri, new String[]{"date","address","type","body"}, null, null, null);12 13 if(cursor == null){14 15 return;16 17 }18 19 while(cursor.moveToNext()){20 21 Log.i(TAG,DateFormat.format("yyyy-MM-dd hh:mm", cursor.getLong(0)).toString());22 23 Log.i(TAG,cursor.getInt(2) == 1 ? "收到":"發送給");24 25 Log.i(TAG,cursor.getString(1)+"的簡訊:");26 27 Log.i(TAG,cursor.getString(3));28 29 }30 31 Log.i(TAG,cursor.getCount()+"");32 33 34 35 }
運行結果:
可以利用前面學習的解析XML的內容,將簡訊備份到XML檔案中,這裡就不再贅述了。
下面簡單說明下使用ContentObserver監聽簡訊的方法:
建立自己的ContentObserver:
1 class MyObserver extends ContentObserver{ 2 3 4 5 public MyObserver(Handler handler) { 6 7 super(handler); 8 9 }10 11 12 13 @Override14 15 public void onChange(boolean selfChange) {16 17 // TODO Auto-generated method stub18 19 Toast.makeText(MainActivity.this, "您有新的簡訊,請注意查收", Toast.LENGTH_SHORT).show();20 21 }22 23 }
在MainActivity註冊ContentObserver:
getContentResolver().registerContentObserver(uri, true, new MyObserver(new Handler()));
使用Emulator Controller向模擬器發送一條簡訊:
運行結果:
注意,在上面的uri後添加上inbox或者outbox可以觀察收件匣及寄件匣中的簡訊。在此,就不一一說明了。
查看連絡人資料的ContentProvider的源碼也可以從github上找到,在filters輸入框中輸入contacts即可:
開啟,然後clone或者打包下載到本地
在src目錄中找到:
ContactsProvider2.java,這個就是了。
搜尋addURI()方法,發現authority內容是:ContactsContract.AUTHORITY
在協助手冊中,尋找ContactsContract類,可以找到其中定義的常量AUTHORITY的值為:”com.android.contacts”。
這樣一來,就可以構造uri了。
在File Exploer中查看有關連絡人資料的表結構:
為了讓表中有資料,先添加幾個連絡人。
找到/data/data/com.android.providers.contacts/databases/contacts2.db
匯出到電腦中,然後使用Sqlite Expert查看,發現有很多張表的。
重點關注raw_contacts、data、mimetypes這三張表。
新增連絡人...過程其實是先向raw_contacts添加一個id,然後把這個id連同該連絡人資料中的某項資料作為一條記錄插入到data表中,該連絡人資料有多少項資料,就在data表中插入多少條資料。
如,我在模擬器中添加了兩個連絡人,每個連絡人都添加了3項資料,分別為姓名、手機號和郵箱。則在raw_contacts中產生兩條記錄,id分別為1、2。然後在data表中產生了6條記錄,每個連絡人3條,都是在raw_contact_id和data1欄位存入我所添加的資料內容。
所以,我們要擷取連絡人資訊時,可以到data表中根據raw_contact_id分組進行擷取每個連絡人的所有資訊。
注意到data表中有個欄位名為mimetype_id存放的是每條記錄中data1的類型,如1表示郵箱,7表示姓名等,可以查看mimetypes表:
Uri raw_uri = Uri.parse("content://com.android.contacts/raw_contacts");
Uri data_uri = Uri.parse("content://com.android.contacts/data");
注意,在通過data_uri擷取資訊時,若指定要查詢的欄位mimetype_id,會發現,找不到該欄位。這是因為,這個ContentProvider內部幫我們做了表串連查詢,我們查看的實際上是串連查詢的視圖,裡面存放的是mimetype這個欄位,而不再有mimetype_id欄位。
修改MainActivity擷取連絡人資訊:
1 Uri raw_uri = Uri.parse("content://com.android.contacts/raw_contacts"); 2 Uri data_uri = Uri.parse("content://com.android.contacts/data"); 3 Cursor raw_cursor = getContentResolver().query(raw_uri, new String[]{"_id"}, null, null, null); 4 5 6 7 if(raw_cursor == null){ 8 9 return ;10 11 }12 13 Log.i(TAG,raw_cursor.getCount()+"");14 15 while(raw_cursor.moveToNext()){16 17 String id = raw_cursor.getString(0);18 19 Cursor data_cursor = getContentResolver().query(data_uri, new String[]{"mimetype","data1"}, "raw_contact_id = ?", new String[]{id}, null);20 21 if(data_cursor == null){22 23 return ;24 25 }26 27 Log.i(TAG,"連絡人"+id);28 29 while(data_cursor.moveToNext()){30 31 String type = data_cursor.getString(0);32 33 if(type.equals("vnd.android.cursor.item/name")){34 35 Log.i(TAG,"姓名:"+data_cursor.getString(1));36 37 }else if(type.equals("vnd.android.cursor.item/phone_v2")){38 39 Log.i(TAG,"手機號:"+data_cursor.getString(1));40 41 }else if(type.equals("vnd.android.cursor.item/email_v2")){42 43 Log.i(TAG,"郵箱:"+data_cursor.getString(1));44 45 }46 47 }48 49 }
讀取連絡人資訊是需要許可權的:
<uses-permission android:name="android.permission.READ_CONTACTS"/>
運行結果:
關於這兩個ContentProvider還有其他的增刪改操作這裡就不一一說明,跟自己定義的ContentProvider的用法基本相同,只要懂得原理,到每個具體的ContentProvider基本上都是大同小異了。