Android 开发之旅:短信的收发及在android模拟器之间实践(二)

简介:

引言

前面我们介绍都只是如何发送SMS消息,接下来我们介绍如何接收SMS消息,及另一种发短信的方式并增强为可以发生图片等,最后介绍一下emulator工具。本文的主要内容如下:

  1. 1~5见Android 开发之旅:短信的收发及在android模拟器之间实践(一)
  2. 6、温故知新之Intent
  3. 7、准备工作:SmsMessage类
  4. 8、SMS接收程序
  5. 9、另一种发送短信的方式:使用Intent
  6. 10、增强SMS为MMS

6、温故知新之Intent

此系列前面简单地接受过意图(Intent),这里再次简单介绍一下,在短信接收程序和使用Intent发送SMS中我们要用到。android应用程序的三大组件——Activities、Services、Broadcast Receiver,通过消息触发,这个消息就称作意图(Intent)。下面以Acitvity为例,介绍一下Intent。Android用Intent这个特殊的类实现在Activity与Activity之间的切换。Intent类用于描述应用的功能。在Intent的描述结构中,有两个最重要的部分:动作和动作对应的数据。典型的动作类型有MAIN、VIEW、PICK、EDIT等,我们在短信接收程序中就用到从广播意图中提取动作类型并判断是否是"android.provider.Telephony.SMS_RECEIVED",进而作深一步的处理。而动作对应的数据则以URI的形式表示。例如,要查看一个人的联系方式,需要创建一个动作为VIEW的Intent,以及表示这个人的URI。

通过解析各种Intent,从一个屏幕导航到另一个屏幕是很简单的。当向前导航时,Activity将会调用startActivity("指定一个Intent")方法。然后,系统会在所有已安装的应用程序中定义的IntentFilter中查找,找到最匹配的Intent对应的Activity。新的Activity接收到指定的Intent的通知后,开始运行。当startActivity()方法被调用时,将触发解析指定Intent的动作,该机制提供了两个关键的好处:

  1. Activity能够重复利用从其他组件中以Intent形式产生的请求。
  2. Activity可以在任何时候被具有相同IntentFilter的新的Activity取代。

7、准备工作:SmsMessage类

顾名思义,SmsMessage类是一个表示短信的类,为了更好地了解Android的短信机制及以后更好地编写短信相关程序,这里介绍一下该类的公有方法和常量,及嵌套枚举、类成员。

公有方法:

  1. public static int[]  calculateLength  (CharSequence  msgBody, boolean use7bitOnly)
    参数
    msgBody-要封装的消息、use7bitOnly-如果为TRUE,不是广播特定7-比特编码的部分字符被认为是单个空字符;如果为FALSE,且msgBody包含非7-比特可编码字符,长度计算使用16-比特编码。
    返回值
    返回一个4个元素的int数组,int[0]表示要求使用的SMS数量、int[1]表示编码单元已使用的数量、int[2]表示剩余到下个消息的编码单元数量、int[3]表示编码单元大小的指示器。
  2. public static int[]  calculateLength  (String  messageBody, boolean use7bitOnly)
    参数返回值跟上面类似
  3. public static SmsMessage   createFromPdu  (byte[] pdu)
    从原始的PDU(protocol description units)创建一个SmsMessage。这个方法很重要,在我们编写短信接收程序要用到,它从我们接收到的广播意图中获取的字节创建SmsMessage。
  4. public String  getDisplayMessageBody()
    返回短信消息的主体,或者Email消息主体(如果这个消息来自一个Email网关)。如果消息主体不可用,返回null。这个方法也很重要,在我们编写短信接收程序也要用到。
  5. public String   getDisplayOriginatingAddress  ()
    返回信息来源地址,或Email地址(如果消息来自Email网关)。如果消息主体不可用,返回null。这个方法在来电显示,短信接收程序中经常用到。
  6. public String   getEmailBody  ()
    如果isEmail为TRUE,即是邮件,返回通过网关发送Email的地址,否则返回null。
  7. public int  getIndexOnIcc  ()
    返回消息记录在ICC上的索引(从1开始的)
  8. public String   getMessageBody  ()
    以一个String返回消息的主体,如果它存在且是基于文本的。
  9. public SmsMessage.MessageClass   getMessageClass  ()
    返回消息的类。
  10. public String   getOriginatingAddress  ()
    以String返回SMS信息的来电地址,或不可用时为null。
  11. public byte[]  getPdu  ()
    返回消息的原始PDU数据。
  12. public int  getProtocolIdentifier  ()
    获取协议标识符。
  13. public String   getPseudoSubject  ()
  14. public String   getServiceCenterAddress  ()
    返回转播消息SMS服务中心的地址,如果没有的话为null。
  15. public int  getStatus  ()
    GSM:为一个SMS-STATUS-REPORT消息,它返回状态报告的status字段。这个字段表示之前提交的SMS消息的状态。
    CDMA:为不影响来自GSM的状态码,值移动到31-16比特。这个值由一个error类(25-16比特)和一个状态码(23-16比特)组成。
    如果是0,表示之前发送的消息已经被收到。
  16. public int  getStatusOnIcc  ()
    返回消息在ICC上的状态(已读、未读、已发送、未发送)。有下面的几个值:SmsManager.STATUS_ON_ICC_FREE、SmsManager.STATUS_ON_ICC_READ、SmsManager.STATUS_ON_ICC_UNREAD、SmsManager.STATUS_ON_ICC_SEND、SmsManager.STATUS_ON_ICC_UNSENT这几个值在上篇的SmsManager类介绍有讲到。
  17. public static SmsMessage.SubmitPdu   getSubmitPdu  (
           String  scAddress, String  destinationAddress, 
           short destinationPort, byte[] data, 
           boolean statusReportRequested)
    参数scAddress - 服务中心的地址(Sercvice Centre address,为null即使用默认的)、destinationAddress - 消息的目的地址、destinationPort- 发送消息到目的的端口号、data - 消息数据。
    返回值:一个包含编码了的SC地址(如果指定了的话)和消息内容的SubmitPdu,否则返回null,如果编码错误。
  18. public static SmsMessage.SubmitPdu   getSubmitPdu  (
           String  scAddress, String  destinationAddress,
           String  message, boolean statusReportRequested)
    和上面类似。
  19. public static int  getTPLayerLengthForPDU  (String  pdu)
    返回指定SMS-SUBMIT PDU的TP-Layer-Length,长度单位是字节而不是十六进字符。
  20. public long  getTimestampMillis  ()
    以currentTimeMillis()格式返回服务中心时间戳。
  21. public byte[]  getUserData  ()
    返回用户数据减去用户数据头部(如果有的话)
  22. public boolean  isCphsMwiMessage  ()
    判断是否是CPHS MWI消息
  23. public boolean  isEmail  ()
    判断是否是Email,如果消息来自一个Email网关且Email发送者(sender)、主题(subject)、解析主体(parsed body)可用,则返回TRUE。
  24. public boolean  isMWIClearMessage  ()
    判断消息是否是一个CPHS 语音邮件或消息等待MWI清除(clear)消息。
  25. public boolean  isMWISetMessage  ()
    判断消息是否是一个CPHS 语音邮件或消息等待MWI设置(set)消息。
  26. public boolean  isMwiDontStore  ()
    如果消息是一个“Message Waiting Indication Group:Discard Message”通知且不应该保存,则返回TRUE,否则返回FALSE。
  27. public boolean  isReplace  ()
    判断是否是一个“replace short message”SMS
  28. public boolean  isReplyPathPresent  ()
    判断消息的TP-Reply-Path位是否在消息中设置了。
  29. public boolean  isStatusReportMessage  ()
    判断是否是一个SMS-STATUS-REPORT消息。

常量值:

  1. public static final int  ENCODING_16BIT :值为3(0x00000003)
  2. public static final int  ENCODING_8BIT :值为2 (0x00000002)
  3. public static final int  ENCODING_UNKNOWN :值为0 (0x00000000) ,用户数据编码单元的大小。
  4. public static final int  MAX_USER_DATA_BYTES :值为140 (0x0000008c),表示每个消息的最大负载字节数。
  5. public static final int  MAX_USER_DATA_BYTES_WITH_HEADER :134 (0x00000086),如果一个用户数据有头部,该值表示它的最大负载字节数,该值假定头部仅包含CONCATENATED_8_BIT_REFENENCE元素。
  6. public static final int  MAX_USER_DATA_SEPTETS :值为160 (0x000000a0) ,表示每个消息的最大负载septets数。
  7. public static final int  MAX_USER_DATA_SEPTETS_WITH_HEADER :值为153 (0x00000099),如果存在用户数据头部,则该值表示最大负载septets数该值假定头部仅包含CONCATENATED_8_BIT_REFENENCE元素。

嵌套枚举成员SmsMessage.MessageClass的枚举值:

  1. public static final SmsMessage.MessageClass   CLASS_0
  2. public static final SmsMessage.MessageClass   CLASS_1
  3. public static final SmsMessage.MessageClass   CLASS_2
  4. public static final SmsMessage.MessageClass   CLASS_3
  5. public static final SmsMessage.MessageClass   CLASS_UNKNOWN

嵌套枚举成员SmsMessage.MessageClass的公有方法:

  1. public static SmsMessage.MessageClass valueOf (String name):返回值的字符串的值
  2. public static final MessageClass[]   values  ():返回MessageClass的值数组

嵌套类成员SmsMessage.SubmitPdu的字段:

  1. public byte[]  encodedMessage :编码了的消息
  2. public byte[]  encodedScAddress :编码的服务中心地址

嵌套类成员SmsMessage.SubmitPdu的公有方法:

  1. public String   toString  ()
    返回一个包含简单的、可读的这个对象的描述字符串。鼓励子类去重写这个方法,并提供实现对象的类型和数据。默认实现简单地连接类名、@、十六进制表示的对象哈希码,即下面的形式: getClass().getName() + '@' + Integer.toHexString(hashCode())

8、SMS接收程序

当一个SMS消息被接收时,一个新的广播意图由android.provider.Telepony.SMS_RECEIVED动作触发。注意:这个一个字符串字面量(string  literal),但是SDK当前并没有包括这个字符串的引用,因此当要在应用程序中使用它时必须自己显示的指定它。现在我们就开始构建一个SMS接收程序:

1)、跟SMS发送程序类似,要在清单文件AndroidManifest.xml中指定权限允许接收SMS:

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

为了能够回发短信,还应该加上发送的权限。

2)、应用程序监听SMS意图广播,SMS广播意图包含了到来的SMS细节。我们要从其中提取出SmsMessage对象,这样就要用到pdu键提取一个SMS PDUs数组(protocol description units—封装了一个SMS消息和它的元数据),每个元素表示一个SMS消息。为了将每个PDU byte数组转化为一个SMS消息对象,需要调用SmsMessage.createFromPdu

每个SmsMessage包含SMS消息的详细信息,包括起始地址(电话号码)、时间戳、消息体。下面编写一个接收短信的类SmsReceiver代码如下:

 
  1. package skynet.com.conblogs.www;  
  2.  
  3. import android.content.BroadcastReceiver;  
  4. import android.content.Context;  
  5. import android.content.Intent;  
  6. import android.os.Bundle;  
  7. import android.telephony.SmsManager;  
  8. import android.telephony.SmsMessage;  
  9. import android.widget.Toast;  
  10.  
  11. public class SmsReceiver extends BroadcastReceiver {  
  12.     @Override 
  13.     public void onReceive(Context _context, Intent _intent) {  
  14.         if (_intent.getAction().equals(SMS_RECEIVER)) {  
  15.             SmsManager sms = SmsManager.getDefault();  
  16.  
  17.             Bundle bundle = _intent.getExtras();  
  18.             if (bundle != null) {  
  19.                 Object[] pdus = (Object[]) bundle.get("pdus");  
  20.                 SmsMessage[] messages = new SmsMessage[pdus.length];  
  21.                 for (int i = 0; i < pdus.length; i++)  
  22.                     messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);  
  23.                 for (SmsMessage message : messages) {  
  24.                     String msg = message.getMessageBody();  
  25.                     String to = message.getOriginatingAddress();  
  26.                     if (msg.toLowerCase().startsWith(queryString)) {  
  27.                         String out = msg.substring(queryString.length());  
  28.                         sms.sendTextMessage(to, null, out, nullnull);  
  29.  
  30.                         Toast.makeText(_context, "success",   
  31.                                 Toast.LENGTH_LONG).show();  
  32.                     }  
  33.                 }  
  34.             }  
  35.         }  
  36.     }  
  37.       
  38.     private static final String queryString="@echo";  
  39.     private static final String SMS_RECEIVER=  
  40.         "android.provider.Telephony.SMS_RECEIVED";  
 上面代码的功能是从接收到的广播意图中提取来电号码、短信内容,然后将短信加上@echo头部回发给来电号码,并在屏幕上显示一个Toast消息提示成功。

9、另一种发送短信的方式:使用Intent

上篇我们使用SmsManager类实现了发送SMS的功能,且并没有用到内置的客户端。实际上,我们很少这样做,自己在应用程序中去完全实现一个完整的SMS客户端。相反我们会去利用它,将需要发送的内容和目的手机号传递给内置的SMS客户端,然后发送。

下面我就向大家介绍如何利用Intent实现利用将我们的东西传递给内置SMS客户端发送我们SMS。为了实现这个功能,就要用到startActivity("指定一个Intent")方法,且指定Intent的动作为Intent.ACTION_SENDTO,用sms:指定目标手机号,用sms_body指定信息内容。java源文件如下所示:

 
  1. package skynet.com.cnblogs.www;  
  2.  
  3. import android.app.Activity;  
  4. import android.content.Intent;  
  5. import android.net.Uri;  
  6. import android.os.Bundle;  
  7. import android.view.View;  
  8. import android.widget.Button;  
  9. import android.widget.EditText;  
  10. import android.widget.Toast;  
  11.  
  12.  
  13. public class TextMessage extends Activity {  
  14.     /** Called when the activity is first created. */ 
  15.     @Override 
  16.     public void onCreate(Bundle savedInstanceState) {  
  17.         super.onCreate(savedInstanceState);  
  18.  
  19.         setContentView(R.layout.main);  
  20.         btnSend = (Button) findViewById(R.id.btnSend);  
  21.         edtPhoneNo = (EditText) findViewById(R.id.edtPhoneNo);  
  22.         edtContent = (EditText) findViewById(R.id.edtContent);  
  23.  
  24.         btnSend.setOnClickListener(new View.OnClickListener() {  
  25.             public void onClick(View v) {  
  26.                 String phoneNo = edtPhoneNo.getText().toString();  
  27.                 String message = edtContent.getText().toString();  
  28.                 if (phoneNo.length() > 0 && message.length() > 0) {  
  29.                      Intent smsIntent=new Intent(Intent.ACTION_SENDTO,  
  30.                              Uri.parse("sms:"+edtPhoneNo.getText().toString()));  
  31.                      smsIntent.putExtra("sms_body", edtContent.getText().toString());  
  32.                      TextMessage.this.startActivity(smsIntent);  
  33.                 } else 
  34.                     Toast.makeText(getBaseContext(),  
  35.                             "Please enter both phone number and message.",  
  36.                             Toast.LENGTH_SHORT).show();  
  37.             }  
  38.         });  
  39.     }  
  40.  
  41.     private Button btnSend;  
  42.     private EditText edtPhoneNo;  
  43.     private EditText edtContent;  

注意代码中的红色粗体部分,就是实现这个功能的核心代码!布局文件maim.xml和值文件string.xml跟上篇中的一样,这里不再累述。运行结果如下图:

Android 开发之旅:短信的收发及在android模拟器之间实践(二)

图2、程序主界面

点击send按钮之后,转到内置的SMS客户端并且将我们输入的值传入了,如下图:

Android 开发之旅:短信的收发及在android模拟器之间实践(二)

图3、内容传至内置SMS客户端

发送之后,5556号android模拟器会收到我们发送的消息,如下图:

Android 开发之旅:短信的收发及在android模拟器之间实践(二)

图5、发送之后5556号android模拟器收到消息

10、增强SMS为MMS

我们讲了这么多,都还只是实现了简单的发生SMS的功能,如果我们想发送图片、音频怎么办(⊙o⊙)?不急,现在我们就将第9节介绍的SMS发送程序改造为MMS。

我们可以附加一个文件到我们的消息做为附件发送,用Intent.EXTRA_STREAM和附件资源的Uri做为参数调用putExtra()方法,附加到信息。并设置Intent的类型为mime-type要注意的是:内置的MMS并不包括一个ACTION_SENDTO动作的Intent接收器,我们需要使用的动作类型是ACTION_SEND,并且目标手机号不在是使用sms:而是address主要代码如下:

// Get the URI of a piece of media to attach.
Uri attached_Uri = Uri.parse("content://media/external/images/media/1");
// Create a new MMS intent
Intent mmsIntent = new Intent(Intent.ACTION_SEND, attached_Uri);
mmsIntent.putExtra("sms_body", edtContent.getText().toString());
mmsIntent.putExtra("address", edtPhoneNo.getText().toString());
mmsIntent.putExtra(Intent.EXTRA_STREAM, attached_Uri);
mmsIntent.setType("image/png");
startActivity(mmsIntent);

将这段代码替换第9节中的红色粗体代码,就完成而来一个MMS的构建。

PS:这篇文章本应该很早就该发出来了,在6月20号就写好了,但由于现在的工作环境完全隔离了Internet,家里又还没有开通网络;还有一个原因是现在工作比较忙!请大家见谅,能够继续支持我,让我有动力写下去。还有一点,这篇文章在我电脑上放久了,对当时的状态有些忘了,不知道文中有什么遗漏和错误,请大家指出!

本系列的其它文章:

  1. Android 开发之旅:环境搭建及HelloWorld
  2. Android 开发之旅:HelloWorld项目的目录结构
  3. Android 开发之旅:android架构
  4. Android 开发之旅:应用程序基础及组件
  5. Android 开发之旅:应用程序基础及组件(续)
  6. Android 开发之旅:活动与任务
  7. Android 开发之旅:进程与线程
  8. Android 开发之旅:组件生命周期(一)
  9. Android 开发之旅:组件生命周期(二)
  10. Android 开发之旅:组件生命周期(三)
  11. Android 开发之旅:又见Hello World!
  12. Android 开发之旅:深入分析布局文件&又是"Hello World!"
  13. Android 开发之旅:view的几种布局方式及实践
  14. Android 开发之旅:短信的收发及在android模拟器之间实践(一)
  15. Android 开发之旅:短信的收发及在android模拟器之间实践(二)





     本文转自Saylor87 51CTO博客,原文链接:http://blog.51cto.com/skynet/363451,如需转载请自行联系原作者




相关文章
|
5天前
|
Linux 编译器 Android开发
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
在Linux环境下,本文指导如何交叉编译x265的so库以适应Android。首先,需安装cmake和下载android-ndk-r21e。接着,下载x265源码,修改crosscompile.cmake的编译器设置。配置x265源码,使用指定的NDK路径,并在配置界面修改相关选项。随后,修改编译规则,编译并安装x265,调整pc描述文件并更新PKG_CONFIG_PATH。最后,修改FFmpeg配置脚本启用x265支持,编译安装FFmpeg,将生成的so文件导入Android工程,调整gradle配置以确保顺利运行。
24 1
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
|
2天前
|
数据库 Android开发 开发者
安卓应用开发:构建高效用户界面的策略
【4月更文挑战第24天】 在竞争激烈的移动应用市场中,一个流畅且响应迅速的用户界面(UI)是吸引和保留用户的关键。针对安卓平台,开发者面临着多样化的设备和系统版本,这增加了构建高效UI的复杂性。本文将深入分析安卓平台上构建高效用户界面的最佳实践,包括布局优化、资源管理和绘制性能的考量,旨在为开发者提供实用的技术指南,帮助他们创建更流畅的用户体验。
|
3天前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin协程的优势与实践
【4月更文挑战第24天】随着移动开发技术的不断演进,提升应用性能和用户体验已成为开发者的核心任务。在Android平台上,Kotlin语言凭借其简洁性和功能性成为主流选择之一。特别是Kotlin的协程功能,它为异步编程提供了一种轻量级的解决方案,使得处理并发任务更加高效和简洁。本文将深入探讨Kotlin协程在Android开发中的应用,通过实际案例分析协程如何优化应用性能,以及如何在项目中实现协程。
|
19天前
|
移动开发 API Android开发
构建高效Android应用:探究Kotlin协程的优势与实践
【4月更文挑战第7天】 在移动开发领域,性能优化和应用响应性的提升一直是开发者追求的目标。近年来,Kotlin语言因其简洁性和功能性在Android社区中受到青睐,特别是其对协程(Coroutines)的支持,为编写异步代码和处理并发任务提供了一种更加优雅的解决方案。本文将探讨Kotlin协程在Android开发中的应用,揭示其在提高应用性能和简化代码结构方面的潜在优势,并展示如何在实际项目中实现和优化协程。
|
19天前
|
XML 开发工具 Android开发
构建高效的安卓应用:使用Jetpack Compose优化UI开发
【4月更文挑战第7天】 随着Android开发不断进化,开发者面临着提高应用性能与简化UI构建流程的双重挑战。本文将探讨如何使用Jetpack Compose这一现代UI工具包来优化安卓应用的开发流程,并提升用户界面的流畅性与一致性。通过介绍Jetpack Compose的核心概念、与传统方法的区别以及实际集成步骤,我们旨在提供一种高效且可靠的解决方案,以帮助开发者构建响应迅速且用户体验优良的安卓应用。
|
22天前
|
监控 算法 Android开发
安卓应用开发:打造高效启动流程
【4月更文挑战第5天】 在移动应用的世界中,用户的第一印象至关重要。特别是对于安卓应用而言,启动时间是用户体验的关键指标之一。本文将深入探讨如何优化安卓应用的启动流程,从而减少启动时间,提升用户满意度。我们将从分析应用启动流程的各个阶段入手,提出一系列实用的技术策略,包括代码层面的优化、资源加载的管理以及异步初始化等,帮助开发者构建快速响应的安卓应用。
|
22天前
|
Java Android开发
Android开发之使用OpenGL实现翻书动画
本文讲述了如何使用OpenGL实现更平滑、逼真的电子书翻页动画,以解决传统贝塞尔曲线方法存在的卡顿和阴影问题。作者分享了一个改造后的外国代码示例,提供了从前往后和从后往前的翻页效果动图。文章附带了`GlTurnActivity`的Java代码片段,展示如何加载和显示书籍图片。完整工程代码可在作者的GitHub找到:https://github.com/aqi00/note/tree/master/ExmOpenGL。
23 1
Android开发之使用OpenGL实现翻书动画
|
22天前
|
Android开发 开发者
Android开发之OpenGL的画笔工具GL10
这篇文章简述了OpenGL通过GL10进行三维图形绘制,强调颜色取值范围为0.0到1.0,背景和画笔颜色设置方法;介绍了三维坐标系及与之相关的旋转、平移和缩放操作;最后探讨了坐标矩阵变换,包括设置绘图区域、调整镜头参数和改变观测方位。示例代码展示了如何使用这些方法创建简单的三维立方体。
18 1
Android开发之OpenGL的画笔工具GL10