android短信会话测试.doc_第1页
android短信会话测试.doc_第2页
android短信会话测试.doc_第3页
android短信会话测试.doc_第4页
android短信会话测试.doc_第5页
免费预览已结束,剩余15页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

古之成大事者,不惟有超世之才,亦有堅韌不拔之志。北宋.蘇軾晁錯論我们的前辈中那些成就大事的人,不单单有过人的智慧和才能,也须有坚韧不拔的意志。试问没有坚韧的意志,如何写得出复杂的系统,如何创造出伟大的产品?作为程序员的我们,智慧和才能似乎不太欠缺,我们欠缺的也许是正是坚韧的意志,所以从今天起,锻炼自己的意志吧,在坚持理想的道路上,让这种意志给自己力量。今天我们来讲一下如何利用ContentProvider读写短消息。上次我们讲了如何通过ContentProvider机制读写联系人,通过读取联系人信息和添加联系人这两种方式对联系人进行操作,相信大家对ContentProvider的基本使用方法也有所了解了。在Android中ContentProvider应用场合还很多,读写短消息就是其中一个,今天我们就来探讨一下利用ContentProvider操作短消息的问题。相对于联系人来说,短消息不是公开的,所以没有专门的API供我们调用,这就要求我们根据源代码进行分析研究,制定出一定的操作方案。我们需要先找到短消息的数据源,打开/data/data/viders.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的单元测试类,用于读取会话信息和短信内容,代码如下:java view plaincopyprint?1. vider;2. 3. importjava.text.SimpleDateFormat;4. 5. importandroid.content.ContentResolver;6. importandroid.database.Cursor;7. importandroid.database.CursorWrapper;8. .Uri;9. importandroid.test.AndroidTestCase;10. importandroid.util.Log;11. 12. publicclassSMSTestextendsAndroidTestCase13. 14. privatestaticfinalStringTAG=SMSTest;15. 16. /会话 17. privatestaticfinalStringCONVERSATIONS=content:/sms/conversations/;18. /查询联系人 19. privatestaticfinalStringCONTACTS_LOOKUP=content:/com.android.contacts/phone_lookup/;20. /全部短信 21. privatestaticfinalStringSMS_ALL=content:/sms/;22. /收件箱 23. /privatestaticfinalStringSMS_INBOX=content:/sms/inbox; 24. /已发送 25. /privatestaticfinalStringSMS_SENT=content:/sms/sent; 26. /草稿箱 27. /privatestaticfinalStringSMS_DRAFT=content:/sms/draft; 28. 29. privateSimpleDateFormatdateFormat=newSimpleDateFormat(yyyy-MM-ddHH:mm:ss);30. 31. /*32. *读取会话信息33. */34. publicvoidtestReadConversation()35. ContentResolverresolver=getContext().getContentResolver();36. Uriuri=Uri.parse(CONVERSATIONS);/37. Stringprojection=newStringgroups.group_thread_idASgroup_id,groups.msg_countASmsg_count,38. groups.group_dateASlast_date,sms.bodyASlast_msg,sms.addressAScontact;39. Cursorthinc=resolver.query(uri,projection,null,null,groups.group_dateDESC);/查询并按日期倒序 40. Cursorrichc=newCursorWrapper(thinc)/对Cursor进行处理,遇到号码后获取对应的联系人名称 41. Override42. publicStringgetString(intcolumnIndex)43. if(super.getColumnIndex(contact)=columnIndex)44. Stringcontact=super.getString(columnIndex);45. /读取联系人,查询对应的名称 46. Uriuri=Uri.parse(CONTACTS_LOOKUP+contact);47. Cursorcursor=getContext().getContentResolver().query(uri,null,null,null,null);48. if(cursor.moveToFirst()49. StringcontactName=cursor.getString(cursor.getColumnIndex(display_name);50. returncontactName;51. 52. returncontact;53. 54. returnsuper.getString(columnIndex);55. 56. ;57. while(richc.moveToNext()58. StringgroupId=groupId:+richc.getInt(richc.getColumnIndex(group_id);59. StringmsgCount=msgCount:+richc.getLong(richc.getColumnIndex(msg_count);60. StringlastMsg=lastMsg:+richc.getString(richc.getColumnIndex(last_msg);61. Stringcontact=contact:+richc.getString(richc.getColumnIndex(contact);62. StringlastDate=lastDate:+dateFormat.format(richc.getLong(richc.getColumnIndex(last_date);63. 64. printLog(groupId,contact,msgCount,lastMsg,lastDate,-END-);65. 66. richc.close();67. 68. 69. /*70. *读取短信71. */72. publicvoidtestReadSMS()73. ContentResolverresolver=getContext().getContentResolver();74. Uriuri=Uri.parse(SMS_ALL);75. Stringprojection=thread_idASgroup_id,addressAScontact,bodyASmsg_content,date,type;76. Cursorc=resolver.query(uri,projection,null,null,dateDESC);/查询并按日期倒序 77. while(c.moveToNext()78. StringgroupId=groupId:+c.getInt(c.getColumnIndex(group_id);79. Stringcontact=contact:+c.getString(c.getColumnIndex(contact);80. StringmsgContent=msgContent:+c.getString(c.getColumnIndex(msg_content);81. Stringdate=date:+dateFormat.format(c.getLong(c.getColumnIndex(date);82. Stringtype=type:+getTypeById(c.getInt(c.getColumnIndex(type);83. 84. printLog(groupId,contact,msgContent,date,type,-END-);85. 86. c.close();87. 88. 89. privateStringgetTypeById(inttypeId)90. switch(typeId)91. case1:returnreceive;92. case2:returnsend;93. case3:returndraft;94. default:returnnone;95. 96. 97. 98. privatevoidprintLog(String.strings)99. for(Strings:strings)100. Log.i(TAG,s=null?NULL:s);101. 102. 103. 我们先分析一下testReadConversation()方法,它是用来读取所有的会话信息的,根据“content:/sms/conversations/”这个URI进行会话数据的读取操作,当取到数据后,对数据进一步的包装,具体做法是遇到号码时再根据“content:/com.android.contacts/phone_lookup/”到联系人中查找对应的名称,如果存在则显示名称而不是号码。我们注意到在查询会话时使用到了projection,这些都是根据什么制定的呢?这就需要我们去看一下源代码了。 我们找到TelephonyProvider中的com/android/providers/telephony/SmsProvider.java文件,看一看究竟:java view plaincopyprint?1. Override2. publicCursorquery(Uriurl,StringprojectionIn,Stringselection,3. StringselectionArgs,Stringsort)4. SQLiteQueryBuilderqb=newSQLiteQueryBuilder();5. 6. /Generatethebodyofthequery. 7. intmatch=sURLMatcher.match(url);8. switch(match)9. .10. caseSMS_CONVERSATIONS:11. qb.setTables(sms,(SELECTthread_idASgroup_thread_id,MAX(date)ASgroup_date,12. +COUNT(*)ASmsg_countFROMsmsGROUPBYthread_id)ASgroups);13. qb.appendWhere(sms.thread_id=groups.group_thread_idANDsms.date=14. +groups.group_date);15. qb.setProjectionMap(sConversationProjectionMap);16. break;17. .18. 19. 20. StringorderBy=null;21. 22. if(!TextUtils.isEmpty(sort)23. orderBy=sort;24. elseif(qb.getTables().equals(TABLE_SMS)25. orderBy=Sms.DEFAULT_SORT_ORDER;26. 27. 28. SQLiteDatabasedb=mOpenHelper.getReadableDatabase();29. Cursorret=qb.query(db,projectionIn,selection,selectionArgs,30. null,null,orderBy);31. 32. /TODO:SincetheURLsareamess,alwaysusecontent:/sms 33. ret.setNotificationUri(getContext().getContentResolver(),34. NOTIFICATION_URI);35. returnret;36. Error! Reference source not found.我们看到,在query方法的case语句中,如果是SMS_CONVERSATIONS类型的话,就为SQLiteQueryBuilder实例对象qb设置对应的查询表和where语句,另外还会为其设置一个基本的查询映射map即sConversationProjectionMap,这个变量在下面代码中体现:java view plaincopyprint?1. static2. .3. sURLMatcher.addURI(sms,conversations,SMS_CONVERSATIONS);4. sURLMatcher.addURI(sms,conversations/*,SMS_CONVERSATIONS_ID);5. .6. 7. sConversationProjectionMap.put(Sms.Conversations.SNIPPET,8. sms.bodyASsnippet);9. sConversationProjectionMap.put(Sms.Conversations.THREAD_ID,10. sms.thread_idASthread_id);11. sConversationProjectionMap.put(Sms.Conversations.MESSAGE_COUNT,12. groups.msg_countASmsg_count);13. sConversationProjectionMap.put(delta,null);14. Error! Reference source not found.这几对数据有什么用处呢?如果我们查询时的projection为null的话,sConversationProjectionMap就将转换为默认的projection,最后查询结果中仅包含这三个最基本的字段:snippet、thread_id、msg_count,可以代表一个会话的最简明的信息,朋友们可以亲自试一试。 当然,如果想运行上面的测试用例,需要配置两个权限:读取短消息权限和读取联系人权限,如下:html view plaincopyprint?1. 2. 3. 4. Error! Reference source not found.然后我们运行一下测试用例,结果如下: 以上就是读取会话的全部内容,下面我们再介绍其中的testReadSMS()方法。在这个方法中我们试图将所有的短消息都获取到,使用了“content:/sms/”进行查询,这个查询相对简单了许多。另外代码中也有几个没使用到的URI,他们分别是收件箱、已发送和草稿箱,这几个查询是“content:/sms/”的子集,分别用了不同的选择条件对短信表进行查询,我们看一下具体的源代码:java view plaincopyprint?1. Override2. publicCursorquery(Uriurl,StringprojectionIn,Stringselection,3. StringselectionArgs,Stringsort)4. SQLiteQueryBuilderqb=newSQLiteQueryBuilder();5. 6. /Generatethebodyofthequery. 7. intmatch=sURLMatcher.match(url);8. switch(match)9. 10. caseSMS_ALL:11. constructQueryForBox(qb,Sms.MESSAGE_TYPE_ALL);12. break;13. caseSMS_INBOX:14. constructQueryForBox(qb,Sms.MESSAGE_TYPE_INBOX);15. break;16. 17. caseSMS_SENT:18. constructQueryForBox(qb,Sms.MESSAGE_TYPE_SENT);19. break;20. 21. caseSMS_DRAFT:22. constructQueryForBox(qb,Sms.MESSAGE_TYPE_DRAFT);23. break;24. 25. .26. Error! Reference source not found.可以看到,他们都调用了constructQueryForBox方法,这个方法是干什么的呢? java view plaincopyprint?1. privatevoidconstructQueryForBox(SQLiteQueryBuilderqb,inttype)2. qb.setTables(TABLE_SMS);3. 4. if(type!=Sms.MESSAGE_TYPE_ALL)5. qb.appendWhere(type=+type);6. 7. Error! Reference source not found.我们发现它其实是添加过滤条件的,如果不是查询全部,则添加类型过滤信息,因此查询出不同的短信集合。朋友们也可以亲自试一试不同类型的查询。 另外,如果我们想根据会话来查询对应的短信集合的话,我们可以用以下两种方式来完成:1.“content:/sms/”(selection:“thread_id=3”)2.“content:/sms/conversations/3”第一种比较容易想到查询的过程,即在上面的基础上加上“thread_id=3”这条where语句即可;第二种是在会话path后面跟上会话id即可,具体的逻辑如下:java view plaincopyprint?1. caseSMS_CONVERSATIONS_ID:2. intthreadID;3. try4. threadID=Integer.parseInt(url.getPathSegments().get(1);5. if(Log.isLoggable(TAG,Log.VERBOSE)6. Log.d(TAG,queryconversations:threadID=+threadID);7. 8. 9. catch(Exceptionex)10. Log.e(TAG,11. Badconversationthreadid:12. +url.getPathSegments().get(1);13. returnnull;14. 15. qb.setTables(TABLE_SMS);16. qb.appendWhere(thread_id=+threadID);17. break;Error! Reference source not found.我们可以看到,它最终还是和第一种方式走上了相同的道儿,两者没什么本质上的区别。但是从简单易用性上来讲,这一种方式是比较好的,朋友们可以比较一下。以上就是获取会话内容和短信内容的全部信息,下面我们介绍一下短信的写入操作。发送和写入短信在某些场合,我们需要发送短信,并将短信写入数据源中,这时我们就需要了解一下发送短信机制和写入短信机制。我们将试图发送一条短信到指定的地址,同时将短信的内容写入到短信数据源中,待短信发送成功后,我们告知用户发送成功,待对方接收到短信后,我们告知用户对方接收成功。要实现这些功能,我们需要了解以下几个重点内容:1.使用android.telephony.SmsManager的API发送短信2.使用ContentProvider机制对“content:/sms/sent”这个URI进行写入操作3.注册“SENT_SMS_ACTION”这个广播地址,待短信发送成功后接收到这条广播4.注册“DELIVERED_SMS_ACTION”这个广播地址,待对方接收到短信后接收到这条广播下面我们就用代码实现这些功能,创建一个名为SMSActivity的Activity,如下:java view plaincopyprint?1. vider;2. 3. importjava.util.List;4. 5. importandroid.app.Activity;6. importandroid.app.PendingIntent;7. importandroid.content.BroadcastReceiver;8. importandroid.content.ContentValues;9. importandroid.content.Context;10. importandroid.content.Intent;11. importandroid.content.IntentFilter;12. .Uri;13. importandroid.os.Bundle;14. importandroid.telephony.SmsManager;15. importandroid.view.View;16. importandroid.widget.EditText;17. importandroid.widget.Toast;18. 19. publicclassSMSActivityextendsActivity20. 21. privateSendReceiversendReceiver=newSendReceiver();22. privateDeliverReceiverdeliverReceiver=newDeliverReceiver();23. 24. privateEditTextaddress;25. privateEditTextbody;26. 27. Override28. protectedvoidonCreate(BundlesavedInstanceState)29. super.onCreate(savedInstanceState);30. setContentView(R.layout.sms);31. 32. address=(EditText)findViewById(R.id.address);33. body=(EditText)findViewById(R.id.body);34. 35. /注册发送成功的广播 36. registerReceiver(sendReceiver,newIntentFilter(SENT_SMS_ACTION);37. /注册接收成功的广播 38. registerReceiver(deliverReceiver,newIntentFilter(DELIVERED_SMS_ACTION);39. 40. 41. Override42. protectedvoidonDestroy()43. super.onDestroy();44. 45. unregisterReceiver(sendReceiver);46. unregisterReceiver(deliverReceiver);47. 48. 49. publicvoidsendSMS(Viewview)50. Stringaddress=this.address.getText().toString();51. Stringbody=this.body.getText().toString();52. /android.telephony.SmsManager,notandroid.telephony.gsm.SmsManager 53. SmsManagersmsManager=SmsManager.getDefault();54. /短信发送成功或失败后会产生一条SENT_SMS_ACTION的广播 55. PendingIntentsendIntent=PendingIntent.getBroadcast(this,0,newIntent(SENT_SMS_ACTION),0);56. /接收方成功收到短信后,发送方会产生一条DELIVERED_SMS_ACTION广播 57. PendingIntentdeliveryIntent=PendingIntent.getBroadcast(this,0,newIntent(DELIVERED_SMS_ACTION),0);58. if(body.length()70)/如果字数超过70,需拆分成多条短信发送 59. Listmsgs=smsManager.divideMessage(body);60. for(Stringmsg:msgs)61. smsManager.sendTextMessage(address,null,msg,sendIntent,deliveryIntent);62. 63. else64. smsManager.sendTextMessage(address,null,body,sendIntent,deliveryIntent);65. 66. 67. /写入到短信数据源 68. Content

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论