Android基础笔记(九)- 广播

  • 广播的概念
  • 广播的生命周期
  • 案例-监听短信到来并解析短信内容
  • 案例-拦截外拨电话并设置区号
  • 案例-SD卡状态监听
  • 介绍一些常用的广播
  • 发送自定义广播
  • 有序广播和无序广播

Android基础笔记(九)- 广播_第1张图片

启程!!!

广播的概念

在Android中,Broadcast是一种广泛运用的在应用程序之间传输信息的机制。而BroadcastReceiver是对发送出来的Broadcast进行过滤接受并响应的一类组件。

广播接收者(BroadcastReceiver)用于接收广播Intent的, 广播Intent的发送是通过调用sendBroadcast/sendOrderedBroadcast来实现的。通常一个广播Intent可以被订阅了此Intent的多个广播接收者所接收。

广播的种类:
无序广播(Normal broadcasts),类似于日常使用的WIFI,由一个广播者发出信号,可以有很多接收者同时接收,并且信号无法被中断和篡改;
有序广播(Ordered broadcasts),可以有最终的接收者,并存在信号接收的优先级;
定义广播接收者的步骤:
①定义一个类,继承 BroadcastReceiver,并重写 onReceive(Context context, Intent intent)方法,处理广播到来的事件;
②注册广播接收者。有两种方式,一种是静态的:在 AndroidManifest.xml文件中实现 <receiver>标签;一种是动态的:使用 Context.registerReceiver()方法动态注册广播接收者;
注意:注册你的广播接收者在 Activity.onResume()方法中;反注册你的广播接收者在 Activity.onPause()中。

本篇注册的广播接收者,都在清单文件中声明;代码注册的方式在下一篇中讲解;

广播的生命周期

广播接收者也是运行在主线程中,所以在广播接收者的onReceive方法内不能有耗时的操作,需要放在子线程中做。

onReceive的生命周期很短,有可能广播接收者结束,子线程还没有结束,这时广播接收者所在进程很有可能被杀掉,这样子线程就会出问题,所以耗时操作最好放到service服务中。

注意事项:
①广播接收者的生命周期是非常短暂的,在接收到广播的时候创建,onReceive()方法结束之后销毁。
②广播接收者中不要做一些耗时的工作,否则会弹出 Application No Response错误对话框。
③最好也不要在广播接收者中创建子线程做耗时的工作,因为广播接收者被销毁后进程就成为了空进程,很容易被系统杀掉。

案例-监听短信到来并解析短信内容

**注意:**4.0版本之后为了安全考虑,要求应用程序必须有界面,且被运行过一次,的程序才能接收广播事件。

通过一个小小的案例来讲解下广播接收者的使用步骤和短信的广播的监听与操作,和一些常用的API;

第一步:定义一个类,继承BroadcastReceiver,并重写onReceive(Context context, Intent intent)方法,处理广播到来的事件;

public class SmsReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        // 以下是获取短信信息的固定写法 

        Bundle bundle = intent.getExtras();
        // 获取到短信的数据
        Object[] objects = (Object[]) bundle.get("pdus");
        for (Object object : objects) {
            SmsMessage sms = SmsMessage.createFromPdu((byte[]) object);
            // 获取短信的发送者
            String originatingAddress = sms.getOriginatingAddress();
            // 获取短信的内容
            String messageBody = sms.getMessageBody();
            System.out.println("广播接收者收到短信:(" + originatingAddress + "," + messageBody + ")");
        }

    }
}

第二步:注册广播接收者。在AndroidManifest.xml文件中实现<receiver>标签;

<!-- 接收短信的权限 -->
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<!-- 声明广播接收者 -->
<receiver android:name="com.bzh.sms.SmsReceiver" >
   <!-- 接收短信广播事件的意图过滤器 -->
   <intent-filter>
       <action android:name="android.provider.Telephony.SMS_RECEIVED" />
   </intent-filter>
</receiver>

值得我们注意的是,在清单文件中声明<receiver>时,千万不要用错标签,假如写成了<activity>的话,系统运行不会报错,但是会接收不到广播事件。

另外,在onReceiver()方法中的解析短信是固定写法,不需要记住,用的时候查一下就行了。

案例-拦截外拨电话并设置区号

也是个很简单的案例,按照上边的步骤创建并声明、添加过滤器就可以了 ,这里主要演示一下几个API的使用。

<!-- 监听外拨电话的权限 -->
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<!-- 声明广播接收者 -->
<receiver android:name="com.bzh.ip.OutCallReceiver" >
    <!-- 接收外拨电话广播需要添加的过滤器 -->
    <intent-filter>
        <action android:name="android.intent.action.NEW_OUTGOING_CALL" >
        </action>
    </intent-filter>
</receiver>

看一下广播接收着中的代码:

public class OutCallReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        String resultData = getResultData();
        System.out.println("未加拨区号前:" + resultData);
        setResultData("010" + resultData);
    }
}

请看一下测试图:

我们拨打的是110,在经过我们自定义的广播接收者后变成了010110,说明我们已经成功的修改了外拨电话广播中的数据。那么我们看一下getResultData()setResultData(String),一个是获取广播中的数据,一个是修改广播中的数据;

但是我们又发现setResultData(String)方法的API中有这么一句only works with broadcasts sent through Context.sendOrderedBroadcast,这说明外拨电话广播事件,是个有序广播,使用Context.sendOrderedBroadcast这个方法来发送的,这个方法下面的案例再详细讲解。

案例-SD卡状态监听

清单文件中所需的过滤器如下,为了监听卸载SD卡的状态,必须指定scheme的类型为file:

<receiver android:name="com.bzh.sdcard.SDCardReceiver" >
    <intent-filter>
        <action android:name="android.intent.action.MEDIA_UNMOUNTED" />
        <data android:scheme="file" />
    </intent-filter>
</receiver>

再看下onReceiver()中的代码

@Override
public void onReceive(Context context, Intent intent) {

    // 获取到触发广播的动作
    String action = intent.getAction();
    if ("android.intent.action.MEDIA_UNMOUNTED".equals(action)) {
        System.out.println("SD卡被卸载了");
    }
}

这里指的注意的是,我们可以通过onReceiver()传递给我们的intent意图对象,拿到触发广播的动作。

介绍一些常用的广播

开机自动启动广播

Android3.0以前不需要加权限, 3.0版本以后增加了安全性, 如果应用程序没有被开启过, 是不可以接收到系统启动完成的广播的.

拦截的动作:android.intent.action.BOOT_COMPLETED
需要的权限:android.permission.RECEIVE_BOOT_COMPLETED
安装和卸载程序的广播

拦截动作:

安装: android.intent.action.PACKAGE_ADDED
卸载: android.intent.action.PACKAGE_REMOVED

指定scheme: package

拦截接收的短信广播

(android 4.2 后废除了此action),但是还是可以使用,拦截动作:

android.provider.Telephony.SMS_RECEIVED      
优先级别设置为最大, 在系统收到短信之前接收短信:priority="1000“
需要权限: android.permission.RECEIVE_SMS

发送自定义广播

发送自定的广播也很简单,根据Google的Api发现可以使用sendBroadcast(intent)方法,把一个意图作为广播发送出去。

此外,我们知道Intent对象可以设置意图(广播)的动作Action;这样的话只要广播接收者设置关心的动作,就能够接收到广播了;

下面是一个超级简单的发送广播的代码;

public void click(View v) {

    Intent intent = new Intent();
    intent.setAction("com.bzh.biezhihua");
    intent.putExtra("name", "别志华");

    // Broadcast the given intent to all interested BroadcastReceivers.
    sendBroadcast(intent);
}

在另一个工程中,我们创建广播接收者并设置意图过滤器(指定关心的动作),就可以得到数据了;

下面是清单文件中广播接收者的配置。

<receiver android:name="com.bzh.receiverbroadcast.BiezhihuaReceiver" >
    <intent-filter>

        <!-- 接收感兴趣的广播事件 -->
        <action android:name="com.bzh.biezhihua" />
    </intent-filter>
</receiver>

下面是接收到广播后,代码的处理。

@Override
public void onReceive(Context context, Intent intent) {
    // 取出数据
    String extra = intent.getStringExtra("name");
    System.out.println(extra);
}

有序广播和无序广播

在一开始我们就知道了有无序广播和有序广播,而有序广播在上面已经讲述了如何创建和使用了,那么无序广播如何创建和使用?以及它们之间有什么区别?

无序广播
不可以被拦截
有序广播
①按照一定的优先级发送,可以在 <intent-filter android:priority="1000">中配置优先级;优先级范围是(-1000,1000)
②可以被拦截、数据可以被修改;

接下来一步一步使用一下有序广播,看看它是怎么样工作的;

需要提前说一下的是:在有序广播中,我们可以使用broadcastReceiver.setResult(int code, String data, Bundle extras)方法修改广播中的数据,也可以使用broadcastReceiver.abortBroadcast()方法终止广播;

① 首先,创建发送有序广播工程,并发送一个有序广播,有序广播有很多参数,在代码中已经给出了注释;

其中,我们给广播设置的动作是com.bzh.sendorder这意味着,广播的接收者需要在意图过滤器中指定该动作和优先级;

public void click(View v) {
    // 被发送的广播
    Intent intent = new Intent();
    intent.setAction("com.bzh.sendorder");// 设置动作

    // 接收广播需要的权限;一般填入null
    String receiverPermission = null;

    // 有序广播的监听者,会在最后接收到发送出的广播
    BroadcastReceiver resultReceiver = new FinalReceiver();

    Handler scheduler = null;

    // 广播的发送码
    int initialCode = 200;

    // 广播的内容,可以使用getResultDataa()方法获取
    String initialData = "我是广播发送者";

    // 广播携带的数据
    Bundle initialExtras = new Bundle();
    initialExtras.putString("name", "别志华");

    sendOrderedBroadcast(intent, receiverPermission, resultReceiver, scheduler, initialCode, initialData, initialExtras);

    System.out.println("发送了有序广播");
}

可能你已经注意到了BroadcastReceiver resultReceiver = new FinalReceiver();它代表着监察广播工作的广播接收者,它会在广播的最后收到广播,用以监察广播中的数据改变;

同样是继承BroadcastReceiver但是却不需要在清单文件中声明;

下面是FinalReceiver监察者的代码;

@Override
public void onReceive(Context context, Intent intent) {
    int resultCode = getResultCode();
    String resultData = getResultData();
    Bundle resultExtras = getResultExtras(true);
    String name = resultExtras.getString("name");

    System.out.println("我是有序广播最后的监听者-结果码:" + resultCode + ",内容:" + resultData + ",携带的数据:" + name);
}

② 然后,创建有序广播接收者工程,并创建FirstReceiverSecondReceiver两个广播接收者;

下面是清单文件中的声明和代码;

<receiver android:name="com.bzh.receiverorder.FirstReceiver" >
   <intent-filter android:priority="1000" >
        <action android:name="com.bzh.sendorder" />
    </intent-filter>
</receiver>
<receiver android:name="com.bzh.receiverorder.SecondReceiver" >
    <intent-filter android:priority="900" >
        <action android:name="com.bzh.sendorder" />
    </intent-filter>
</receiver>

这是第一个广播接收者的代码:

@Override
public void onReceive(Context context, Intent intent) {

    // 拿到广播发送的结果码
    int resultCode = getResultCode();

    // 拿到广播发送的内容
    String resultData = getResultData();

    // 拿到广播携带的数据
    Bundle resultExtras = getResultExtras(true);
    String name = resultExtras.getString("name");

    System.out.println("第一个接收者-结果码:" + resultCode + ",内容:" + resultData + ",携带的数据:" + name);

    // 修改携带的数据
    resultExtras.putString("name", "胡玉琼");

    // 继续传播广播
    setResult(300, "我是第一个接收者", resultExtras);
}

这是第二个广播接收者的代码:

@Override
public void onReceive(Context context, Intent intent) {

    int resultCode = getResultCode();
    String resultData = getResultData();
    Bundle resultExtras = getResultExtras(true);
    String name = resultExtras.getString("name");

    System.out.println("第二个接收者-结果码:" + resultCode + ",内容:" + resultData + ",携带的数据:" + name);

    resultExtras.putString("name", "我爱大家");

    setResult(400, "我是第二个接收者", resultExtras);
}

最后,看一下测试的结果:
Android基础笔记(九)- 广播_第2张图片

可以清楚的看到,结果按照我们代码写的顺序发生;

结合工作和面试

请描述一下广播?
广播是Android系统的一个事件,他通过广播的机制去分发这个事件,系统已经实现了很多广播接收者。像:低电量,手机重启,接收短信,拨打电话,sd卡挂载,一个apk的安装成功…,系统都会通过广播给这些事件分发出去。这个广播被分发出去之后呢,如果有人想接收到这个广播事件,就需要用这个广播接收者了。
粘性广播?
Sticky:粘性 开发很少用到,但是面试有可能会问到:
sendStickyBroadcast(intent) // 阴魂不散的广播 (粘性的广播)
粘性广播,会一直等待intent指定的事件处理完毕,才会消失。
广播接受者的生命周期都是比较短的,一般接受到广播之后10s左右就会结束,但是有一些广播事件是比较耗时的。比如WIFI状态改变。
Wifi设置:发送wifi状态改变的广播,系统就是通过sendStickyBroadcast来实现的,因为获取wifi状态改变是一个很耗时的操作(获取手机的SSID,并且会获取IP地址等等一系列操作),如果用一般发送广播方式,还没等wifi状态获取完,广播就结束了。

你可能感兴趣的:(android,监听,案例,设置,广播)