赞
踩
Android SmsMessage类
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]表示编码单元大小的指示器。
public static int[] calculateLength (String messageBody, boolean use7bitOnly)
参数和返回值跟上面类似
public static SmsMessage createFromPdu (byte[] pdu)
从原始的PDU(protocol description units)创建一个SmsMessage。这个方法很重要,在我们编写短信接收程序要用到,它从我们接收到的广播意图中获取的字节创建SmsMessage。
public String getDisplayMessageBody()
返回短信消息的主体,或者Email消息主体(如果这个消息来自一个Email网关)。如果消息主体不可用,返回null。这个方法也很重要,在我们编写短信接收程序也要用到。
public String getDisplayOriginatingAddress ()
返回信息来源地址,或Email地址(如果消息来自Email网关)。如果消息主体不可用,返回null。这个方法在来电显示,短信接收程序中经常用到。
public String getEmailBody ()
如果isEmail为TRUE,即是邮件,返回通过网关发送Email的地址,否则返回null。
public int getIndexOnIcc ()
返回消息记录在ICC上的索引(从1开始的)
public String getMessageBody ()
以一个String返回消息的主体,如果它存在且是基于文本的。
public SmsMessage.MessageClass getMessageClass ()
返回消息的类。
public String getOriginatingAddress ()
以String返回SMS信息的来电地址,或不可用时为null。
public byte[] getPdu ()
返回消息的原始PDU数据。
public int getProtocolIdentifier ()
获取协议标识符。
public String getPseudoSubject ()
public String getServiceCenterAddress ()
返回转播消息SMS服务中心的地址,如果没有的话为null。
public int getStatus ()
GSM:为一个SMS-STATUS-REPORT消息,它返回状态报告的status字段。这个字段表示之前提交的SMS消息的状态。
CDMA:为不影响来自GSM的状态码,值移动到31-16比特。这个值由一个error类(25-16比特)和一个状态码(23-16比特)组成。
如果是0,表示之前发送的消息已经被收到。
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类介绍有讲到。
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,如果编码错误。
public static SmsMessage.SubmitPdu getSubmitPdu (
String scAddress, String destinationAddress,
String message, boolean statusReportRequested)
和上面类似。
public static int getTPLayerLengthForPDU (String pdu)
返回指定SMS-SUBMIT PDU的TP-Layer-Length,长度单位是字节而不是十六进字符。
public long getTimestampMillis ()
以currentTimeMillis()格式返回服务中心时间戳。
public byte[] getUserData ()
返回用户数据减去用户数据头部(如果有的话)
public boolean isCphsMwiMessage ()
判断是否是CPHS MWI消息
public boolean isEmail ()
判断是否是Email,如果消息来自一个Email网关且Email发送者(sender)、主题(subject)、解析主体(parsed body)可用,则返回TRUE。
public boolean isMWIClearMessage ()
判断消息是否是一个CPHS 语音邮件或消息等待MWI清除(clear)消息。
public boolean isMWISetMessage ()
判断消息是否是一个CPHS 语音邮件或消息等待MWI设置(set)消息。
public boolean isMwiDontStore ()
如果消息是一个“Message Waiting Indication Group:Discard Message”通知且不应该保存,则返回TRUE,否则返回FALSE。
public boolean isReplace ()
判断是否是一个“replace short message”SMS
public boolean isReplyPathPresent ()
判断消息的TP-Reply-Path位是否在消息中设置了。
public boolean isStatusReportMessage ()
判断是否是一个SMS-STATUS-REPORT消息。
常量值:
public static final int ENCODING_16BIT :值为3(0x00000003)
public static final int ENCODING_8BIT :值为2 (0x00000002)
public static final int ENCODING_UNKNOWN :值为0 (0x00000000) ,用户数据编码单元的大小。
public static final int MAX_USER_DATA_BYTES :值为140 (0x0000008c),表示每个消息的最大负载字节数。
public static final int MAX_USER_DATA_BYTES_WITH_HEADER :134 (0x00000086),如果一个用户数据有头部,该值表示它的最大负载字节数,该值假定头部仅包含CONCATENATED_8_BIT_REFENENCE元素。
public static final int MAX_USER_DATA_SEPTETS :值为160 (0x000000a0) ,表示每个消息的最大负载septets数。
public static final int MAX_USER_DATA_SEPTETS_WITH_HEADER :值为153 (0x00000099),如果存在用户数据头部,则该值表示最大负载septets数该值假定头部仅包含CONCATENATED_8_BIT_REFENENCE元素。
嵌套枚举成员SmsMessage.MessageClass的枚举值:
public static final SmsMessage.MessageClass CLASS_0
public static final SmsMessage.MessageClass CLASS_1
public static final SmsMessage.MessageClass CLASS_2
public static final SmsMessage.MessageClass CLASS_3
public static final SmsMessage.MessageClass CLASS_UNKNOWN
嵌套枚举成员SmsMessage.MessageClass的公有方法:
public static SmsMessage.MessageClass valueOf (String name):返回值的字符串的值
public static final MessageClass[] values ():返回MessageClass的值数组
嵌套类成员SmsMessage.SubmitPdu的字段:
public byte[] encodedMessage :编码了的消息
public byte[] encodedScAddress :编码的服务中心地址
嵌套类成员SmsMessage.SubmitPdu的公有方法:
public String toString ()
返回一个包含简单的、可读的这个对象的描述字符串。鼓励子类去重写这个方法,并提供实现对象的类型和数据。默认实现简单地连接类名、@、十六进制表示的对象哈希码,即下面的形式: getClass().getName() + '@' + Integer.toHexString(hashCode())
BroadcastReceiver是接收从sendBroadcast()发出的intent的基类。你可以通过Context.registerReceiver()方法在代码中动态的注册一个BroadcastReceiver的实例,也可以通过再AndroidManifest.xml文件中用<receiver>标签来静态声明。
注意:如果你实在Activity.onResume()方法中注册的一个receiver,那么你必须在Activity.onPause()方法中进行注销。(当一个activity处于暂停状态是不会接收intents的,并且这样做也可以减小系统不必要的开销)。不要在Activity.onSaveInstanceState()方法中注销receiver,因为activity从栈中恢复的时候并不会调用这个方法了。
订阅广播:
<receiver android:name="MySMSListener"> <intent-filter> <action android:name="android.provider.Telephony.SMS_RECEIVED"/> </intent-filter> </receiver> |
IntentFilter filter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED"); IncomingSMSReceiver receiver = new IncomingSMSReceiver(); registerReceiver(receiver, filter); |
可以接收的broadcast主要分为两种类型:
普通的broadcasts(通过Context.sendBroadcast发送)是完全异步的。这个broadcast的receiver以无序的状态运行,经常是在同一时刻运行。这种做法是十分高效的,但是也意味着receiver不能够利用相互处理的结果或者是调用退出的API来退出(因为不知道哪个receiver先接收到intent)。
有序的broadcasts(通过Context.sendOrderedBroadcast发送)一次只发送给一个receiver。每一个receiver是有序的处理这个intent的,前面的receiver可以传递结果给下一个receiver,或者任意一个receiver都可以完全的退出,这样intent就不会传递给其他的receivers.receiver的执行顺序可以通过匹配的intent-filter中的android:priority属性来控制;如果有多个receivers处于同一个优先级,那么这几个receivers将会以任意的顺序来执行。
即使是在广播普通的broadcasts的情况下,系统也有可能在某些情况下转换为一次发送一个broadcast给一个receriver。特别是当receivers需要创建进程时,在同一时刻仅仅一个receiver可以运行,避免系统因为这些新建的进程而过载。
注意:尽管Intent类是用来发送和接受这些broadcasts,这里的Intent broadcast机制和那些通过Context.startActivity()方法来启动activity的intent是完全独立的。一个BroadcastReceiver是没办法观察和捕获一个用于启动activity的intent的;同样的,当你通过intent来发出broadcast时,你也不可能(通过这个intent)找到或者启动一个activity的。这两种操作是完全不同的:通过一个intent来启动一个activity是一个前台操作,会改变用户当前交互的对象;而通过intent来发出broadcast是一个后台操作,用户经常是察觉不到的。
BroadcastReceiver类(通过一个manifest的<receiver>标签作为一个组件启动)是应用程序全局声明周期重要的一部分。
讨论的主题
1、Receiver的生命周期
2、权限
3、进程的生命周期
可以接收的broadcast主要分为两种类型:
普通的broadcasts(通过Context.sendBroadcast发送)是完全异步的。这个broadcast的receiver以无序的状态运行,经常是在同一时刻运行。这种做法是十分高效的,但是也意味着receiver不能够利用相互处理的结果或者是调用退出的API来退出(因为不知道哪个receiver先接收到intent)。
有序的broadcasts(通过Context.sendOrderedBroadcast发送)一次只发送给一个receiver。每一个receiver是有序的处理这个intent的,前面的receiver可以传递结果给下一个receiver,或者任意一个receiver都可以完全的退出,这样intent就不会传递给其他的receivers.receiver的执行顺序可以通过匹配的intent-filter中的android:priority属性来控制;如果有多个receivers处于同一个优先级,那么这几个receivers将会以任意的顺序来执行。
即使是在广播普通的broadcasts的情况下,系统也有可能在某些情况下转换为一次发送一个broadcast给一个receriver。特别是当receivers需要创建进程时,在同一时刻仅仅一个receiver可以运行,避免系统因为这些新建的进程而过载。
注意:
尽管Intent类是用来发送和接受这些broadcasts,这里的Intent broadcast机制和那些通过Context.startActivity()方法来启动activity的intent是完全独立的。一个BroadcastReceiver是没办法观察和捕获一个用于启动activity的intent的;同样的,当你通过intent来发出broadcast时,你也不可能(通过这个intent)找到或者启动一个activity的。这两种操作是完全不同的:通过一个intent来启动一个activity是一个前台操作,会改变用户当前交互的对象;而通过intent来发出broadcast是一个后台操作,用户经常是察觉不到的。
BroadcastReceiver类(通过一个manifest的<receiver>标签作为一个组件启动)是应用程序全局声明周期重要的一部分。
讨论的主题
1、Receiver的生命周期
2、权限
3、进程的生命周期
Receiver的生命周期
一个BroadcastReceiver的对象仅仅在调用onReceiver(COntext, Intent)的时间中有效。一旦你的代码从这个函数中返回,那么系统就认为这个对象应该结束了,不能再被激活。你在onReceive(Context, Intent)中的实现有着非常重要的影响:任何对于异步操作的请求都是不允许的,因为你可能需要从这个函数中返回去处理异步的操作,但是在那种情况下,BroadcastReceiver将不会再被激活,因此系统就会再异步操作之前杀死这个进程。
特别是,你不应该再一个BroadcastReceiver中显示一个对话框或者绑定一个服务。对于前者(显示一个对话框),你应该用NotificationManagerAPI来替代,对于后者(绑定一个服务),你可以使用Context.startService()发送一个命令给那个服务来实现绑定效果。
权限
存取的权限可以通过在发送方的Intent或者接收方的Intent中强制指定。
在发送一个broadcast时强制指定权限,就必须提供一个非空的peemission参数给sendBroadcast(Intent, String)或者是sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handel, int, String, Bundle)。只有那些拥有这些权限(通过在ANdroidManifest.xml文件中相应的声明<uses-permission>标签)的receiver能够接收这些broadcast。
在接收一个broadcast时强制指定权限,就必须在注册receiver时提供一个非空的permission参数--无论是在调用registerReceiver(BroadcastReceiver, IntentFilter, String, android.os.Handler)或者是通过再AndroidManifest.xml文件中通过<receiver>静态标签来声明。只有那些拥有这些权限(通过在相应的AndroidManifest.xml文件中查询<uses-permission>标签来获知)的发送方将能够给这个receiver发送Intent。
对于安全和权限的详细内容请查看Security and Permission文档。
进程的生命周期
一个正在执行BroadcastReceiver(也就是,正在执行onReceive(COntext, Intent)方法)的进程被认为是一个前台的进程,将会一直运行,除非系统处于内存极度低的情况下。
一旦从OnReceive()方法中返回,这个BroadcastReceiver将不会再被激活,此时它的主进程就和任何其他运行于此应用程序中的组件拥有相同的优先级。这一点非常重要,如果进程仅仅只是拥有BroadReceiver(一个普遍的情况是用户从不或者是最近没有和它进行交互),因此一旦它从onReceive()方法中返回时,系统就会认为进程是空的并且主动的杀死它,以便这些资源可以被其他重要的进程利用。
这意味着对于耗时的操作,可以采用将Service和BroadcastReceiver结合使用以确保执行这个操作的进程在整个执行过程中都保持激活状态。
服务器(videoweb):
l 修改formbean: VideoForm中增加短信的时间、内容和发送者属性
l VideoManageAction中增加方法getSMS来获取窃听器发送的短消息
public ActionForward getSMS(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception { VideoForm formbean = (VideoForm)form; System.out.println("发送时间:"+ formbean.getTime()); System.out.println("谁给她发的短信:"+ formbean.getSender()); System.out.println("内容:"+ formbean.getContent()); return mapping.findForward("result"); } |
客户端SMS_Listener
1、 清单文件
中订阅广播
<receiver android:name=".MySMSListener"> <intent-filter> <action android:name="android.provider.Telephony.SMS_RECEIVED"/> </intent-filter> </receiver> |
添加短信接收权限,访问网络权限
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.RECEIVE_SMS"/> |
2、 客户端MySMSListener.java
功能:收取短信广播,接收并解析短信然后发送至服务器端进行后台打印。
package cn.class3g.smslistener; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; import cn.class3g.utils.SocketHttpRequester; … public class MySMSListener extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { Object[] pdus = (Object[]) intent.getExtras().get("pdus"); if (pdus != null && pdus.length > 0) { SmsMessage[] messages = new SmsMessage[pdus.length]; for (int i = 0; i < messages.length; i++) { byte[] pdu = (byte[]) pdus[i]; messages[i] = SmsMessage.createFromPdu(pdu); } for (SmsMessage msg : messages) { String content = msg.getMessageBody(); String sender = msg.getOriginatingAddress(); Date date = new Date(msg.getTimestampMillis()); SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss"); String sendTime = sdf.format(date); Map<String,String> param = new HashMap<String,String>(); param.put("method", "getSMS"); param.put("sender", sender); param.put("content", content); param.put("time", sendTime); String path = "http://192.168.1.100:8080/videoweb/video/manage.do"; try { SocketHttpRequester.post(path, param, "UTF-8"); } catch (Exception e) { Log.e("TAG",e.toString()); } } } } } |
SmsMessage
public static SmsMessage createFromPdu (byte[] pdu)
从原始的PDU(protocol description units)创建一个SmsMessage。这个方法很重要,在我们编写短信接收程序要用到,它从我们接收到的广播意图中获取的字节创建SmsMessage。
public String getOriginatingAddress ()
以String返回SMS信息的来电地址,或不可用时为null。
public String getMessageBody ()
以一个String返回消息的主体,如果它存在且是基于文本的。
短信管理器 : SmsManager
1). 在 Android 2.0 以前 应该使用 android.telephony.gsm.SmsManager
之后应该用 android.telephony.SmsManager;
2). 获取系统默认的短信管理器
SmsManager smsManager = SmsManager.getDefault();
3). 按照每条短信最大字数来拆分短信
List<String> divideContents = smsManager.divideMessage(content);
4). 发送短信
smsManager.sendTextMessage(destinationAddress, scAddress, text, sentIntent, deliveryIntent)
-- destinationAddress:目标电话号码
-- scAddress:短信中心号码,测试可以不填
-- text: 短信内容
-- sentIntent:发送 -->中国移动 --> 中国移动发送失败 --> 返回发送成功或失败信号 --> 后续处理 即,这个意图包装了短信发送状态的信息
-- deliveryIntent: 发送 -->中国移动 --> 中国移动发送成功 --> 返回对方是否收到这个信息 --> 后续处理 即:这个意图包装了短信是否被对方收到的状态信息(供应商已经发送成功,但是对方没有收到)。
5). 声明短信发送权限
* AndroidManifest.xml
<uses-permission android:name="android.permission.SEND_SMS"/>
3、 进一步添加客户端功能:将监听到的指定的短信进行拦截并且自动进行回复
添加短信发送权限:
<uses-permission android:name="android.permission.SEND_SMS"/> |
MySMSListener的onReceive中添加代码如下
String sendContent = sdf.format(date) + ":" + sender + "--" + content; Log.i("TAG",sendContent); if(sender!= null && sender.endsWith("5556")){//5556".equals(sender)){ SmsManager smsManager = SmsManager.getDefault(); smsManager.sendTextMessage("5556",null,"game over",null,null); this.abortBroadcast(); //终止广播 } |
测试:
启动另一个模拟器,向部署客户端的模拟器发送短信,查看服务器端后台输出,并且观察客户端运行模拟器是否有短信接收。
除了短信到来广播 Intent , Android 还有很多广播 Intent ,如:开机启动、电池电量变化、时间已经改变等广播 Intent 。
l 接收电池电量变化广播 Intent ,在 AndroidManifest.xml 文件中的 <application>
节点里订阅此 Intent:
<receiver android:name=".IncomingSMSReceiver"> <intent-filter> <action android:name="android.intent.action.BATTERY_CHANGED"/> </intent-filter> </receiver> |
l 接收开机启动广播 Intent , 在 AndroidManifest.xml 文件中的 <application> 节点里
订阅此 Intent:
<receiver android:name=".IncomingSMSReceiver"> <intent-filter> <action android:name=”android.intent.action.BOOT_COMPLETED” /> </intent-filter> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> |
通常一个 BroadcastReceiver 对象的生命周期不超过 5 秒 , 所以在 BroadcastReceiver里不能做一些比较耗时的操作 , 如果需要完成一项比较耗时的工作 , 可以通过发送 Intent给 Activity 或 Service ,由 Activity 或 Service 来完成。
public class IncomingSMSReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { // 发送 Intent 启动服务,由服务来完成比较耗时的操作 Intent service = new Intent(context, XxxService.class); context.startService(service); // 发送 Intent 启动 Activity ,由 Activity 来完成比较耗时的操作 Intent newIntent = new Intent(context, XxxActivity.class); context.startActivity(newIntent); } } |
当然 , 实现了 BroadcastReceiver , 有时你可能会觉得不需要它 , 那么你可以将已经注册好的 BroadcastReceiver 进行注销 :
unregisterReceiver( BroadcastReceiver receiver) ; |
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。