Android开发&动态广播注册广播监听器——rregister

安卓中广播接收器有两种,动态注册,静态注册。
从当前的情况来看,安卓安全管理方面做的越来越严禁,越来越倾向于使用动态监听来处理系统信息,然后在退出应用或界面时移除监听。
安卓中常用的广播监听类别不是很多,主要包括:电量情况、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时需要满足以下两个条件:

  1. 必须继承自BaseReceiver,且严格按照指定的格式,在apply方法中处理结果,并提供默认的单参(HttpCallback类型)造函数,因为内部使用的是反射机制,否则,在创建实例时会直接出错。
  2. 不支持多重继承,即不能编写类继承自类似SMSReceiver这样的监听器类,这会导致泛型检查出错,当然如果真有这样需求,那么也可以,只要在定义回调类时,使用lambal表达式,就可以避过泛型检查。

同样重要的,在调用registerBroadcast方法时,要注意处理抛出的异常,这样才能保证系统的健壮,毕竟使用反射的话,如果类编写不规范就会直接出错。

因为个人原因,监听库中肯定会有许多情况未考虑到,也会有很多到广播未添加进入,如果有更多的需求可以直接 ISSURES

具体的使用方法详见GITHUB:动态广播监听rregister

猜你喜欢

转载自blog.csdn.net/lovingning/article/details/80725542
今日推荐