Android四大组件之4 - BroadcastReceiver

1. 什么是广播

Android 系统自己在很多时候都会发送广播,比如电量低或者充足,刚启动完,插入耳机,输入法改变等, 发生这些时间,系统都会发送广播,这个叫系统广播

每个 APP 都会收到,如果想让一个应用在接收到广播的时候做一些操作,比如:系统开机后,偷偷后台跑服务~哈哈,这个时候只需要为应用注册一个用于监视开机的 BroadcastReceiver ,当接收到开机广播就做写偷偷摸摸的勾当

应用场景:

  • 同一应用具有多个进程的不同组件之间的消息通信
  • 不同应用间的组件之间的消息通信
  • 与Android系统在特定情况下的通信
    • 如:系统开机,网络变化等

实现原理:

  • Android中的广播使用了设计模式中的观察者模式:基于消息的发布/订阅事件模型。因此,Android将广播的发送者和接收者解耦,使得系统方便集成,更易扩展。

 

模型中有3个角色

  • 消息订阅者(广播接收者)
  • 消息发布者(广播发布者)
  • 消息中心(AMS,即Activity Manager Service)

     

原理描述:

  • 广播接收者通过Binder机制在AMS注册
  • 广播发送者通过Binder机制向AMS发送广播
  • AMS根据广播发送者要求,在已注册列表中,寻找合适的广播接收者
    寻找依据:IntentFilter/Permission
  • AMS将广播发送到合适的广播接收者响应的消息循环队列中;
  • 广播接收者通过消息循环拿到此广播,并回调onReceiver()
    特别注意:广播发送者和广播接收者的执行时异步的,发出去的广播不会关心有无接收者,也不确定接收者到底是何时才能接收到;

 

2. 四种广播类型

  1. 标准广播

    完全异步执行的广播,发出广播后,所有的广播接收器几乎会在同一时刻收到这条广播通知

  2. 有序广播

    同步执行的一种广播,发出广播后,同一时间只有一个广播接收者能收到,当这个广播接收者的逻辑执行完后,才会传递到下一个广播接收者

    前一个广播接收者还可以截断广播,让广播不会继续传递

  3. 系统广播

Android系统中内置了多个系统广播,只要涉及到手机的基本操作,基本上都会发出相应的系统广播。如:开启启动,网络状态改变,拍照,屏幕关闭与开启,点亮不足等等。每个系统广播都具有特定的intent-filter,其中主要包括具体的action,系统广播发出后,将被相应的BroadcastReceiver接收。系统广播在系统内部当特定事件发生时,有系统自动发出。

  1. 应用内广播

Android中的广播可以跨进程甚至跨App直接通信,且注册是exported对于有intent-filter的情况下默认值是true,由此将可能出现安全隐患如下:

  1. 其他App可能会针对性的发出与当前App intent-filter相匹配的广播,由此导致当前App不断接收到广播并处理;

  2. 其他App可以注册与当前App一致的intent-filter用于接收广播,获取广播具体信息。

无论哪种情形,这些安全隐患都确实是存在的。由此,最常见的增加安全性的方案是:

  1. 对于同一App内部发送和接收广播,将exported属性人为设置成false,使得非本App内部发出的此广播不被接收;

  2. 在广播发送和接收时,都增加上相应的permission,用于权限验证;

  3. 发送广播时,指定特定广播接收器所在的包名,具体是通过intent.setPackage(packageName)指定在,这样此广播将只会发送到此包中的App内与之相匹配的有效广播接收器中。

App应用内广播可以理解成一种局部广播的形式,广播的发送者和接收者都同属于一个App。实际的业务需求中,App应用内广播确实可能需要用到。同时,之所以使用应用内广播时,而不是使用全局广播的形式,更多的考虑到的是Android广播机制中的安全性问题。

相比于全局广播,App应用内广播优势体现在:

  1. 安全性更高;

  2. 更加高效。

为此,Android v4兼容包中给出了封装好的LocalBroadcastManager类,用于统一处理App应用内的广播问题,使用方式上与通常的全局广播几乎相同,只是注册/取消注册广播接收器和发送广播时将主调context变成了LocalBroadcastManager的单一实例。

3. 接受广播的方式

接收广播之前,先要为我们的 APP 注册广播接收器

Android 提供了两种注册广播的方式 动态 与 静态

动态注册:

就是在 Java 代码中指定 Intent-filter,然后添加不同的 Action

想监听什么广播就写什么 Action

动态注册需要应用程序启动后才能接收广播信息

注意: 动态注册的广播一定要调用 unregisterReceive() 取消广播注册 

例子:  网络状态广播 ,一开始时没有联网,打开网络时,Toast 通知

 

1. MsBroadcastReceiver.java, onReceive() 方法中完成广播要处理的事务

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class MsBroadcastReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context,"网络状态发生改变~",Toast.LENGTH_SHORT).show();
    }
}

2. MainActivity.java 动态注册广播

import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    MsBroadcastReceiver mReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mReceiver = new MsBroadcastReceiver();

        IntentFilter itFilter = new IntentFilter();

        // 设置接受关闭的类型
        itFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
        registerReceiver(mReceiver, itFilter); // 动态注册
    }

    //别忘了将广播取消掉

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(mReceiver); // 销毁广播
    }
}

假如我们需要程序没有启动也能接收广播的话,那么就需要注册静态广播 

2. 静态注册

在 AndroidManifest.xml 设置 <receiver> 就可以让 APP 在未启动的情况下接收到广播

1.MsBootCompleteReceiver,重写 onReceive 完成事务处理:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class MsBootCompleteReceiver extends BroadcastReceiver {

    private final String ACTION_BOOT = "android.intent.action.BOOT_COMPLETED";

    @Override
    public void onReceive(Context context, Intent intent) {
    if (ACTION_BOOT.equals(intent.getAction()))
        Toast.makeText(context, "开机完毕~", Toast.LENGTH_LONG).show();
    }
}

2. 添加开机广播的 intent-filter 和注册权限

<receiver android:name=".MsBootCompleteReceiver">
    <intent-filter>
        <action android:name = "android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

注册权限

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
  1. 然后重启下手机会发现过了一会儿,就会弹出开机完毕这个 Toast 的了

    Android 4.3 以上的版本,是允许将程序安装到 SD 卡上的,假如你的程序是安装在 SD 上的,就会收不到开机广播

注意事项

广播更多的时候扮演的是一个打开其它组件的角色,比如启动 Service, Notification 提示 , Activity 等

不要在广播里添加过多逻辑或者进行任何耗时操作

因为在广播中是不允许开辟线程的,当 onReceiver() 方法运行较长时间 ( 超过 10 秒 ) 还没有结束的话,那么程序会报错

4. 发送广播

一个发送广播的流程:

自定义一个 BroadcastReceiver ,重写 onReceive() 方法,然后注册下广播:

  1. 发送标准广播 sendBroadcast(intent);
  2. 发送有序广播 `sendOrderedBroadcast(intent,null)

    可以在 AndroidManifest.xml 中的 <Intent-filter> 中通过 android:priority="100" 设置优先级,值越大优先级越高,越先收到广播,优先级可选范围 -1000 - 1000

  3. 可以调用 abortBroadcast() 截断广播,让其不再继续传递

一个自己向自己表白的例子:

a. 新建一个广播接收者 MsBroadcastReceiver.java

package cn.twle.android.sendbroadcast;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class MsBroadcastReceiver extends BroadcastReceiver {

    private final String ACTION_BOOT = "cn.twle.android.sendbroadcast.MS_BROADCAST";

    @Override
    public void onReceive(Context context, Intent intent) {
        if( ACTION_BOOT.equals(intent.getAction()))
            Toast.makeText(context, "收到告白啦~",Toast.LENGTH_LONG).show();
    }
}

b.修改 AndroidManifest.xml 注册 MsBroadcastReceiver ,写上 Intent-filter

<receiver android:name=".MsBroadcastReceiver">
    <intent-filter>
        <action android:name="cn.twle.android.sendbroadcast.MS_BROADCAST"/>
    </intent-filter>
</receiver>

c.添加一个按钮 

<?xml version="1.0" encoding="utf-8" ?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal" 
    android:orientation="vertical" >

    <Button 
        android:text="发送告白"
        android:id="@+id/btn_send"
        android:layout_width="wrap_content" 
        android:layout_height="wrap_content" />

</LinearLayout>

d. 使用 sendBroadcast 发送广播

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button btn_send = (Button) findViewById(R.id.btn_send);
        btn_send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                sendBroadcast(new Intent("cn.twle.android.sendbroadcast.MS_BROADCAST"));
            }
        });
    }
}

5. 本地广播

前面说的广播都是 全局广播,意味着我们 APP 发出的广播,其它 APP 都会接收到, 或者其它 APP 发送的广播,我们的 APP 也会接收到,这样容易引起一些安全性的问题

Android 提供了 本地广播 的机制,使用该机制发出的广播只会在 APP 内部传播,而且 广播接收者也只能收到本应用发出的广播

例子链接

使用流程

使用 LocalBroadcastManager 来管理广播

  1. 调用 LocalBroadcastManager.getInstance() 获得实例 mLBM
  2. 调用 mLBM.registerReceiver() 注册广播
  3. 调用 mLBM.sendBroadcase() 发送广播
  4. 调用 mLBM.unregisterReceiver() 取消注册广播

 

6. 系统广播

系统广播常量说明
Intent.ACTION_AIRPLANE_MODE_CHANGED关闭或打开飞行模式时的广播
Intent.ACTION_HEADSET_PLUG在耳机口上插入耳机时发出的广播
Intent.ACTION_POWER_CONNECTED插上外部电源时发出的广播
Intent.ACTION_POWER_DISCONNECTED已断开外部电源连接时发出的广播
Intent.ACTION_REBOOT重启设备时的广播
Intent.ACTION_SCREEN_OFF屏幕被关闭之后的广播
Intent.ACTION_SCREEN_ON屏幕被打开之后的广播
Intent.ACTION_UMS_DISCONNECTED设备已从USB大容量储存状态转为正常状态时发出的广播

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值