根底总结篇之七:ContentProvider之读写短音讯-Android-优质IT资源分享社区

admin
管理员
管理员
  • UID1
  • 粉丝26
  • 关注4
  • 发帖数581
  • 社区居民
  • 忠实会员
  • 原创写手
阅读:161回复:0

  根底总结篇之七:ContentProvider之读写短音讯

楼主#
更多 发布于:2016-06-02 20:51

古之成大事者,不惟有超世之才,亦有堅韌不拔之志。北宋.蘇軾《晁錯論》

咱们的长辈中那些成就大事的人,不单单有过人的才智和才干,也须有坚定不移的毅力。试问没有坚韧的毅力,怎么写得出杂乱的系统,怎么创造出巨大的商品?作为程序员的咱们,才智和才干好像不太短缺,咱们短缺的也许是恰是坚韧的毅力,所以从今日起,训练自个的毅力吧,在坚持抱负的道路上,让这种毅力给自个力气。

今日咱们来讲一下怎么运用ContentProvider读写短音讯。

前次咱们讲了怎么经过ContentProvider机制读写联络人,经过读取联络人信息和增加联络人这两种办法对联络人进行操作,相信咱们对ContentProvider的根本运用办法也有所了解了。在Android中ContentProvider运用场合还很多,读写短音讯即是其间一个,今日咱们就来讨论一下运用ContentProvider操作短音讯的问题。

相对于联络人来说,短音讯不是揭露的,所以没有专门的API供咱们调用,这就请求咱们依据源代码进行剖析研究,拟定出必定的操作计划。

咱们需求先找到短音讯的数据源,翻开/data/data/com.android.providers.telephony能够看到:

其间的mmssms.db即是短音讯的数据源,兄弟们能够导出一下这个文件,用专业工具软件检查一下表构造。为了便利咱们了解,我简略介绍一下今日涉及到的两张表以及表中的常用字段:

如图所示,两张表别离是threads表和sms表,前者代表一切会话信息,每个会话代表和一个联络人之间短信的群组;后者代表短信的详细信息。在sms表中的thread_id指向了threads表中的_id,指定每条短信的会话id,以便对短信进行分组。下面介绍一下表中的每个字段的含义:

threads表:_id字段表明该会话id;date表明该会话终究一条短信的日期,一般用来对多个会话排序显现;message_count表明该会话所包括的短信数量;snippet表明该会话中终究一条短信的内容;read表明该会话是不是已读(0:未读,1:已读),一般来说该会话中有了新短信但没检查时,该会话read变为未读状况,当检查过新短信后read就变为已读状况。

sms表:_id表明该短信的id;thread_id表明该短信所属的会话的id;date表明该短信的日期;read表明该短信是不是已读;type表明该短信的类型,例如1表明接纳类型,2表明发送类型,3表明草稿类型;body表明短信的内容。

下面咱们会经过单元测验的办法演示一下读取会话信息和短信内容。在写代码之前,咱们先初始化一些数据,详细进程是发动三个模拟器5554、5556、5558,让5554别离与5556和5558互发短信,如下:

咱们看到5554这小子名叫Jack;5556名叫Lucy,也许知道有几天了,手机上存了她的号码;5558名叫Lisa,也许刚知道,还没来得及存号码。Jack这小子真狠啊,想一起泡两个妞,莫非姓名叫Jack的长得都很帅?下面是以上的两个会话信息:

能够看到,由于在联络人里存了Lucy,所以显现时并不再直接显现生疏的数字,而是其姓名;括号内显现了该会话的短信数;下面文字显现了终究一条短信的内容和日期。

下面咱们创立一个名为SMSTest的单元测验类,用于读取会话信息和短信内容,代码如下:

package com.scott.provider;import

java.text.SimpleDateFormat;import android.content.ContentResolver;import

android.database.Cursor;import android.database.CursorWrapper;import

android.net.Uri;import android.test.AndroidTestCase;import

android.util.Log;public class SMSTest extends AndroidTestCase {     private

static final String TAG = SMSTest;     //会话     private static final String

CONVERSATIONS = content://sms/conversations     //查询联络人     private static final

String CONTACTS_LOOKUP = content://com.android.contacts/phone_lookup/     //悉数短信

    private static final String SMS_ALL = content://sms/;     //收件箱     //

private static final String SMS_INBOX = content://sms/inbox;     //已发送     //

private static final String SMS_SENT = content://sms/sent;     //草稿箱     //

private static final String SMS_DRAFT = content://sms/draft;     private

SimpleDateFormat dateFormat = new SimpleDateFormat(yyyy-MM-dd HH:mm:ss);     /**

      * 读取会话信息       */     public void testReadConversation() {        

 ContentResolver resolver = getContext().getContentResolver();          Uri uri

= Uri.parse(CONVERSATIONS);          String[] projection = new

String[]{groups.group_thread_id AS group_id, groups.msg_count AS msg_count,

groups.group_date AS last_date, sms.body AS last_msg, sms.address AS contact};  

       Cursor thinc = resolver.query(uri, projection, null, null,

groups.group_date DESC); //查询并按日期倒序          Cursor richc = new

CursorWrapper(thinc) { //对Cursor进行处理,遇到号码后获取对应的联络人称号                @Override  

             public String getString(int columnIndex) {                    

if(super.getColumnIndex(contact) == columnIndex){                          

String contact = super.getString(columnIndex);                          

//读取联络人,查询对应的称号                           Uri uri = Uri.parse(CONTACTS_LOOKUP +

contact);                           Cursor cursor =

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

                if(cursor.moveToFirst()){                                

 String contactName = cursor.getString(cursor.getColumnIndex(display_name));    

                             return contactName;                           }    

                      return contact;                     }                    

return super.getString(columnIndex);               }         };         while

(richc.moveToNext()) {               String groupId = groupId: +

richc.getInt(richc.getColumnIndex(group_id));               String msgCount =

msgCount: + richc.getLong(richc.getColumnIndex(msg_count));               String

lastMsg = lastMsg: + richc.getString(richc.getColumnIndex(last_msg));          

    String contact = contact: + richc.getString(richc.getColumnIndex(contact));

              String lastDate = lastDate: +

dateFormat.format(richc.getLong(richc.getColumnIndex(last_date)));              

printLog(groupId, contact, msgCount, lastMsg, lastDate,

---------------END---------------);         }         richc.close();    }    /**

     * 读取短信      */    public void testReadSMS() {          ContentResolver

resolver = getContext().getContentResolver();          Uri uri =

Uri.parse(SMS_ALL);          String[] projection = {thread_id AS group_id,

address AS contact, body AS msg_content, date, type};          Cursor c =

resolver.query(uri, projection, null, null, date DESC); //查询并按日期倒序        

 while (c.moveToNext()) {                String groupId = groupId:  +

c.getInt(c.getColumnIndex(group_id));                String contact = contact:

 + c.getString(c.getColumnIndex(contact));                String msgContent =

msgContent: + c.getString(c.getColumnIndex(msg_content));                String

date = date: + dateFormat.format(c.getLong(c.getColumnIndex(date)));            

   String type = type:  + getTypeById(c.getInt(c.getColumnIndex(type)));        

      printLog(groupId, contact, msgContent, date, type,

---------------END---------------);          }          c.close();     }    

private String getTypeById(int typeId) {           switch (typeId) {            

   case 1: return receive;                case 2: return send;              

 case 3: return draft;                default: return none;           }     }  

  private void printLog(String...strings) {           for (String s : strings) {

                Log.i(TAG, s == null ? NULL : s);          }     }}

咱们先剖析一下testReadConversation()办法,它是用来读取一切的会话信息的,依据“content://sms/conversations/”这个URI进行会话数据的读取操作,当取到数据后,对数据进一步的包装,详细做法是遇到号码时再依据“content://com.android.contacts/phone_lookup/”到联络人中查找对应的称号,假如存在则显现称号而不是号码。咱们留意到在查询会话时运用到了projection,这些都是依据啥拟定的呢?这就需求咱们去看一下源代码了。

咱们找到TelephonyProvider中的com/android/providers/telephony/SmsProvider.java文件,看一看终究:

@Overridepublic Cursor query(Uri url, String[]

projectionIn, String selection,     String[] selectionArgs, String sort) {    

SQLiteQueryBuilder qb = new SQLiteQueryBuilder();     // Generate the body of

the query.     int match = sURLMatcher.match(url);     switch (match) {        

  ...           case SMS_CONVERSATIONS:                 qb.setTables(sms,

(SELECT thread_id AS group_thread_id, MAX(date)AS group_date, + COUNT(*) AS

msg_count FROM sms GROUP BY thread_id) AS groups);                

qb.appendWhere(sms.thread_id = groups.group_thread_id AND sms.date = +

groups.group_date);                

qb.setProjectionMap(sConversationProjectionMap);                 break;        

  ...      }      String orderBy = null;      if (!TextUtils.isEmpty(sort)) {  

         orderBy = sort;      } else if (qb.getTables().equals(TABLE_SMS)) {    

       orderBy = Sms.DEFAULT_SORT_ORDER;      }      SQLiteDatabase db =

mOpenHelper.getReadableDatabase();      Cursor ret = qb.query(db, projectionIn,

selection, selectionArgs, null, null, orderBy);      // TODO: Since the URLs are

a mess, always use content://sms    

 ret.setNotificationUri(getContext().getContentResolver(), NOTIFICATION_URI);  

   return ret;}

咱们看到,在query办法的case句子中,假如是SMS_CONVERSATIONS类型的话,就为SQLiteQueryBuilder实例目标qb设置对应的查询表和where句子,别的还会为其设置一个根本的查询映射map即sConversationProjectionMap,这个变量在下面代码中表现:

static {      ...      sURLMatcher.addURI(sms,

conversations, SMS_CONVERSATIONS);      sURLMatcher.addURI(sms, conversations,

SMS_CONVERSATIONS_ID);      ...    

 sConversationProjectionMap.put(Sms.Conversations.SNIPPET, sms.body AS snippet);

     sConversationProjectionMap.put(Sms.Conversations.THREAD_ID, sms.thread_id

AS thread_id);    

 sConversationProjectionMap.put(Sms.Conversations.MESSAGE_COUNT,

groups.msg_count AS msg_count);      sConversationProjectionMap.put(delta,

null); }

这几对数据有啥用途呢?假如咱们查询时的projection为null的话,sConversationProjectionMap就将转换为默许的projection,终究查询成果中仅包括这三个最根本的字段:snippet、thread_id、msg_count,能够代表一个会话的最简明的信息,兄弟们能够亲身试一试。

当然,假如想运转上面的测验用例,需求装备两个权限:读取短音讯权限和读取联络人权限,如下:

<-- --=""><-- --="">;

然后咱们运转一下测验用例,成果如下:

以上即是读取会话的悉数内容,下面咱们再介绍其间的testReadSMS()办法。在这个办法中咱们企图将一切的短音讯都获取到,运用了“content://sms/”进行查询,这个查询相对简略了很多。别的代码中也有几个没运用到的URI,他们别离是收件箱、已发送和草稿箱,这几个查询是“content://sms/”的子集,别离用了不一样的挑选条件对短信表进行查询,咱们看一下详细的源代码:

@Overridepublic Cursor query(Uri url, String[]

projectionIn, String selection,     String[] selectionArgs, String sort) {    

SQLiteQueryBuilder qb = new SQLiteQueryBuilder();     // Generate the body of

the query.     int match = sURLMatcher.match(url);     switch (match) {        

 case SMS_ALL:               constructQueryForBox(qb, Sms.MESSAGE_TYPE_ALL);    

          break;          case SMS_INBOX:               constructQueryForBox(qb,

Sms.MESSAGE_TYPE_INBOX);               break;          case SMS_SENT:          

    constructQueryForBox(qb, Sms.MESSAGE_TYPE_SENT);               break;      

   case SMS_DRAFT:               constructQueryForBox(qb,

Sms.MESSAGE_TYPE_DRAFT);               break;      }      ...}

能够看到,他们都调用了constructQueryForBox办法,这个办法是干啥的呢?

private void

constructQueryForBox(SQLiteQueryBuilder qb, int type) {    

qb.setTables(TABLE_SMS);    if (type != Sms.MESSAGE_TYPE_ALL) {          

qb.appendWhere(type= + type);     }}

咱们发现它其实是增加过滤条件的,假如不是查询悉数,则增加类型过滤信息,因此查询出不一样的短信调集。兄弟们也能够亲身试一试不一样类型的查询。

别的,假如咱们想依据会话来查询对应的短信调集的话,咱们能够用以下两种办法来完结:

1.“content://sms/”(selection:“thread_id=3”)

2.“content://sms/conversations/3”

第一种对比简略想到查询的进程,即在上面的根底上加上“thread_id=3”这条where句子即可;第二种是在会话path后边跟上会话id即可,详细的逻辑如下:

case SMS_CONVERSATIONS_ID:     int threadID;    

try {          threadID = Integer.parseInt(url.getPathSegments().get(1));      

   if (Log.isLoggable(TAG, Log.VERBOSE)) {                Log.d(TAG, query

conversations: threadID= + threadID);          }     } catch (Exception ex) {  

       Log.e(TAG, Bad conversation thread id:  + url.getPathSegments().get(1));

         return null;     }     qb.setTables(TABLE_SMS);    

qb.appendWhere(thread_id =  + threadID);     break;

咱们能够看到,它终究仍是和第一种办法走上了一样的道儿,两者没啥本质上的差异。可是从简略易用性上来讲,这一种办法是对比好的,兄弟们能够对比一下。

以上即是获取会话内容和短信内容的悉数信息,下面咱们介绍一下短信的写入操作。

发送和写入短信

在某些场合,咱们需求发送短信,并将短信写入数据源中,这时咱们就需求了解一下发送短信机制和写入短信机制。

咱们将企图发送一条短信到指定的地址,一起将短信的内容写入到短信数据源中,待短信发送成功后,咱们奉告用户发送成功,待对方接纳到短信后,咱们奉告用户对方接纳成功。

要完成这些功用,咱们需求了解以下几个要点内容:

1.运用android.telephony.SmsManager的API发送短信

2.运用ContentProvider机制对“content://sms/sent”这个URI进行写入操作

3.注册“SENT_SMS_ACTION”这个播送地址,待短信发送成功后接纳到这条播送

4.注册“DELIVERED_SMS_ACTION”这个播送地址,待对方接纳到短信后接纳到这条播送

下面咱们就用代码完成这些功用,创立一个名为SMSActivity的Activity,如下:

package com.scott.provider;import

java.util.List;import android.app.Activity;import

android.app.PendingIntent;import android.content.BroadcastReceiver;import

android.content.ContentValues;import android.content.Context;import

android.content.Intent;import android.content.IntentFilter;import

android.net.Uri;import android.os.Bundle;import

android.telephony.SmsManager;import android.view.View;import

android.widget.EditText;import android.widget.Toast;public class SMSActivity

extends Activity {      private SendReceiver sendReceiver = new SendReceiver();

     private DeliverReceiver deliverReceiver = new DeliverReceiver();    

 private EditText address;      private EditText body;      @Override    

 protected void onCreate(Bundle savedInstanceState) {            

super.onCreate(savedInstanceState);             setContentView(R.layout.sms);  

          address = (EditText) findViewById(R.id.address);             body =

(EditText) findViewById(R.id.body);             //注册发送成功的播送            

registerReceiver(sendReceiver, new IntentFilter(SENT_SMS_ACTION));            

//注册接纳成功的播送             registerReceiver(deliverReceiver, new

IntentFilter(DELIVERED_SMS_ACTION));      }     @Override      protected void

onDestroy() {            super.onDestroy();          

unregisterReceiver(sendReceiver);          

 unregisterReceiver(deliverReceiver);      }      public void sendSMS(View view)

{           String address = this.address.getText().toString();           String

body = this.body.getText().toString();           //android.telephony.SmsManager,

not [android.telephony.gsm.SmsManager]           SmsManager smsManager =

SmsManager.getDefault();           //短信发送成功或失利后会发生一条SENT_SMS_ACTION的播送          

PendingIntent sendIntent = PendingIntent.getBroadcast(this, 0, new

Intent(SENT_SMS_ACTION), 0);          

//接纳方成功收到短信后,发送方会发生一条DELIVERED_SMS_ACTION播送           PendingIntent

deliveryIntent = PendingIntent.getBroadcast(this, 0, new

Intent(DELIVERED_SMS_ACTION), 0);           if (body.length() > 70) {

//假如字数超越70,需拆分红多条短信发送                 Listmsgs =

smsManager.divideMessage(body);                 for (String msg : msgs) {      

                 smsManager.sendTextMessage(address, null, msg, sendIntent,

deliveryIntent);                 }           } else {                

smsManager.sendTextMessage(address, null, body, sendIntent, deliveryIntent);    

      }           //写入到短信数据源           ContentValues values = new

ContentValues();           values.put(address,address); //发送地址          

values.put(body, body); //音讯内容           values.put(date,

System.currentTimeMillis()); //创立时刻           values.put(read, 0); //0:未读;1:已读  

        values.put(type, 2); //1:接纳;2:发送          

getContentResolver().insert(Uri.parse(content://sms/sent), values); //刺进数据     }

    private class SendReceiver extends BroadcastReceiver {           @Override  

        public void onReceive(Context context, Intent intent) {              

 switch (getResultCode()) {                      case Activity.RESULT_OK:      

                     Toast.makeText(context, Sent Successfully.,

Toast.LENGTH_SHORT).show();                            break;                  

   default:                            Toast.makeText(context, Failed to Send.,

Toast.LENGTH_SHORT).show();                }           }      }      /**      

 * 发送方的短信发送到对方手机上以后,对方手时机回来给运营商一个信号,        * 运营商再把这个信号发给发送方,发送方此刻可承认对方接纳成功    

   * 模拟器不支持,真机上需等候顷刻        * @author user        *        */      private class

DeliverReceiver extends BroadcastReceiver {            @Override          

 public void onReceive(Context context, Intent intent) {                

 Toast.makeText(context, Delivered Successfully., Toast.LENGTH_SHORT).show();  

         }      }}

规划文件如下:

android:orientation=vertical

            android:layout_width=fill_parent            

android:layout_height=fill_parentandroid:layout_width=fill_parent    

        android:layout_height=wrap_content            

android:text=address/>android:id=@+id/address            

android:layout_width=fill_parent            

android:layout_height=wrap_contentandroid:layout_width=fill_parent    

        android:layout_height=wrap_content            

android:text=body/>android:id=@+id/body            

android:layout_width=fill_parent             android:layout_height=150dp        

    android:gravity=top>android:layout_width=fill_parent            

android:layout_height=wrap_content             android:text=sendSMS            

android:onClick=sendSMS/>

需求留意的是,这个进程要声明发送短信的权限和写入短信的权限,咱们在AndroidManifest.xml的声明如下:

然后,运转该程序,咱们让Jack给Lisa发送一条短信,看看成果怎么:

看来咱们的操作成功了,究竟Jack能不能泡到Lisa呢,兄弟们,发挥你们的想象力吧。

终究需求留意的一件事,代码里也提到过,即是在模拟器测验时,是不支持“接纳成功”这个功用的,所以兄弟们想要看到“Delivered

Successfully”,还必须在真机上试,而且需求耐性等上顷刻。感兴趣的兄弟赶忙试一试吧。

优质IT资源分享社区为你提供此文。

站有大量优质android教程视频,资料等资源,包含android基础教程,高级进阶教程等等,教程视频资源涵盖传智播客,极客学院,达内,北大青鸟,猎豹网校等等IT职业培训机构的培训教学视频,价值巨大。欢迎点击下方链接查看。

android教程视频

优质IT资源分享社区(www.itziyuan.top)
一个免费,自由,开放,共享,平等,互助的优质IT资源分享网站。
专注免费分享各大IT培训机构最新培训教学视频,为你的IT学习助力!

!!!回帖受限制请看点击这里!!!
!!!资源失效请在此版块发帖说明!!!

[PS:按 CTRL+D收藏本站网址~]

——“优质IT资源分享社区”管理员专用签名~

本版相似帖子

游客