小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Android開(kāi)發(fā)筆記(五十四)數(shù)據(jù)共享接口ContentProvider

 jiffes 2019-01-31

ContentProvider

前面幾節(jié)介紹了進(jìn)程間通信的幾種方式,包括消息包級(jí)別的Messenger、接口調(diào)用級(jí)別的AIDL、啟動(dòng)頁(yè)面/服務(wù)級(jí)別的Notification,還有就是本節(jié)這個(gè)數(shù)據(jù)庫(kù)級(jí)別的ContentProvider。
ContentProvider為存取數(shù)據(jù)提供統(tǒng)一的接口,它讓不同APP之間得以共享數(shù)據(jù)。ContentProvider類(lèi)本身是個(gè)服務(wù)端的數(shù)據(jù)存取接口,主要操作類(lèi)似SQLite,也都提供了如下常見(jiàn)的數(shù)據(jù)庫(kù)管理API:
query : 查詢(xún)數(shù)據(jù)。
insert : 插入數(shù)據(jù)。
update : 更新數(shù)據(jù)。
delete : 刪除數(shù)據(jù)。
getType : 獲取數(shù)據(jù)類(lèi)型。
實(shí)際開(kāi)發(fā)中,APP很少會(huì)開(kāi)放數(shù)據(jù)接口給其他應(yīng)用,所以ContentProvider類(lèi)作為服務(wù)端接口反而基本用不到。Content組件中能夠用到的場(chǎng)合,基本上是APP想要使用系統(tǒng)的手機(jī)通訊數(shù)據(jù),比如查看聯(lián)系人/短信/彩信/通話(huà)記錄,以及對(duì)這些通訊信息進(jìn)行增刪改。


ContentResolver

使用說(shuō)明

ContentResolver是客戶(hù)端APP用來(lái)操作服務(wù)端數(shù)據(jù)的接口,相對(duì)應(yīng)的ContentProvider是服務(wù)端的接口。獲取一個(gè)ContentResolver對(duì)象,調(diào)用Context.getContentResolver()即可。
與ContentProvider一樣,客戶(hù)端的ContentResolver也提供了query、insert、update、delete、getType等等方法。其中最常用的是query函數(shù),調(diào)用該函數(shù)返回一個(gè)Cursor對(duì)象,有關(guān)Cursor的操作參見(jiàn)《Android開(kāi)發(fā)筆記(三十一)SQLite游標(biāo)及其數(shù)據(jù)結(jié)構(gòu)》。下面是query的具體參數(shù)說(shuō)明:
uri : Uri類(lèi)型,可以理解為本次操作的數(shù)據(jù)表路徑
projection : String[]類(lèi)型,指定將要查詢(xún)的字段名列表
selection : String類(lèi)型,指定查詢(xún)條件
selectionArgs : String[]類(lèi)型,指定查詢(xún)條件中的參數(shù)取值列表
sortOrder : String類(lèi)型,指定排序條件


下面是ContentResolver在查看通訊信息中的具體運(yùn)用:


讀取聯(lián)系人

代碼示例如下:
  1. private static Uri mContactUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
  2. private static String[] mContactColumn = new String[] {
  3. ContactsContract.CommonDataKinds.Phone.NUMBER,
  4. ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME };
  5. public static int readPhoneContacts(ContentResolver resolver) {
  6. ArrayList<Contact> contactArray = new ArrayList<Contact>();
  7. Cursor cursor = resolver.query(mContactUri, mContactColumn, null, null, null);
  8. if (cursor.moveToFirst()) {
  9. for (;; cursor.moveToNext()) {
  10. Contact contact = new Contact();
  11. contact.phone = cursor.getString(0).replace("+86", "").replace(" ", "");
  12. contact.name = cursor.getString(1);
  13. Log.d(TAG, contact.name+" "+contact.phone);
  14. contactArray.add(contact);
  15. if (cursor.isLast() == true) {
  16. break;
  17. }
  18. }
  19. }
  20. cursor.close();
  21. return contactArray.size();
  22. }

上面代碼獲取的是手機(jī)里的聯(lián)系人。獲取SIM卡上的聯(lián)系人與之類(lèi)似,不同之處要把Uri換成“content://icc/adn”。



讀取短信

代碼示例如下:
  1. private static Uri mSmsUri;
  2. private static String[] mSmsColumn;
  3. @TargetApi(Build.VERSION_CODES.KITKAT)
  4. public static int readSms(ContentResolver resolver, String phone, int gaps) {
  5. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
  6. mSmsUri = Telephony.Sms.Inbox.CONTENT_URI;
  7. mSmsColumn = new String[] {
  8. Telephony.Sms.ADDRESS, Telephony.Sms.PERSON,
  9. Telephony.Sms.BODY, Telephony.Sms.DATE,
  10. Telephony.Sms.TYPE};
  11. } else {
  12. mSmsUri = Uri.parse("content://sms/inbox");
  13. mSmsColumn = new String[] { "address","person","body","date","type" };
  14. }
  15. ArrayList<SmsContent> smsArray = new ArrayList<SmsContent>();
  16. String selection = "";
  17. if (phone!=null && phone.length()>0) {
  18. selection = String.format("address=''%s''", phone);
  19. }
  20. if (gaps > 0) {
  21. selection = String.format("%s%sdate>%d", selection,
  22. (selection.length()>0)?" and ":"", System.currentTimeMillis()-gaps*1000);
  23. }
  24. Cursor cursor = resolver.query(mSmsUri, mSmsColumn, selection, null, "date desc");
  25. if (cursor.moveToFirst()) {
  26. for (;; cursor.moveToNext()) {
  27. SmsContent sms = new SmsContent();
  28. sms.address = cursor.getString(0);
  29. sms.person = cursor.getString(1);
  30. sms.body = cursor.getString(2);
  31. sms.date = formatDate(cursor.getLong(3));
  32. sms.type = cursor.getInt(4); //type=1表示收到的短信,type=2表示發(fā)送的短信
  33. Log.d(TAG, sms.address+" "+sms.person+" "+sms.date+" "+sms.type+" "+sms.body);
  34. smsArray.add(sms);
  35. if (cursor.isLast() == true) {
  36. break;
  37. }
  38. }
  39. }
  40. cursor.close();
  41. return smsArray.size();
  42. }



讀取彩信

代碼示例如下:
  1. private static Uri mMmsUri;
  2. private static String[] mMmsColumn;
  3. @TargetApi(Build.VERSION_CODES.KITKAT)
  4. public static int readMms(ContentResolver resolver, int gaps) {
  5. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
  6. mMmsUri = Telephony.Mms.Inbox.CONTENT_URI;
  7. mMmsColumn = new String[] {
  8. Telephony.Mms.DATE, Telephony.Mms.READ,
  9. Telephony.Mms.SUBJECT, Telephony.Mms.EXPIRY,
  10. Telephony.Mms.STATUS, Telephony.Mms.MESSAGE_SIZE};
  11. } else {
  12. mMmsUri = Uri.parse("content://mms/inbox");
  13. mMmsColumn = new String[] { "date","read","sub","exp","st","m_size" };
  14. }
  15. ArrayList<MmsContent> mmsArray = new ArrayList<MmsContent>();
  16. String selection = String.format("date>%d", System.currentTimeMillis()-gaps*1000);
  17. Cursor cursor = resolver.query(mMmsUri, mMmsColumn, selection, null, "date desc");
  18. if (cursor.moveToFirst()) {
  19. for (;; cursor.moveToNext()) {
  20. MmsContent mms = new MmsContent();
  21. mms.date = formatDate(cursor.getLong(0));
  22. mms.read = cursor.getString(1);
  23. mms.subject = cursor.getString(2);
  24. mms.expire = cursor.getString(3);
  25. mms.status = cursor.getString(4);
  26. mms.message_size = cursor.getString(5);
  27. Log.d(TAG, mms.date+" "+mms.read+" "+mms.subject+" "+mms.expire+" "+mms.status+" "+mms.message_size);
  28. mmsArray.add(mms);
  29. if (cursor.isLast() == true) {
  30. break;
  31. }
  32. }
  33. }
  34. cursor.close();
  35. return mmsArray.size();
  36. }



讀取通話(huà)記錄

代碼示例如下:
  1. private static Uri mRecordUri = CallLog.Calls.CONTENT_URI;
  2. private static String[] mRecordColumn = new String[] {
  3. CallLog.Calls.CACHED_NAME, CallLog.Calls.NUMBER, CallLog.Calls.TYPE,
  4. CallLog.Calls.DATE, CallLog.Calls.DURATION, CallLog.Calls.NEW };
  5. public static int readCallRecord(ContentResolver resolver, int gaps) {
  6. ArrayList<CallRecord> recordArray = new ArrayList<CallRecord>();
  7. String selection = String.format("date>%d", System.currentTimeMillis()-gaps*1000);
  8. Cursor cursor = resolver.query(mRecordUri, mRecordColumn, selection, null, "date desc");
  9. if (cursor.moveToFirst()) {
  10. for (;; cursor.moveToNext()) {
  11. CallRecord record = new CallRecord();
  12. record.name = cursor.getString(0);
  13. record.phone = cursor.getString(1);
  14. record.type = cursor.getInt(2); //type=1表示接聽(tīng),2表示撥出,3表示未接
  15. record.date = formatDate(cursor.getLong(3));
  16. record.duration = cursor.getLong(4);
  17. record._new = cursor.getInt(5);
  18. Log.d(TAG, record.name+" "+record.phone+" "+record.type+" "+record.date+" "+record.duration);
  19. recordArray.add(record);
  20. if (cursor.isLast() == true) {
  21. break;
  22. }
  23. }
  24. }
  25. cursor.close();
  26. return recordArray.size();
  27. }




ContentProviderOperation

使用說(shuō)明

前面說(shuō)過(guò),ContentResolver可以由客戶(hù)端用來(lái)給服務(wù)端添加數(shù)據(jù),不過(guò)有時(shí)候某種數(shù)據(jù)在服務(wù)端對(duì)應(yīng)的是多張表,比如說(shuō)聯(lián)系人信息在服務(wù)端實(shí)際有聯(lián)系人姓名表、聯(lián)系人電話(huà)表(因?yàn)橛屑彝ル娫?huà)、工作電話(huà)之分)、聯(lián)系人電子郵箱表。對(duì)于這種情況,使用ContentResolver固然可以通過(guò)多次插入來(lái)實(shí)現(xiàn),可是多次插入就對(duì)應(yīng)多個(gè)事務(wù),一旦某次插入失敗,那我們還得手工進(jìn)行回滾操作,非常麻煩。
針對(duì)上面的問(wèn)題,Android提供了ContentProviderOperation類(lèi),用于在一個(gè)事務(wù)中批量插入多條記錄,這樣即使出現(xiàn)失敗,也會(huì)由ContentProviderOperation統(tǒng)一處理回滾事宜,避免了開(kāi)發(fā)者關(guān)注內(nèi)部事務(wù)的麻煩。


下面是兩種插入方式在添加聯(lián)系人信息中的具體運(yùn)用:


ContentResolver方式

代碼示例如下:
  1. public static void addContacts(ContentResolver resolver) {
  2. //往 raw_contacts 中添加數(shù)據(jù),并獲取添加的id號(hào)
  3. Uri raw_uri = Uri.parse("content://com.android.contacts/raw_contacts");
  4. ContentValues values = new ContentValues();
  5. long contactId = ContentUris.parseId(resolver.insert(raw_uri, values));
  6. //往 data 中添加數(shù)據(jù)(要根據(jù)前面獲取的id號(hào))
  7. Uri uri = Uri.parse("content://com.android.contacts/data");
  8. ContentValues name = new ContentValues();
  9. name.put("raw_contact_id", contactId);
  10. name.put("mimetype", "vnd.android.cursor.item/name");
  11. name.put("data2", "阿四");
  12. resolver.insert(uri, name);
  13. ContentValues phone = new ContentValues();
  14. phone.put("raw_contact_id", contactId);
  15. phone.put("mimetype", "vnd.android.cursor.item/phone_v2");
  16. phone.put("data2", "2");
  17. phone.put("data1", "15960238696");
  18. resolver.insert(uri, phone);
  19. ContentValues email = new ContentValues();
  20. email.put("raw_contact_id", contactId);
  21. email.put("mimetype", "vnd.android.cursor.item/email_v2");
  22. email.put("data2", "2");
  23. email.put("data1", "aaa@163.com");
  24. resolver.insert(uri, email);
  25. }



ContentProviderOperation方式

代碼示例如下:
  1. public static void addFullContacts(ContentResolver resolver) {
  2. Uri raw_uri = Uri.parse("content://com.android.contacts/raw_contacts");
  3. Uri uri = Uri.parse("content://com.android.contacts/data");
  4. ContentProviderOperation op_main = ContentProviderOperation.newInsert(raw_uri)
  5. .withValue("account_name", null).build();
  6. ContentProviderOperation op_name = ContentProviderOperation.newInsert(uri)
  7. .withValueBackReference("raw_contact_id", 0)
  8. .withValue("mimetype", "vnd.android.cursor.item/name")
  9. .withValue("data2", "阿三").build();
  10. ContentProviderOperation op_phone = ContentProviderOperation.newInsert(uri)
  11. .withValueBackReference("raw_contact_id", 0)
  12. .withValue("mimetype", "vnd.android.cursor.item/phone_v2")
  13. .withValue("data2", "2")
  14. .withValue("data1", "15960238696").build();
  15. ContentProviderOperation op_email = ContentProviderOperation
  16. .newInsert(uri).withValueBackReference("raw_contact_id", 0)
  17. .withValue("mimetype", "vnd.android.cursor.item/email_v2")
  18. .withValue("data2", "2")
  19. .withValue("data1", "aaa@163.com").build();
  20. ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
  21. operations.add(op_main);
  22. operations.add(op_name);
  23. operations.add(op_phone);
  24. operations.add(op_email);
  25. try {
  26. resolver.applyBatch("com.android.contacts", operations);
  27. } catch (Exception e) {
  28. e.printStackTrace();
  29. }
  30. }




ContentObserver

使用說(shuō)明

有時(shí)我們不但要獲取以往的數(shù)據(jù),還要實(shí)時(shí)獲取新增的數(shù)據(jù),最常見(jiàn)的業(yè)務(wù)場(chǎng)景便是短信驗(yàn)證碼。電商APP中常常在用戶(hù)注冊(cè)或者付款時(shí)下發(fā)驗(yàn)證碼短信,這時(shí)為提高用戶(hù)體驗(yàn),APP就得自動(dòng)獲取手機(jī)剛收到的短信驗(yàn)證碼。類(lèi)似的場(chǎng)景在系統(tǒng)APP中也存在,比如流量監(jiān)控APP向運(yùn)營(yíng)商發(fā)送流量校準(zhǔn)短信,此時(shí)APP也得自動(dòng)攔截短信來(lái)獲取流量信息。
由于系統(tǒng)在接收短信后會(huì)同時(shí)發(fā)出一個(gè)廣播“android.provider.Telephony.SMS_RECEIVED”,所以我們可以使用廣播接收器來(lái)監(jiān)聽(tīng)短信的接收動(dòng)作。然而不是所有的系統(tǒng)數(shù)據(jù)變更都會(huì)觸發(fā)廣播(比如添加聯(lián)系人),所以Android又提供了ContentObserver類(lèi),該類(lèi)可協(xié)助處理Content數(shù)據(jù)變化的監(jiān)聽(tīng)事件。
下面是在ContentResolver對(duì)象中使用ContentObserver的相關(guān)方法:
registerContentObserver : 注冊(cè)內(nèi)容觀察者。
unregisterContentObserver : 注銷(xiāo)內(nèi)容觀察者。
notifyChange : 通知內(nèi)容觀察者發(fā)生了數(shù)據(jù)變化。


下面是兩種監(jiān)聽(tīng)方式在監(jiān)聽(tīng)短信接收中的具體運(yùn)用,監(jiān)聽(tīng)結(jié)果消息使用了Notification推送到消息欄,有關(guān)Notification的使用說(shuō)明參見(jiàn)《Android開(kāi)發(fā)筆記(五十二)通知推送Notification》。


廣播方式

廣播類(lèi)的代碼示例如下:
  1. import android.content.BroadcastReceiver;
  2. import android.content.Context;
  3. import android.content.Intent;
  4. import android.os.Bundle;
  5. import android.telephony.SmsMessage;
  6. import android.util.Log;
  7. public class SmsGetReceiver extends BroadcastReceiver {
  8. private static final String TAG = "SmsGetReceiver";
  9. @Override
  10. public void onReceive(Context context, Intent intent) {
  11. Log.d(TAG, "onReceive");
  12. Bundle bundle = intent.getExtras();
  13. SmsMessage[] smsMessages = null;
  14. Object[] pdus = null;
  15. if (bundle != null) {
  16. pdus = (Object[]) bundle.get("pdus");
  17. }
  18. if (pdus !=null){
  19. smsMessages = new SmsMessage[pdus.length];
  20. String sender = "";
  21. String content = "";
  22. for (int i=0; i<pdus.length; i++){
  23. smsMessages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
  24. sender = smsMessages[i].getOriginatingAddress();
  25. content = smsMessages[i].getMessageBody();
  26. Log.d(TAG, "SMS:"+sender+content);
  27. }
  28. NotificationUtil.sendSmsNotify(context, "廣播來(lái)源:"+sender, content);
  29. }
  30. }
  31. }


配置文件需要注冊(cè)該廣播
  1. <receiver android:name=".content.util.SmsGetReceiver">
  2. <intent-filter>
  3. <action android:name="android.provider.Telephony.SMS_RECEIVED" />
  4. </intent-filter>
  5. </receiver>



觀察者方式

觀察者類(lèi)的代碼示例如下:
  1. import android.annotation.TargetApi;
  2. import android.content.Context;
  3. import android.database.ContentObserver;
  4. import android.database.Cursor;
  5. import android.net.Uri;
  6. import android.os.Build;
  7. import android.os.Handler;
  8. import android.provider.Telephony;
  9. @TargetApi(Build.VERSION_CODES.KITKAT)
  10. public class SmsGetObserver extends ContentObserver {
  11. private static final String TAG = "SmsGetObserver";
  12. private Context mContext;
  13. private static Uri mSmsUri;
  14. private static String[] mSmsColumn;
  15. public SmsGetObserver(Context context, Handler handler) {
  16. super(handler);
  17. mContext = context;
  18. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
  19. mSmsUri = Telephony.Sms.Inbox.CONTENT_URI;
  20. mSmsColumn = new String[] {
  21. Telephony.Sms.ADDRESS, Telephony.Sms.BODY, Telephony.Sms.DATE };
  22. } else {
  23. mSmsUri = Uri.parse("content://sms/inbox");
  24. mSmsColumn = new String[] { "address","body","date" };
  25. }
  26. }
  27. @Override
  28. public void onChange(boolean selfChange) {
  29. String sender = "";
  30. String content = "";
  31. String selection = String.format("date>%d", System.currentTimeMillis()-2*1000);
  32. Cursor cursor = mContext.getContentResolver().query(
  33. mSmsUri, mSmsColumn, selection, null, null);
  34. while(cursor.moveToNext()){
  35. sender = cursor.getString(0);
  36. content = cursor.getString(1);
  37. }
  38. cursor.close();
  39. NotificationUtil.sendSmsNotify(mContext, "觀察者來(lái)源:"+sender, content);
  40. super.onChange(selfChange);
  41. }
  42. }


主頁(yè)面中對(duì)觀察者類(lèi)的調(diào)用代碼如下:
  1. SmsGetObserver observer = new SmsGetObserver(this, new Handler());
  2. getContentResolver().registerContentObserver(
  3. Uri.parse("content://sms"), true, observer);



常用的Uri

總結(jié)下在Content組件中使用過(guò)程中遇到的幾個(gè)Uri常量:
聯(lián)系人信息(不包含手機(jī)號(hào)與電子郵箱):
ContactsContract.Contacts.CONTENT_URI   content://com.android.contacts/contacts
聯(lián)系人電話(huà)信息:
ContactsContract.CommonDataKinds.Phone.CONTENT_URI   content://com.android.contacts/data/phones
聯(lián)系人郵箱信息:
ContactsContract.CommonDataKinds.Email.CONTENT_URI   content://com.android.contacts/data/emails
SIM卡聯(lián)系人信息:
content://icc/adn
短信信息:
Telephony.Sms.CONTENT_URI   content://sms
彩信信息:
Telephony.Mms.CONTENT_URI   content://mms
通話(huà)記錄信息:
CallLog.Calls.CONTENT_URI   content://call_log/calls


下面是與短信有關(guān)的Uri分類(lèi)說(shuō)明:
收件箱:
Telephony.Sms.Inbox.CONTENT_URI   content://sms/inbox
已發(fā)送:
Telephony.Sms.Sent.CONTENT_URI   content://sms/sent
草稿箱:
Telephony.Sms.Draft.CONTENT_URI   content://sms/draft
發(fā)件箱(正在發(fā)送的信息):
Telephony.Sms.Outbox.CONTENT_URI   content://sms/outbox
發(fā)送失?。?br> content://sms/failed         
待發(fā)送列表(比如開(kāi)啟飛行模式后,該短信就在待發(fā)送列表里):
content://sms/queued    
 

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶(hù)發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶(hù) 評(píng)論公約

    類(lèi)似文章 更多