Android基础之四大组件---BroadcastReceiver

学习Android一段时间,来总结下BroadcastReceiver的使用方法。

一、什么是广播,广播接受者

  • 广播:广泛运用在应用程序之间传输信息的机制。
  • 广播接受者(BroadcastReceiver):接收来自系统和应用中的广播。

Android系统中,广播体现在很多方面,例如开机启动后会发出一条广播,当接收到这条广播就能实现开机启动服务的功能。当网络状态发生变化时产生一条广播,接收到后能及时做出提示和保存数据等操作。当电池电量改变时,系统会产生一条广播,接收到这条广播就能在电量低时告知用户及时保存进度,等等。

二、广播注册的两种方式

  • 1.静态注册
    静态注册方式是在AndroidManifest.xml中配置。如下所示
<receiver android:name=".MyBroadcastReceiver">
            <intent-filter>
                <action android:name="com.hlh.base"/>
                <category android:name="android.intent.category.DEFAULT"/>
            intent-filter>
receiver>

如果当注册一些如短信的广播时,需要配置权限

<receiver>
    < intent-filter android:priority= "1000">     
        < action android:name= " android.provider.Telephony.SMS_RECEIVED"/>
    </ intent-filter >
</receiver>

//加上权限
< uses-permission android:name= "android.permission.RECEIVE_SMS"/>
< uses-permission android:name= "android.permission.SEND_SMS"/>
  • 2.动态注册
    动态注册需要在代码中动态的指定广播地址并注册,通常我们是在Activity或Service注册一个广播,下面我们就来看一下注册的代码:
MyBroadcastReceiver receiver = new MyBroadcastReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("com.hlh.base");
registerReceiver(receiver, filter);

但是代码中注册也分两种情况
注册方法一:registerReceiver(BroadcastReceiver receiver, IntentFilter fileter);第一个参数是我们要处理广播的BroadcastReceiver;第二个参数是意图过滤器。

注册方式二:registerReceiver(receiver, filter, broadcastPermission, scheduler)第一个参数是 BroadcastReceiver (广播接收者,可以是系统的,也可以是自定义的);第二个参数是意图过滤器;第三个参数是广播权限;第四个参数是 Hander ;

note:权限重复问题,如果AndroidManifest.xml注册了该权限,用该方法再注册,则receiver无法接收到广播;如果AndroidManifest.xml没有注册该权限,该方法注册也无法收到广播;当该方法没有注册权限,AndroidManifest.xml注册的时候,receiver可以接收到广播。

三、Android中使用广播的一般步骤

  • 1.定义广播接受者
public class MyBroadcastReceiver extends BroadcastReceiver{
    public static final String TAG= "broadcastreceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "接收到广播" + intent.getStringExtra("msg"));
    }
}
  • 2.注册广播
MyBroadcastReceiver receiver = new MyBroadcastReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("com.hlh.base");
registerReceiver(receiver, filter);
  • 3.发送广播
Intent intent = new Intent("com.hlh.base");
intent.putExtra("msg", "love Android");
sendBroadcast(intent);
  • 4.接收广播、处理操作

这里写图片描述

四、广播类型

  • 1、普通广播(Normal Broadcast) :接受者不能终止广播。普通广播对于多个接收者来说是完全异步的,通常每个接收者都无需等待即可以接收到广播,接收者相互之间不会有影响。对于这种广播,接收者无法终止广播,即无法阻止其他接收者的接收动作。
    为了验证以上论断,我们新建三个BroadcastReceiver,演示一下这个过程,FirstReceiver、SecondReceiver和ThirdReceiver的代码如下:
public class FirstReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(MyConfig.LOG, "FirstReceiver  " + intent.getStringExtra("msg"));
    }
}
public class SecondReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(MyConfig.LOG, "SecondReceiver " + intent.getStringExtra("msg"));
    }
}
public class ThirdReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(MyConfig.LOG, "ThirdReceiver " + intent.getStringExtra("msg"));
    }
}

注册完广播后,发送广播,控制台打印如下:
这里写图片描述

然后在三个广播接受者的onReceive()中加入abortBroadcast()方法终止广播。再次点击发送按钮,我们会发现,控制台中三个接收者仍然都打印了自己的日志,因此接收者并不能终止广播。

  • 2、有序广播(Ordered Broadcast)
    有序广播按照接受者的优先级顺序接收广播,优先级别在intent-filter中的priority中声明。-1000到1000之间,值越大,优先级越高。它每次只发送到优先级较高的接收者那里,然后由优先级高的接受者再传播到优先级低的接收者那里,优先级高的接收者有能力终止这个广播。
    演示有序广播流程,修改上面三个接收者的代码,如下所示。
    FirstReceiver.java
public class FirstReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(MyConfig.LOG, "FirstReceiver  " + intent.getStringExtra("msg"));

        Bundle bundle = new Bundle();
        bundle.putString("msg", intent.getStringExtra("msg") + " @FirstReceiver");
        setResultExtras(bundle);
    }
}

SecondReceiver.java

public class SecondReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(MyConfig.LOG, "SecondReceiver " + getResultExtras(true).getString("msg"));

        Bundle bundle = new Bundle();
        bundle.putString("msg", getResultExtras(true).getString("msg") + " @SecondReceiver");
        setResultExtras(bundle);
    }
}

ThirdReceiver.java

public class ThirdReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(MyConfig.LOG, "ThirdReceiver " + getResultExtras(true).getString("msg"));
    }
}

在FirstReceiver和SecondReceiver中最后都使用了setResultExtras方法将一个Bundle对象设置为结果集对象,传递到下一个接收者那里,这样以来,优先级低的接收者可以用getResultExtras获取到最新的经过处理的信息集合。
代码改完之后,我们需要为三个接收者注册广播地址,我们修改一下AndroidMainfest.xml文件:

<receiver android:name=".FirstReceiver">
            <intent-filter android:priority="1000">
                <action android:name="com.hlh.base"/>
                <category android:name="android.intent.category.DEFAULT"/>
            intent-filter>
        receiver>
        <receiver android:name=".SecondReceiver">
            <intent-filter android:priority="500">
                <action android:name="com.hlh.base"/>
                <category android:name="android.intent.category.DEFAULT"/>
            intent-filter>
        receiver>
        <receiver android:name=".ThirdReceiver">
            <intent-filter android:priority="0">
                <action android:name="com.hlh.base"/>
                <category android:name="android.intent.category.DEFAULT"/>
            intent-filter>
        receiver>

最后,修改发送广播的代码:

Intent intent = new Intent("com.hlh.base");
intent.putExtra("msg", "love Android");
sendOrderedBroadcast(intent, "com.hlh.base.MY_BROADCASTRECEIVER_PERMISSION");

注意,使用sendOrderedBroadcast方法发送有序广播时,需要一个权限参数,如果为null则表示不要求接收者声明指定的权限,如果不为null,则表示接收者若要接收此广播,需声明指定权限。这样做是从安全角度考虑的,例如系统的短信就是有序广播的形式,一个应用可能是具有拦截垃圾短信的功能,当短信到来时它可以先接受到短信广播,必要时终止广播传递,这样的软件就必须声明接收短信的权限。
所以我们在AndroidMainfest.xml中定义一个权限:

<permission
        android:name="com.hlh.base.MY_BROADCASTRECEIVER_PERMISSION"
        android:protectionLevel="normal"/>

然后声明使用了此权限:

<uses-permission android:name="com.hlh.base.MY_BROADCASTRECEIVER_PERMISSION"/>

然后我们点击发送按钮发送一条广播,控制台打印如下:
这里写图片描述
我们看到接收是按照顺序的,第一个和第二个都在结果集中加入了自己的标记,并且向优先级低的接收者传递下去。
既然是顺序传递,试着终止这种传递,看一看效果如何,我们修改FirstReceiver的代码,在onReceive的最后一行添加以下代码:abortBroadcast();
再次运行控制台只打印了
这里写图片描述
此次,只有第一个接收者执行了,其它两个都没能执行,因为广播被第一个接收者终止了。

五、实例

  • 1.开机启动服务

我们经常有这样的应用场合,比如消息推送服务,需要开机启动就能实现。实现该功能,必须订阅开机启动完成这条广播,接收到这条广播后,即可启动我们的消息推送服务。接收开机完成的广播接收者BootCompleteReceiver.java和消息推送服务MsgPushService.java如下所示

public class BootCompleteReceiver extends BroadcastReceiver{
    private static final String MSG = "BootCompleteReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        //启动消息推送服务
        Intent service = new Intent(context, MsgPushService.class);
        context.startService(service);
        Log.d(MSG, "Boot Complete , Starting MsgPushService...");
    }
}
public class MsgPushService extends Service{
    private static final String TAG = "MsgPushService";

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate complete...");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand complete... ");
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

接下来在AnroidManifest.xml配置相关信息

    <receiver android:name=".BootCompleteReceiver">
            <intent-filter>
                
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
                <category android:name="android.intent.category.DEFAULT"/>
            intent-filter>
    receiver>
    
    <service android:name=".MsgPushService"/>

我们看到BootCompleteReceiver注册了“android.intent.action.BOOT_COMPLETED”这个开机广播地址,从安全角度考虑,系统要求必须声明接收开机启动广播的权限,于是我们再声明使用下面的权限:

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

经过上面的几个步骤之后,我们就完成了开机启动的功能,将应用运行在模拟器上,然后重启模拟器,控制台打印如下:
这里写图片描述

这里写图片描述

  • 2.网络变化服务
    在某些场合,比如用户浏览网络信息时,网络突然断开,我们要及时地提醒用户网络已断开。要实现这个功能,我们可以接收网络状态改变这样一条广播,当由连接状态变为断开状态时,系统就会发送一条广播,我们接收到之后,再通过网络的状态做出相应的操作。下面就来实现一下这个功能:
public class NetworkStateReceiver extends BroadcastReceiver{
    private static final String TAG = "NetworkStateReceiver";
    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "network state changed");
        if (!isNetworkAvailable(context)) {
            Toast.makeText(context, "network disconnected error", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * 网络是否可用
     */
    public static boolean isNetworkAvailable(Context context) {
        ConnectivityManager ctm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo[] info = ctm.getAllNetworkInfo();
        if (info != null) {
            for (int i = 0; i < info.length; i++) {
                if(info[i].getState() == NetworkInfo.State.CONNECTED)
                    return true;
            }
        }
        return false;
    }
}

注册接收者的信息:

 <receiver android:name=".NetworkStateReceiver">
            <intent-filter>
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
            intent-filter>
 receiver>

因为在isNetworkAvailable方法中我们使用到了网络状态相关的API,所以需要声明相关的权限才行,下面就是对应的权限声明:

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

六、注意的问题

  • BroadcastReceiver的哦你Receive()生命周期只有十秒左右,如果在 onReceive() 内做超过十秒内的事情,就会报错。
    每次广播到来时 , 会重新创建 BroadcastReceiver 对象 , 并且调用 onReceive() 方法 , 执行完以后 , 该对象即被销毁 . 当 onReceive() 方法在 10 秒内没有执行完毕, Android 会认为该程序无响应 . 所以在
    BroadcastReceiver 里不能做一些比较耗时的操作 , 否侧会弹出 ANR(Application No Response) 的对话框。
    如果需要完成一项比较耗时的工作 , 应该通过发送 Intent 给 Service, 由 Service 来完成 .

你可能感兴趣的:(【Android】基础总结)