原由
之前使用Rxjava2实现的单例模式的Rxbus来进行组件通信
现在有两个问题不好实现:
- 在不同的Activity中同时post出两个相同的Class类型,只想在当前Activity中接收该类型事件
- 实现订阅事件后产生的Disposable统一管理回收(使用者不用考虑回收问题,在当前Activity产生的Disposable自动在onDestroy()后被回收)
之后是为什么会产生上面两种需求的场景
最近在使用MVP模式封装Retrofit网络访问模块时,需要从Model层回调给Presenter层再回调给View层,虽然解耦很强,但作为喜欢偷懒的程序员,总觉得这么写很费劲.
于是乎,就思考如何简化代码而又不影响解耦程度
经过一天的思考和设计,得到以下方案:
- 去除Presenter层(使用Rxbus代替其功能)
- View层接口去除,接收事件改造成订阅事件
- Model层功能不变,回调数据给Presenter层改为发送事件给订阅者(View层)
仔细想了想,发现这个方案不仅没有增加耦合度,还精简了一半的代码,而其中最重要的就是通过Rxbus来进行事件的订阅和发送
注:这种方式实现MVP模型的方式放在下一篇文章来说,这篇文章主要讲一讲结合dagger2作用域实现局部单例
备注:图画的有点丑.别介意!
实现
首先写出dagger2的Component和Module
//这里使用单例域
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
//这里暴露子组件
BaseComponent baseComponent(BaseModule baseModule);
}
@Singleton
@Module
public class AppModule {
private App mApp;
public AppModule(App app) {
mApp = app;
}
/**
* 将App提供出去
* @return
*/
@Singleton
@Provides
App providerApp() {
return mApp;
}
}
以上是单例域的Component域Module,在App中注册,因应用只有一个Application,所以在这个域中提供的@Singleton注解的对象都将只有一份(也就是单例效果)
之后是Activity域的dagger组件
//这里是针对BaseActivity,所有继承BaseActivity的Activity都在BaseScope域下,但这些子类之间却互不影响,各自维持一个BaseScope
//因为每个子类都会在父类的onCreate()方法中创建一个BaseComponent,虽然都在BaseScope下,但却被两个不同的BaseComponent对象分别管理
//所以BaseModule所提供的对象都只在当前Activity中保持单例
@BaseScope
@Subcomponent(modules = BaseModule.class)
public interface BaseComponent {
void inject(MainActivity activity);
void inject(Main2Activity activity);
}
@BaseScope
@Module
public class BaseModule {
private Activity mActivity;
private CompositeDisposable mDisposable = new CompositeDisposable();
public BaseModule(Activity activity) {
mActivity = activity;
}
@BaseScope
@Provides
Activity providerActivity() {
return mActivity;
}
@BaseScope
@Provides
Context providerContext() {
return mActivity;
}
/**
* CompositeDisposable,这里使用了BaseScope域,则在此域中的mDisposable对象只有一份(也就是局部单例)
* @return
*/
@BaseScope
@Provides
CompositeDisposable providerDisposable() {
return mDisposable;
}
}
dagger的组件都搭建完毕,此时就该正主上场了
在这里将实现一个非单例的Rxbus,通过dagger作用域来实现局部单例,这也就会使发送的事件只会在局部被接收,并通过事件管理器来自动回收资源
//使Rxbus的作用域为Activity域,保证其在该域中,也就是Activity中保证单例(本例中作用域被限制在了Activity中,可根据情况更改)
@ActivityScope
public class RxBus {
private final FlowableProcessor<Object> mBus;
//将Rxbus注入
@Inject
public RxBus() {
//调用toSerialized()方法,保证线程安全
mBus = PublishProcessor.create().toSerialized();
}
/**
* 发送消息
* @param o
*/
public void post(Object o) {
new SerializedSubscriber<>(mBus).onNext(o);
}
/**
* 确定接收消息的类型
* @param aClass
* @param <T>
* @return
*/
public <T> Flowable<T> toFlowable(Class<T> aClass) {
return mBus.ofType(aClass);
}
/**
* 判断是否有订阅者
* @return
*/
public boolean hasSubscribers() {
return mBus.hasSubscribers();
}
}
然后是事件管理器和事件包裹类
@BaseScope
public class SubstribeManager {
@Inject
RxBus mRxBus;
//这里得到的CompositeDisposable与Activity中的是同一个对象
@Inject
CompositeDisposable mDisposable;
@Inject
public SubstribeManager() {
}
/**
* 发送事件
* @param o
*/
public void post(Object o) {
mRxBus.post(o);
}
/**
* 订阅事件,返回包裹类,方便同一回收Disposable
* @param aClass
* @param <T>
* @return
*/
public <T> FlowableWrap<T> subscribeResult(Class<T> aClass) {
return new FlowableWrap<>(mRxBus.toFlowable(aClass),mDisposable);
}
}
public class FlowableWrap<T> {
private Flowable<T> mFlowable;
private CompositeDisposable mDisposable;
public FlowableWrap(Flowable<T> flowable, CompositeDisposable disposable) {
mFlowable = flowable;
mDisposable = disposable;
}
/**
* 订阅事件,与Rxjava一致,自动添加事件到CompositeDisposable中,方便回收
*/
public void subscribe(final Consumer<T> consumer) {
mDisposable.add(mFlowable.subscribe(new Consumer<T>() {
@Override
public void accept(@NonNull T t) throws Exception {
consumer.accept(t);
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
//do something for error
}
}));
}
/**
* 订阅事件,与Rxjava一致,自动添加事件到CompositeDisposable中,方便回收
* 可自己处理异常
*/
public void subscribe(final Consumer<T> consumer, final Consumer<? super Throwable> error) {
mDisposable.add(mFlowable.subscribe(new Consumer<T>() {
@Override
public void accept(@NonNull T t) throws Exception {
consumer.accept(t);
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
error.accept(throwable);
}
}));
}
}
然后开始使用!!!
为了体现出效果,用RecyclerView创建出一个列表,当点击item时发送item的position
在Activity中订阅Integer类型的事件,接收到事件后把position 吐司出来
adapter代码
@BaseScope
public class MainRvAdapter extends RecyclerView.Adapter<MainRvAdapter.MyViewHolder> {
@Inject
SubstribeManager mManager;
//将Adapter也注入
@Inject
public MainRvAdapter() {
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
TextView tv = new TextView(parent.getContext());
return new MyViewHolder(tv);
}
@Override
public void onBindViewHolder(MyViewHolder holder, final int position) {
TextView tv = (TextView) holder.itemView;
tv.setText("随意的布局,随意的数据"+position);
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//发送事件,类型为Integer的position
mManager.post(position);
}
});
}
@Override
public int getItemCount() {
return 100;
}
class MyViewHolder extends RecyclerView.ViewHolder {
public MyViewHolder(View itemView) {
super(itemView);
}
}
}
再来看看BaseActivity代码
@BaseScope
public abstract class BaseActivity extends AppCompatActivity{
//CompositeDisposable在BaseModule中被提供,并且作用域是BaseScope,所以在该域内该对象都只有一份
@Inject
CompositeDisposable mDisposable;
@Inject
SubstribeManager mManager;
private BaseComponent mBaseComponent;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//通过父组件AppComponent来注册BaseComponent
mBaseComponent = ((App) getApplication()).getAppComponent().baseComponent(new BaseModule(this));
inject();
}
/**
* 得到BaseComponent
* @return
*/
protected BaseComponent getBaseComponent() {
return mBaseComponent;
}
/**
* 需要子类进行inject
*/
protected abstract void inject();
@Override
protected void onDestroy() {
super.onDestroy();
//回收CompositeDisposable,因整个BaseScope域都用的同一个,所以能把所有产生的Disposable回收
mDisposable.dispose();
}
}
主要的Activity代码
public class MainActivity extends BaseActivity {
@Inject
MainRvAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView rv = (RecyclerView) findViewById(R.id.rv);
rv.setLayoutManager(new LinearLayoutManager(this));
rv.setAdapter(mAdapter);
Log.d("MainActivity", mDisposable+"");
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//这里可以尝试提前dispose(),就会发现之前订阅的事件已经接收不到了,也验证了事件被自动添加到CompositeDisposable中了
// mDisposable.dispose();
startActivity(new Intent(MainActivity.this,Main2Activity.class));
}
});
subtribeData();
}
@Override
protected void inject() {
getBaseComponent().inject(this);
}
/**
* 订阅Integer类型事件
*/
private void subtribeData() {
//这里不需要再考虑回收问题
mManager.subscribeResult(Integer.class).subscribe(new Consumer<Integer>() {
@Override
public void accept(@NonNull Integer integer) throws Exception {
Toast.makeText(MainActivity.this, "被点到的item为: "+integer, Toast.LENGTH_SHORT).show();
}
});
}
}
结果图
这里验证一下两个Activity中维持的事件管理器(SubstribeManager)是否是同一个
看截图就会发现不同的Activity之间提供的处于BaseScope域中的对象不相同,所以Activity之间相互独立,发送的事件也不会"窜门"!
最后需要验证的就是Disposable是否会被自动回收
在点击事件中提前把CompositeDisposable集合回收了,
就会发现再也收不到之前订阅的事件了,这也就验证了Disposable被自动添加到集合中了,并在onDestroy()时,通过父类的实现,而被回收了
本篇文章是关于Rxbus组件内(局部)通信的简单使用,之后的文章会带来更加有意思的用法!
**
github:https://github.com/lightofrain/LocalRxbus.git
**