利用Lifecycler实现无侵入的广播功能

686 阅读3分钟

「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战」。

前言

做应用开发的很多时候在一个项目中总是有很多功能需要重复写入不同的Activity。尽管我们对某些功能进行封装,例如:GsonUtil , DateUtil。

但是很多时候我们都是没有办法单纯利用工具类就无侵入实现和Activity生命周期以及四大组件深度绑定的功能,例如A对服务,A对广播进行调用的相关功能。

在学习Lifecycler之后,我们才得到了一种良好的解耦生命周期的方式。

本文我们就结合Lifecycler实现一个无侵入的广播功能。

无侵入定义:在具体使用相关功能的Activity中,没有相关功能的实现代码,连调用都看不到。

正文

实现例子:PDA的广播扫描功能。

实现原理

基于观察者模式

写一个ScanBaseActivity 作为被观察者,这个类可以继承BaseActivity,其余需要Scan功能的activity 可以继承他。同理,也可以写一个ScanBaseFragment给Fragment 使用,这里不再赘述。

ScanBaseActivity 负责订阅观察者,观察者负责实现调用广播的功能。(举一反三,也可以实现别的功能)

写一个ScanManager 实现 LifecycleEventObserver 作为观察者。

通过onStateChanged(@NonNull LifecycleOwner owner, @NonNull Lifecycle.Event event)方法来判断在不同的生命周期方法中实现对广播的注册和移除。

写一个ScanReceiver 实现广播接收者对于扫描广播的信息接收。

同时,在广播接收者中调用ScanBaseActivity 中的抽象方法getCode(String scanResult)。这个方法由实际使用的子类实现。

具体代码如下

ScanBaseActivity:

这个类做两件事。第一,添加观察者。第二,定义子类需要接收扫描结果的抽象方法。

/**
 * @TODO: Scan功能Activity的父类 被观察者
 * @Date: 2021/11/17 8:34
 * @User: lay
 * 1. 利用Lifecycler实现无侵入的广播功能植入到所有扫描Activity
 */
public abstract class ScanBaseActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //和广播观察者建立关系
        getLifecycle().addObserver(ScanManager.getInstance());
    }
​
    public abstract void getCode(String scanResult);
}

ScanManager:

这个类我们实现了LifecycleEventObserver接口。

值得一提的是,目前官方废弃了@OnLifecycleEvent ,改用LifecycleEventObserver接口来实现对于生命周期方法的观察。

LifecycleEventObserver接口是一个单方法接口,这个接口只定义了onStateChanged(@NonNull LifecycleOwner owner, @NonNull Lifecycle.Event event)方法。

我们可以在这个方法中得到当前的生命周期对象Lifecycle.Event event。

如此就能对不同的生命周期执行不同的方法。

/**
 * @TODO: 广播观察者
 * @Date: 2021/11/17 9:09
 * @User: lay
 * 观察者:观察生命周期,实现对应生命周期的方法
 */
public class ScanManager implements LifecycleEventObserver {
    private static final ScanManager single_SManager = new ScanManager();
    private ScanReceiver scanReceiver;
​
    private ScanManager(){}
    public static ScanManager getInstance(){
        return single_SManager;
    }
    //ON_RESUME 注册广播
    public void registerReceiver(@NonNull LifecycleOwner owner){
        scanReceiver = new ScanReceiver();
        IntentFilter intentFilter = new IntentFilter("nlscan.action.SCANNER_RESULT");
        if(owner instanceof AppCompatActivity){
            ((AppCompatActivity)owner).registerReceiver(scanReceiver,intentFilter);
        }else if(owner instanceof Fragment){
            (((Fragment)owner).getActivity()).registerReceiver(scanReceiver,intentFilter);
        }
​
    }
    //ON_PAUSE 移除广播
    public void unRegisterReceiver(@NonNull LifecycleOwner owner){
        if(owner instanceof AppCompatActivity){
            ((AppCompatActivity)owner).unregisterReceiver(scanReceiver);
        }else if(owner instanceof Fragment){
            (((Fragment)owner).getActivity()).unregisterReceiver(scanReceiver);
        }
    }
​
    @Override
    public void onStateChanged(@NonNull LifecycleOwner owner, @NonNull Lifecycle.Event event) {
        if(event == Lifecycle.Event.ON_RESUME){
            registerReceiver(owner);
        }else if(event == Lifecycle.Event.ON_PAUSE){
            unRegisterReceiver(owner);
        }
    }
}

ScanReceiver:

根据PDA文档,接收对应Key的数据信息。

判断如果当前上下文context是ScanBaseActivity类的对象,则将结果传递给ScanBaseActivity的抽象方法getCode()。

/**
 * @TODO: 扫描的广播接收者实现
 * @Date: 2021/11/17 9:46
 * @User: lay
 */
public class ScanReceiver extends BroadcastReceiver {
​
    private static final String SCAN_ACTION = "nlscan.action.SCANNER_RESULT";
​
    //Android 4.4 以上
    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    @Override
    public void onReceive(Context context, Intent intent) {
        final String scan_barcode1 = intent.getStringExtra("SCAN_BARCODE1");
​
        final String scanStatus = intent.getStringExtra("SCAN_STATE");
        if("ok".equals(scanStatus)){
            if(context instanceof ScanBaseActivity){
                ((ScanBaseActivity)context).getCode(scan_barcode1);
            }
        }else{
            Log.e("registerReceiver", "onReceive: scanStatus:"+ scanStatus );
        }
    }
}

具体子类:

实现ScanBaseActivity的抽象方法,接收扫描数据结果。

/**
 * @TODO: 具体子类
 * @Date: 2021/11/17 8:32
 * @User: lay
 */
public class DCodeReportLossActivity extends ScanBaseActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
​
    @Override
    public void getCode(String scanResult) {
        ToastUtils.show("Scan");
    }
​
}

如此,具体子类就能无侵入的直接使用扫描功能的结果了。