Rxbus结合dagger2作用域实现局部单例(资源自动回收和事件独立)

原由

之前使用Rxjava2实现的单例模式的Rxbus来进行组件通信

现在有两个问题不好实现:

  • 在不同的Activity中同时post出两个相同的Class类型,只想在当前Activity中接收该类型事件
  • 实现订阅事件后产生的Disposable统一管理回收(使用者不用考虑回收问题,在当前Activity产生的Disposable自动在onDestroy()后被回收)

之后是为什么会产生上面两种需求的场景

最近在使用MVP模式封装Retrofit网络访问模块时,需要从Model层回调给Presenter层再回调给View层,虽然解耦很强,但作为喜欢偷懒的程序员,总觉得这么写很费劲.
于是乎,就思考如何简化代码而又不影响解耦程度

经过一天的思考和设计,得到以下方案:

  • 去除Presenter层(使用Rxbus代替其功能)
  • View层接口去除,接收事件改造成订阅事件
  • Model层功能不变,回调数据给Presenter层改为发送事件给订阅者(View层)

仔细想了想,发现这个方案不仅没有增加耦合度,还精简了一半的代码,而其中最重要的就是通过Rxbus来进行事件的订阅和发送

注:这种方式实现MVP模型的方式放在下一篇文章来说,这篇文章主要讲一讲结合dagger2作用域实现局部单例

3501495-a758dc63cd78364a.png

备注:图画的有点丑.别介意!

实现

首先写出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();
            }
        });
    }
}

结果图


Screenshot_2017-05-24-21-25-17.png

这里验证一下两个Activity中维持的事件管理器(SubstribeManager)是否是同一个

image.png

看截图就会发现不同的Activity之间提供的处于BaseScope域中的对象不相同,所以Activity之间相互独立,发送的事件也不会"窜门"!

最后需要验证的就是Disposable是否会被自动回收

QQ截图20170526121332.png

在点击事件中提前把CompositeDisposable集合回收了,
就会发现再也收不到之前订阅的事件了,这也就验证了Disposable被自动添加到集合中了,并在onDestroy()时,通过父类的实现,而被回收了


本篇文章是关于Rxbus组件内(局部)通信的简单使用,之后的文章会带来更加有意思的用法!

**
github:https://github.com/lightofrain/LocalRxbus.git
**

推荐阅读更多精彩内容