Android BroadcastReceiver使用详解

在 Android 系统中,广播(Broadcast)是在组件之间传播数据的一种机制,这些组件能够位于不一样的进程中,起到进程间通讯的做用android

BroadcastReceiver 是对发送出来的 Broadcast 进行过滤、接受和响应的组件。首先将要发送的消息和用于过滤的信息(Action,Category)装入一个 Intent 对象,而后经过调用 Context.sendBroadcast()sendOrderBroadcast() 方法把 Intent 对象以广播形式发送出去。 广播发送出去后,因此已注册的 BroadcastReceiver 会检查注册时的 IntentFilter 是否与发送的 Intent 相匹配,若匹配则会调用 BroadcastReceiver 的 onReceiver() 方法git

因此当咱们定义一个 BroadcastReceiver 的时候,都须要实现 onReceiver() 方法。BroadcastReceiver 的生命周期很短,在执行 onReceiver() 方法时才有效,一旦执行完毕,该Receiver 的生命周期就结束了github

Android中的广播分为两种类型,标准广播和有序广播缓存

  • 标准广播 标准广播是一种彻底异步执行的广播,在广播发出后全部的广播接收器会在同一时间接收到这条广播,之间没有前后顺序,效率比较高,且没法被截断
  • 有序广播 有序广播是一种同步执行的广播,在广播发出后同一时刻只有一个广播接收器可以接收到, 优先级高的广播接收器会优先接收,当优先级高的广播接收器的 onReceiver() 方法运行结束后,广播才会继续传递,且前面的广播接收器能够选择截断广播,这样后面的广播接收器就没法接收到这条广播了

1、静态注册

静态注册即在清单文件中为 BroadcastReceiver 进行注册,使用**< receiver >**标签声明,并在标签内用 < intent-filter > 标签设置过滤器。这种形式的 BroadcastReceiver 的生命周期伴随着整个应用,若是这种方式处理的是系统广播,那么无论应用是否在运行,该广播接收器都能接收到该广播安全

1.一、发送标准广播

首先,继承 BroadcastReceiver 类建立一个用于接收标准广播的Receiver,在 onReceive() 方法中取出 Intent 传递来的字符串bash

public class NormalReceiver extends BroadcastReceiver {

    private static final String TAG = "NormalReceiver";

    public NormalReceiver() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        String msg = intent.getStringExtra("Msg");
        Log.e(TAG, msg);
    }

}
复制代码

在清单文件中声明的 BroadcastReceiver ,必须包含值为 NORMAL_ACTION 字符串的 action 属性,该广播接收器才能收到如下代码中发出的广播网络

发送标准广播调用的是 sendBroadcast(Intent) 方法app

public class MainActivity extends AppCompatActivity {

    private final String NORMAL_ACTION = "com.example.normal.receiver";

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

    public void sendBroadcast(View view) {
        Intent intent = new Intent(NORMAL_ACTION);
        intent.putExtra("Msg", "Hi");
        sendBroadcast(intent);
    }

}
复制代码

在清单文件中注册 BroadcastReceiver异步

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver android:name=".NormalReceiver">
            <intent-filter>
                <action android:name="com.example.normal.receiver" />
            </intent-filter>
        </receiver>
        
    </application>
复制代码

1.二、发送有序广播

首先,继承 BroadcastReceiver 类建立三个用于接收有序广播的Receiver,名字依次命名为 OrderReceiver_一、OrderReceiver_二、OrderReceiver_3。此外,既然 Receiver 在接收广播时存在前后顺序,那么 Receiver 除了能从发送广播使用的 Intent 接收数据外,优先级高的 Receiver 也能在处理完操做后向优先级低的 Receiver 传送处理结果ide

public class OrderReceiver_1 extends BroadcastReceiver {

    private final String TAG = "OrderReceiver_1";

    public OrderReceiver_1() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e(TAG, "OrderReceiver_1被调用了");

        //取出Intent当中传递来的数据
        String msg = intent.getStringExtra("Msg");
        Log.e(TAG, "OrderReceiver_1接收到的值: " + msg);

        //向下一优先级的Receiver传递数据
        Bundle bundle = new Bundle();
        bundle.putString("Data", "(Hello)");
        setResultExtras(bundle);
    }
}
复制代码
public class OrderReceiver_2 extends BroadcastReceiver {

    private final String TAG = "OrderReceiver_2";

    public OrderReceiver_2() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e(TAG, "OrderReceiver_2被调用了");

        //取出上一优先级的Receiver传递来的数据
        String data = getResultExtras(true).getString("Data");
        Log.e(TAG, "从上一优先级的Receiver传递来的数据--" + data);

        //向下一优先级的Receiver传递数据
        Bundle bundle = new Bundle();
        bundle.putString("Data", "(叶应是叶)");
        setResultExtras(bundle);
    }
}
复制代码
public class OrderReceiver_3 extends BroadcastReceiver {

    private final String TAG = "OrderReceiver_3";

    public OrderReceiver_3() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e(TAG, "OrderReceiver_3被调用了");

        //取出上一优先级的Receiver传递来的数据
        String data = getResultExtras(true).getString("Data");
        Log.e(TAG, "从上一优先级的Receiver传递来的数据--" + data);
    }
}
复制代码

在清单文件中对三个 Receiver 进行注册,指定相同的 action 属性值,Receiver 之间的优先级使用 priority 属性来断定,数值越大,优先级越高

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

		
        <receiver android:name=".OrderReceiver_1">
            <intent-filter android:priority="100">
                <action android:name="com.example.order.receiver" />
            </intent-filter>
        </receiver>
		
        <receiver android:name=".OrderReceiver_2">
            <intent-filter android:priority="99">
                <action android:name="com.example.order.receiver" />
            </intent-filter>
        </receiver>
		
        <receiver android:name=".OrderReceiver_3">
            <intent-filter android:priority="98">
                <action android:name="com.example.order.receiver" />
            </intent-filter>
        </receiver>
		
    </application>
复制代码

发送有序广播调用的是 sendOrderedBroadcast(Intent , String) 方法,String 参数值在自定义权限时使用,下边会有介绍

public class MainActivity extends AppCompatActivity {

    private final String NORMAL_ACTION = "com.example.normal.receiver";

    private final String ORDER_ACTION = "com.example.order.receiver";

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

    public void sendBroadcast(View view) {
        Intent intent = new Intent(NORMAL_ACTION);
        intent.putExtra("Msg", "Hi");
        sendBroadcast(intent);
    }

    public void sendOrderBroadcast(View view) {
        Intent intent = new Intent(ORDER_ACTION);
        intent.putExtra("Msg", "Hi");
        sendOrderedBroadcast(intent, null);
    }

}
复制代码

运行结果是

02-20 22:52:30.135 6714-6714/com.example.zy.myapplication E/OrderReceiver_1: OrderReceiver_1被调用了
02-20 22:52:30.135 6714-6714/com.example.zy.myapplication E/OrderReceiver_1: OrderReceiver_1接收到的值: Hi
02-20 22:52:30.143 6714-6714/com.example.zy.myapplication E/OrderReceiver_2: OrderReceiver_2被调用了
02-20 22:52:30.143 6714-6714/com.example.zy.myapplication E/OrderReceiver_2: 从上一优先级的Receiver传递来的数据--(Hello)
02-20 22:52:30.150 6714-6714/com.example.zy.myapplication E/OrderReceiver_3: OrderReceiver_3被调用了
02-20 22:52:30.150 6714-6714/com.example.zy.myapplication E/OrderReceiver_3: 从上一优先级的Receiver传递来的数据--(叶应是叶)
复制代码

能够看出 Receiver 接收广播时不只由于“priority”属性存在前后顺序,且 Receiver 之间也可以传递数据

此外,BroadcastReceiver 也能调用 abortBroadcast() 方法截断广播,这样低优先级的广播接收器就没法接收到广播了

2、动态注册

动态注册 BroadcastReceiver 是在代码中定义并设置好一个 IntentFilter 对象,而后在须要注册的地方调用 Context.registerReceiver() 方法,调用 Context.unregisterReceiver() 方法取消注册,此时就不须要在清单文件中注册 Receiver 了

这里采用在 Service 中注册广播接收器的形式,分别在注册广播接收器取消注册广播接受器接收到广播时输出Log

public class BroadcastService extends Service {

    private BroadcastReceiver receiver;

    private final String TAG = "BroadcastService";

    public BroadcastService() {
    }

    @Override
    public void onCreate() {
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(MainActivity.ACTION);
        receiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                Log.e(TAG, "BroadcastService接收到了广播");
            }
        };
        registerReceiver(receiver, intentFilter);
        Log.e(TAG, "BroadcastService注册了接收器");
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(receiver);
        Log.e(TAG, "BroadcastService取消注册接收器");
    }

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

}
复制代码

提供启动服务,中止服务、发送广播的方法

public class MainActivity extends AppCompatActivity {

    public final static String ACTION = "com.example.receiver";

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

    public void startService(View view) {
        Intent intent = new Intent(this, BroadcastService.class);
        startService(intent);
    }

    public void sendBroadcast(View view) {
        Intent intent = new Intent(ACTION);
        sendBroadcast(intent);
    }

    public void stopService(View view) {
        Intent intent = new Intent(this, BroadcastService.class);
        stopService(intent);
    }

}

复制代码

运行结果以下所示

02-20 23:55:20.967 22784-22784/com.example.zy.myapplication E/BroadcastService: BroadcastService注册了接收器
02-20 23:55:22.811 22784-22784/com.example.zy.myapplication E/BroadcastService: BroadcastService接收到了广播
02-20 23:55:23.179 22784-22784/com.example.zy.myapplication E/BroadcastService: BroadcastService接收到了广播
02-20 23:55:23.461 22784-22784/com.example.zy.myapplication E/BroadcastService: BroadcastService接收到了广播
02-20 23:55:23.694 22784-22784/com.example.zy.myapplication E/BroadcastService: BroadcastService接收到了广播
02-20 23:55:23.960 22784-22784/com.example.zy.myapplication E/BroadcastService: BroadcastService接收到了广播
02-20 23:55:24.282 22784-22784/com.example.zy.myapplication E/BroadcastService: BroadcastService接收到了广播
02-20 23:55:24.529 22784-22784/com.example.zy.myapplication E/BroadcastService: BroadcastService接收到了广播
02-20 23:55:24.916 22784-22784/com.example.zy.myapplication E/BroadcastService: BroadcastService取消注册接收器
复制代码

3、本地广播

以前发送和接收到的广播全都是属于系统全局广播,即发出的广播能够被其余应用接收到,并且也能够接收到其余应用发送出的广播,这样可能会有不安全因素

所以,在某些状况下能够采用本地广播机制,使用这个机制发出的广播只能在应用内部进行传递,并且广播接收器也只能接收本应用内自身发出的广播

本地广播是使用 LocalBroadcastManager 来对广播进行管理

函数 做用
LocalBroadcastManager.getInstance(this).registerReceiver(BroadcastReceiver, IntentFilter) 注册Receiver
LocalBroadcastManager.getInstance(this).unregisterReceiver(BroadcastReceiver); 注销Receiver
LocalBroadcastManager.getInstance(this).sendBroadcast(Intent) 发送异步广播
LocalBroadcastManager.getInstance(this).sendBroadcastSync(Intent) 发送同步广播

首先,建立一个 BroadcastReceiver 用于接收本地广播

public class LocalReceiver extends BroadcastReceiver {

    private final String TAG = "LocalReceiver";

    public LocalReceiver() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e(TAG, "接收到了本地广播");
    }
    
}
复制代码

以后就是使用 LocalBroadcastManager 对 LocalReceiver 进行注册和解除注册了

private LocalBroadcastManager localBroadcastManager;

    private LocalReceiver localReceiver;

    private final String LOCAL_ACTION = "com.example.local.receiver";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        localBroadcastManager = LocalBroadcastManager.getInstance(this);
        localReceiver = new LocalReceiver();
        IntentFilter filter = new IntentFilter(LOCAL_ACTION);
        localBroadcastManager.registerReceiver(localReceiver, filter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(batteryReceiver);
        localBroadcastManager.unregisterReceiver(localReceiver);
    }
	
	public void sendLocalBroadcast(View view) {
        Intent intent = new Intent(LOCAL_ACTION);
        localBroadcastManager.sendBroadcast(intent);
    }

复制代码

须要注意的是,本地广播是没法经过静态注册的方式来接收的,由于静态注册广播主要是为了在程序未启动的状况下也能接收广播,而本地广播是应用本身发送的,此时应用确定是启动的了

4、使用私有权限

使用动态注册广播接收器存在一个问题,即系统内的任何应用都可监听并触发咱们的 Receiver 。一般状况下咱们是不但愿如此的

解决办法之一是在清单文件中为 < receiver > 标签添加一个 android:exported="false" 属性,标明该 Receiver 仅限应用内部使用。这样,系统中的其余应用就没法接触到该 Receiver 了

此外,也能够选择建立本身的使用权限,即在清单文件中添加一个 < permission > 标签来声明自定义权限

<permission
        android:name="com.example.permission.receiver"
        android:protectionLevel="signature" />
复制代码

自定义权限时必须同时指定 protectionLevel 属性值,系统根据该属性值肯定自定义权限的使用方式

属性值 限定方式
normal 默认值。较低风险的权限,对其余应用,系统和用户来讲风险最小。系统在安装应用时会自动批准授予应用该类型的权限,不要求用户明确批准(虽然用户在安装以前老是能够选择查看这些权限)
dangerous 较高风险的权限,请求该类型权限的应用程序会访问用户私有数据或对设备进行控制,从而可能对用户形成负面影响。由于这种类型的许可引入了潜在风险,因此系统可能不会自动将其授予请求的应用。例如,系统能够向用户显示由应用请求的任何危险许可,而且在继续以前须要确认,或者能够采起一些其余方法来避免用户自动容许
signature 只有在请求该权限的应用与声明权限的应用使用相同的证书签名时,系统才会授予权限。若是证书匹配,系统会自动授予权限而不通知用户或要求用户的明确批准
signatureOrSystem 系统仅授予Android系统映像中与声明权限的应用使用相同的证书签名的应用。请避免使用此选项,“signature”级别足以知足大多数需求,“signatureOrSystem”权限用于某些特殊状况

首先,新建一个新的工程,在它的清单文件中建立一个自定义权限,并声明该权限。protectionLevel 属性值设为“signature

<permission
        android:name="com.example.permission.receiver"
        android:protectionLevel="signature" />

    <uses-permission android:name="com.example.permission.receiver" />
复制代码

而后,发送含有该权限声明的 Broadcast 。这样,只有使用相同证书签名且声明该权限的应用才能接收到该 Broadcast 了

private final String PERMISSION_PRIVATE = "com.example.permission.receiver";

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

    public void sendPermissionBroadcast(View view) {
        sendBroadcast(new Intent("Hi"), PERMISSION_PRIVATE);
    }
复制代码

回到以前的工程 首先在清单文件中声明权限

<uses-permission android:name="com.example.permission.receiver" />
复制代码

建立一个 BroadcastReceiver

public class PermissionReceiver extends BroadcastReceiver {

    private final String TAG = "PermissionReceiver";

    public PermissionReceiver() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e(TAG, "接收到了私有权限广播");
    }
}
复制代码

而后注册广播接收器。由于 Android Studio 在调试的时候会使用相同的证书为每一个应用签名,因此,在以前新安装的App发送出广播后,PermissionReceiver 就会输出 Log 日志

private final String PERMISSION_PRIVATE = "com.example.permission.receiver";

    private PermissionReceiver permissionReceiver;

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

        IntentFilter intentFilter1 = new IntentFilter("Hi");
        permissionReceiver = new PermissionReceiver();
        registerReceiver(permissionReceiver, intentFilter1, PERMISSION_PRIVATE, null);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(permissionReceiver);
    }
复制代码

5、实战演练

5.一、监听网络状态变化

首先须要一个用来监测当前网络状态的工具类

/**
 * Created by 叶应是叶 on 2017/2/21.
 */

public class NetworkUtils {

    /**
     * 标记当前网络状态,分别是:移动数据、Wifi、未链接、网络状态已公布
     */
    public enum State {
        MOBILE, WIFI, UN_CONNECTED, PUBLISHED
    }

    /**
     * 为了不因屡次接收到广播反复提醒的状况而设置的标志位,用于缓存收到新的广播前的网络状态
     */
    private static State tempState;

    /**
     * 获取当前网络链接状态
     *
     * @param context Context
     * @return 网络状态
     */
    public static State getConnectState(Context context) {
        ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networkInfo = manager.getActiveNetworkInfo();
        State state = State.UN_CONNECTED;
        if (networkInfo != null && networkInfo.isAvailable()) {
            if (isMobileConnected(context)) {
                state = State.MOBILE;
            } else if (isWifiConnected(context)) {
                state = State.WIFI;
            }
        }
        if (state.equals(tempState)) {
            return State.PUBLISHED;
        }
        tempState = state;
        return state;
    }

    private static boolean isMobileConnected(Context context) {
        return isConnected(context, ConnectivityManager.TYPE_MOBILE);
    }

    private static boolean isWifiConnected(Context context) {
        return isConnected(context, ConnectivityManager.TYPE_WIFI);
    }

    private static boolean isConnected(Context context, int type) {
        //getAllNetworkInfo() 在 API 23 中被弃用
        //getAllNetworks() 在 API 21 中才添加
        ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            NetworkInfo[] allNetworkInfo = manager.getAllNetworkInfo();
            for (NetworkInfo info : allNetworkInfo) {
                if (info.getType() == type) {
                    return info.isAvailable();
                }
            }
        } else {
            Network[] networks = manager.getAllNetworks();
            for (Network network : networks) {
                NetworkInfo networkInfo = manager.getNetworkInfo(network);
                if (networkInfo.getType() == type) {
                    return networkInfo.isAvailable();
                }
            }
        }
        return false;
    }

}
复制代码

而后声明一个 BroadcastReceiver ,在onReceive() 方法中用Log输出当前网络状态

public class NetworkReceiver extends BroadcastReceiver {

    private static final String TAG = "NetworkReceiver";

    public NetworkReceiver() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        switch (NetworkUtils.getConnectState(context)) {
            case MOBILE:
                Log.e(TAG, "当前链接了移动数据");
                break;
            case WIFI:
                Log.e(TAG, "当前链接了Wifi");
                break;
            case UN_CONNECTED:
                Log.e(TAG, "当前没有网络链接");
                break;
        }
    }

}
复制代码

在清单文件中注册广播接收器,“android.net.conn.CONNECTIVITY_CHANGE”是系统预约义好的 action 值,只要系统网络状态发生变化,NetworkReceiver 就能收到广播

<receiver android:name=".NetworkReceiver">
            <intent-filter>
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
            </intent-filter>
        </receiver>
复制代码

此外,还要申请查看网络状态的权限

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
复制代码

5.二、监听电量变化

由于系统规定监听电量变化的广播接收器不能静态注册,因此这里只能使用动态注册的方式了

private final String TAG = "MainActivity";

    private BroadcastReceiver batteryReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
        batteryReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                // 当前电量
                int currentBattery = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
                // 总电量
                int totalBattery = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);         
                Log.e(TAG, "当前电量:" + currentBattery + "-总电量:" + totalBattery);
            }
        };
        registerReceiver(batteryReceiver, intentFilter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(batteryReceiver);
    }
复制代码

onReceive(Context , Intent ) 中的 Intent 值包含了一些额外信息,能够取出当前电量和总电量

为了方便查看电量变化,能够在模拟器的“extended controls”面板中主动地改变模拟器的电量,查看Log输出

这里写图片描述

5.三、应用安装更新卸载监听

首先,建立 BroadcastReceiver

public class AppReceiver extends BroadcastReceiver {

    private final String TAG = "AppReceiver";

    public AppReceiver() {
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        //判断广播类型
        String action = intent.getAction();
        //获取包名
        Uri appName = intent.getData();
        if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
            Log.e(TAG, "安装了:" + appName);
        } else if (Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
            Log.e(TAG, "更新了:" + appName);
        } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
            Log.e(TAG, "卸载了:" + appName);
        }
    }
}

复制代码

注册广播接收器

<receiver android:name=".train.AppReceiver">
            <intent-filter>
                <!--安装应用-->
                <action android:name="android.intent.action.PACKAGE_ADDED" />
                <!--更新应用-->
                <action android:name="android.intent.action.PACKAGE_REPLACED" />
                <!--卸载应用-->
                <action android:name="android.intent.action.PACKAGE_REMOVED" />
                <!--携带包名-->
                <data android:scheme="package" />
            </intent-filter>
        </receiver>
复制代码

这里提供本文章全部示例代码下载:Android BroadcastReceiver使用详解

相关文章
相关标签/搜索