安卓中广播接收器有两种,动态注册,静态注册。
从当前的情况来看,安卓安全管理方面做的越来越严禁,越来越倾向于使用动态监听来处理系统信息,然后在退出应用或界面时移除监听。
安卓中常用的广播监听类别不是很多,主要包括:电量情况、Home键位监听、本地环境切换(中英文环境切换)、网络、短信内容读取、屏幕打开或关闭。
一般需要在onCreate或者其他情况下开启监听,然后在onStop或者onDestroy时移除,在适应需求的同时合乎广播使用的规范。
这里将经常使用的几种动态监听器进行封装,同时对新的广播监听实现开放接口,简单说明该库的实现。
一、注册后动态移除
广播使用基本都是在活动(Activity)内,移除的时间点一般就两个:OnStop、onDestroy,所以在BaseActivity类中,针对这两个时间点添加回调处理:
public abstract class BaseActivity extends Activity implements ListenerInActivity {
/**
* 注册监听器,当销毁或者是暂停的时候,用于执行回调
*/
private HttpCallback<Object> callbackOnDestroy, callbackOnStop;
/**
* 当activity销毁时候,关闭资源
*/
@Override
public void onDestroy() {
super.onDestroy();
if (callbackOnDestroy != null) {
callbackOnDestroy.run(null);
}
}
@Override
protected void onStop() {
super.onStop();
if (callbackOnStop != null) {
callbackOnStop.run(null);
}
}
/**
* 当activity被关掉时,添加监听
*/
@Override
public void listenerOnDestroy(HttpCallback<Object> callback) {
callbackOnDestroy = callback;
}
/**
* 当activity不可见时,添加监听
*/
@Override
public void listenerOnStop(HttpCallback<Object> callback) {
callbackOnStop = callback;
}
}
HttpCallback为一个接口,用于回调方法的执行,然后在注册监听器的同时,指定移除的时间,以短信监听为例:
/**
* 短信监听器
* <p>
* 需要短信读取和接收权限
*
* @param activity activity
* @param regs 注册机
* @param time 监听器被移除的周期
* @param callback 拦截成功的回调,first参数表示联系人,second参数表示验证码
*/
@RequiresPermission(allOf = {Manifest.permission.SEND_SMS, Manifest.permission.RECEIVE_SMS, Manifest.permission.READ_SMS, Manifest.permission.RECEIVE_WAP_PUSH, Manifest.permission.RECEIVE_MMS})
public static BroadcastReceiver registerSMSReceiver(Activity activity, ListenerInActivity regs, RemoveTime time, HttpCallback<Pair<String, String>> callback) {
SMSReceiver smsReceiver = new SMSReceiver(callback);
return commonRemove(activity, regs, time, smsReceiver.getIntentFilter(), smsReceiver);
}
SMSReceiver为以内置的广播监听器,commonRemove方法实现了动态移除功能:
/**
* 移除监听
*/
private static BroadcastReceiver commonRemove(Activity activity, ListenerInActivity regs, RemoveTime time, IntentFilter filter, BroadcastReceiver receiver) {
activity.registerReceiver(receiver, filter);
Log.v(BCRMachine.class.getName(), "已注册监听器..." + receiver.getClass().getName());
if (time == onStop) {
regs.listenerOnStop(tag -> {
activity.unregisterReceiver(receiver);
Log.v(BCRMachine.class.getName(), "已移除监听器..."+ receiver.getClass().getName());
});
} else if (time == onDestroy) {
regs.listenerOnDestroy(tag -> {
activity.unregisterReceiver(receiver);
Log.v(BCRMachine.class.getName(), "已移除监听器..."+ receiver.getClass().getName());
});
}
return receiver;
}
这样,在活动onStop或者onDestroy时,监听器就可以动态移除掉,也因为这样的实现,该库暂时不支持在同一个activity中监听多个广播,因为那样会导致回调被覆盖。
二、正常使用方法
如果只是使用前面提到的几种基本的广播监听,可以直接使用提供注册机的内置方法(BCRMachine类 ):
- registerSMSReceiver:短信监听
- registerBatteryChange:电量监听(低电量,电量正常,电池温度等状态)
- registerLocale:本地环境切换(中英文环境改变等)
- registerNetStatus:网络状态变化
- registerScreen:屏幕开启或者关闭
- registerHome:Home键位监听处理
在调用时,只需要按照参数说明,指定activity,监听器,监听器移除的时间,回调类等,就可以实现广播的自动添加或者移除功能。
三、定义自己的广播监听
如果提供的监听器无法满足需求,则可以自定义广播监听器;
假使自定义了一个广播,在登录成功或者离线后,需要通知其他activity调用相应的逻辑,则可以模仿内置的监听器这样来处理:
public class MyReceiver extends BaseReceiver<MyBean> {
/**
* 指定要拦截的广播
*/
public MyReceiver(@NonNull HttpCallback<MyBean> httpCallback) {
super(httpCallback,"要拦截的广播1","要拦截的广播2", "...");
}
/**
* 从Intent中拿到需要的处理结果
*/
@Override
protected MyBean apply(Intent intent) {
// TODO 返回MyBean类型的对象,与父类指定的该泛型必须相同
}
}
在构造方法中来指定需要监听广播事件,在apply方法中处理广播接收到的Intent,然后解析获取需要的结果,如果发现该Intent不符合自己的要求,那么直接抛出异常即可。
自定义了广播接收器后,再调用注册机BCRMachine 的静态方法registerBroadcast:
该方法逻辑如下所示:
/**
* 注册监听器,通过公用方法,利用反射来生成对象
* <p>
* 反射只调用一次,基本不会影响性能
* <p>
* 规定添加的监听器构造方法需要传入 callback,且构造方法访问权限为public
* <p>
* 泛型 F 为回调函数中需要传入的类型
* <p>
* 泛型 T 为新添加的广播接收者的类型
*
* @return 注册得到的广播监听器
* <p>
* 该方法只向外部提供,用于动态的添加监听器;
* 本模块中方法不会主动去调用
*/
public static <F, T extends BaseReceiver> BroadcastReceiver registerBroadcast(Activity activity, ListenerInActivity regs, RemoveTime time, HttpCallback<F> cb, Class<T> clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, UnsupportedOperationException {
Constructor<T> constructor = clazz.getConstructor(HttpCallback.class);
T receiver = constructor.newInstance(cb);
//获取回调接口中定义的泛型类型
Type[] genericInterfaces = cb.getClass().getGenericInterfaces();
Type type_callback = null;
for (Type genericInterface : genericInterfaces) {
if (genericInterface.toString().contains(HttpCallback.class.getName())) {
type_callback = genericInterface;
break;
}
}
//在java1.8后,使用lambda会导致泛型不可见,因此,如果发现泛型被移除,则不再进行判断
if (type_callback instanceof ParameterizedType) {
Type type_receiver = ((ParameterizedType) receiver.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
type_callback = ((ParameterizedType) type_callback).getActualTypeArguments()[0];
//如果目标需求的参数与返回的参数不同,则默认方法调用失败
if (!type_callback.equals(type_receiver)) {
throw new UnsupportedOperationException("receiver接收器参数类型与需要回调的类型不符");
}
}
return commonRemove(activity, regs, time, receiver.getIntentFilter(), receiver);
}
系统默认会检查BaseReceiver的子类中指定的泛型与apply中指定的泛型是否相同,如果不同,则会直接抛出异常,因此这要求自定义BroadcastReceiver时需要满足以下两个条件:
- 必须继承自BaseReceiver,且严格按照指定的格式,在apply方法中处理结果,并提供默认的单参(HttpCallback类型)造函数,因为内部使用的是反射机制,否则,在创建实例时会直接出错。
- 不支持多重继承,即不能编写类继承自类似SMSReceiver这样的监听器类,这会导致泛型检查出错,当然如果真有这样需求,那么也可以,只要在定义回调类时,使用lambal表达式,就可以避过泛型检查。
同样重要的,在调用registerBroadcast方法时,要注意处理抛出的异常,这样才能保证系统的健壮,毕竟使用反射的话,如果类编写不规范就会直接出错。
因为个人原因,监听库中肯定会有许多情况未考虑到,也会有很多到广播未添加进入,如果有更多的需求可以直接 ISSURES
具体的使用方法详见GITHUB:动态广播监听rregister