Android基础笔记(九)- 广播

启程!!

广播的概念

在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()方法中的解析短信是固定写法,不须要记住,用的时候查一下即可了。

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

也是个非常easy的案例,依照上边的步骤创建并声明、加入过滤器就能够了 ,这里主要演示一下几个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

发送自己定义广播

发送自定的广播也非常easy,依据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);
}

在还有一个project中,我们创建广播接收者并设置意图过滤器(指定关心的动作)。就能够得到数据了;

以下是清单文件里广播接收者的配置。

<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()方法终止广播。

① 首先,创建发送有序广播project,并发送一个有序广播。有序广播有非常多參数,在代码中已经给出了凝视。

当中,我们给广播设置的动作是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);
}

② 然后。创建有序广播接收者project,并创建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系统的一个事件,他通过广播的机制去分发这个事件,系统已经实现了非常多广播接收者。

像:低电量,手机重新启动,接收短信,拨打电话,sd卡挂载,一个apk的成功安装…,系统都会通过广播给这些事件分发出去。

这个广播被分发出去之后呢,假设有人想接收到这个广播事件,就须要用这个广播接收者了。

粘性广播?
Sticky:粘性 开发非常少用到。可是面试有可能会问到:
sendStickyBroadcast(intent) // 阴魂不散的广播 (粘性的广播)
粘性广播,会一直等待intent指定的事件处理完毕,才会消失。
广播接受者的生命周期都是比較短的。一般接受到广播之后10s左右就会结束,可是有一些广播事件是比較耗时的。比方WIFI状态改变。
Wifi设置:发送wifi状态改变的广播,系统就是通过sendStickyBroadcast来实现的,由于获取wifi状态改变是一个非常耗时的操作(获取手机的SSID,而且会获取IP地址等等一系列操作),假设用一般发送广播方式,还没等wifi状态获取完。广播就结束了。
原文地址:https://www.cnblogs.com/lcchuguo/p/5392690.html