前言
前段时间给GeekNews做了一部分重构和修改,记录下来大致可以分为三点:
基类的改动
这次改动新增了RootActivity
,并且让BaseActivity
继承了SimpleActivity
,各层职责明确,最后形成了如下的继承关系链:
RootActivity
–> BaseActivity
–> SimpleActivity
–> SupportActivity
–> Activity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| public abstract class SimpleActivity extends SupportActivity { protected Activity mContext; private Unbinder mUnBinder; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(getLayout()); mUnBinder = ButterKnife.bind(this); mContext = this; onViewCreated(); App.getInstance().addActivity(this); initEventAndData(); } protected void setToolBar(Toolbar toolbar, String title) { toolbar.setTitle(title); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { onBackPressedSupport(); } }); } protected void onViewCreated() { } @Override protected void onDestroy() { super.onDestroy(); App.getInstance().removeActivity(this); mUnBinder.unbind(); } protected abstract int getLayout(); protected abstract void initEventAndData(); }
|
抽象类SimpleActivity
继承自Fragmentation
的SupportActivity
,负责完成布局渲染,控件注入,初始化Toolbar
的工作,封装度低,非常灵活,可以自由扩展,通常用于不需要Presenter
层的简单页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| public abstract class BaseActivity<T extends BasePresenter> extends SimpleActivity implements BaseView { @Inject protected T mPresenter; protected ActivityComponent getActivityComponent(){ return DaggerActivityComponent.builder() .appComponent(App.getAppComponent()) .activityModule(getActivityModule()) .build(); } protected ActivityModule getActivityModule(){ return new ActivityModule(this); } @Override protected void onViewCreated() { super.onViewCreated(); initInject(); if (mPresenter != null) mPresenter.attachView(this); } @Override protected void onDestroy() { if (mPresenter != null) mPresenter.detachView(); super.onDestroy(); } @Override public void showErrorMsg(String msg) { SnackbarUtil.show(((ViewGroup) findViewById(android.R.id.content)).getChildAt(0), msg); } @Override public void useNightMode(boolean isNight) { if (isNight) { AppCompatDelegate.setDefaultNightMode( AppCompatDelegate.MODE_NIGHT_YES); } else { AppCompatDelegate.setDefaultNightMode( AppCompatDelegate.MODE_NIGHT_NO); } recreate(); } @Override public void stateError() { } @Override public void stateLoading() { } @Override public void stateMain() { } protected abstract void initInject(); }
|
抽象类BaseActivity
继承自SimpleActivity
, 由于附带了<T extends BasePresenter>
的泛型并且实现了BaseView
的接口,使它具备了与Presenter
交互的能力,在这里完成依赖注入和Presenter
的绑定,通常用于普通MVP结构中的View层
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| public abstract class RootActivity<T extends BasePresenter> extends BaseActivity<T>{ private static final int STATE_MAIN = 0x00; private static final int STATE_LOADING = 0x01; private static final int STATE_ERROR = 0x02; private ProgressImageView ivLoading; private LinearLayout viewError; private FrameLayout viewLoading; private ViewGroup viewMain; private int currentState = STATE_MAIN; @Override protected void initEventAndData() { viewMain = (ViewGroup) findViewById(R.id.view_main); if (viewMain == null) { throw new IllegalStateException( "The subclass of RootActivity must contain a View named view_main."); } if (!(viewMain.getParent() instanceof ViewGroup)) { throw new IllegalStateException( "view_main's ParentView should be a ViewGroup"); } ViewGroup parent = (ViewGroup) viewMain.getParent(); View.inflate(mContext, R.layout.view_error, parent); View.inflate(mContext, R.layout.view_progress, parent); viewError = (LinearLayout) parent.findViewById(R.id.view_error); viewLoading = (FrameLayout) parent.findViewById(R.id.view_loading); ivLoading = (ProgressImageView) viewLoading.findViewById(R.id.iv_progress); viewError.setVisibility(View.GONE); viewLoading.setVisibility(View.GONE); viewMain.setVisibility(View.VISIBLE); } @Override public void stateError() { if (currentState == STATE_ERROR) return; hideCurrentView(); currentState = STATE_ERROR; viewError.setVisibility(View.VISIBLE); } @Override public void stateLoading() { if (currentState == STATE_LOADING) return; hideCurrentView(); currentState = STATE_LOADING; viewLoading.setVisibility(View.VISIBLE); ivLoading.start(); } @Override public void stateMain() { if (currentState == STATE_MAIN) return; hideCurrentView(); currentState = STATE_MAIN; viewMain.setVisibility(View.VISIBLE); } private void hideCurrentView() { switch (currentState) { case STATE_MAIN: viewMain.setVisibility(View.GONE); break; case STATE_LOADING: ivLoading.stop(); viewLoading.setVisibility(View.GONE); break; case STATE_ERROR: viewError.setVisibility(View.GONE); break; } } }
|
抽象类RootActivity
继承自BaseActivity
,进一步实现了BaseView
中的几个state
控制方法,使页面具备了在Error
、Loading
、Main
等状态间自由切换的能力,实现的原理是首先将当面页面布局文件中显示主体内容的控件命名为view_main
,随后在initEventAndData
方法中会自动寻找view_main
,并在同一层级中动态添加Error与Loading视图,然后在各state方法中控制各状态页面的显隐。按照这个思路,根据自己的需求还可以添加Empty
, NoNetWorking
等状态视图,如果状态视图比较复杂,为了性能可以把状态视图定义为ViewStub
,实际用到时才去渲染
Model层的改动
这次改动抽象出了HttpHelper
, DBHelper
, PreferencesHelper
这些接口,RealmHelper
,RetrofitHelper
等只是做为接口的一种实现,这样显然要合理的多。并且添加了DataManager
做为数据分发中心,DataManager
分别实现了HttpHelper
, DBHelper
, PreferencesHelper
接口,具体实现还是交由各子Helper
类来完成,这样的好处是对上层屏蔽了数据来源,Presenter
层无论需要什么数据都可以从DataManager
中取,各Helper
类由Dagger2
注入到DataManager
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| @Module public class AppModule { private final App application; public AppModule(App application) { this.application = application; } @Provides @Singleton App provideApplicationContext() { return application; } @Provides @Singleton HttpHelper provideHttpHelper(RetrofitHelper retrofitHelper) { return retrofitHelper; } @Provides @Singleton DBHelper provideDBHelper(RealmHelper realmHelper) { return realmHelper; } @Provides @Singleton PreferencesHelper providePreferencesHelper(ImplPreferencesHelper implPreferencesHelper) { return implPreferencesHelper; } @Provides @Singleton DataManager provideDataManager(HttpHelper httpHelper, DBHelper DBHelper, PreferencesHelper preferencesHelper) { return new DataManager(httpHelper, DBHelper, preferencesHelper); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public class DataManager implements HttpHelper, DBHelper, PreferencesHelper { HttpHelper mHttpHelper; DBHelper mDbHelper; PreferencesHelper mPreferencesHelper; public DataManager(HttpHelper httpHelper, DBHelper dbHelper, PreferencesHelper preferencesHelper) { mHttpHelper = httpHelper; mDbHelper = dbHelper; mPreferencesHelper = preferencesHelper; } @Override public boolean getNightModeState() { return mPreferencesHelper.getNightModeState(); } @Override public GoldManagerBean getGoldManagerList() { return mDbHelper.getGoldManagerList(); } @Override public Flowable<List<RepliesListBean>> fetchRepliesList(String id) { return mHttpHelper.fetchRepliesList(id); } }
|
到此Model
层的改动算结束了吗?其实还有一些工作可以做,依照参考Android-CleanArchitecture,我们还可以引入Mapper
和UseCase
:
Mapper(DTO->VO)
为数据添加一层Mapper
映射,完成DTO->VO
的转化,提高上层的稳定性,让Model层完成更多职责。
具体来说:我们从服务端拿到的原始数据可以视作DTO
,我们在View
层直接使用的数据则是VO
,如果没有这层中间转化,直接使用从服务端取到的原始数据可以会面临参数值为null,或者接口字段不匹配等等问题,导致View层加入不必要的逻辑判断,或是伴随Model层的变动不断修改,通过加入这层Mapper
映射后我们有机会把更多处理交由Model层完成,保证上层的纯净和稳定。具体内容可以看YoKey的这篇[架构向] 谈Android中DTO -> VO的重要性
摘取Android-CleanArchitecture中的一段示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| @PerActivity public class UserModelDataMapper { @Inject public UserModelDataMapper() {} * Transform a {@link User} into an {@link UserModel}. * * @param user Object to be transformed. * @return {@link UserModel}. */ public UserModel transform(User user) { if (user == null) { throw new IllegalArgumentException("Cannot transform a null value"); } final UserModel userModel = new UserModel(user.getUserId()); userModel.setCoverUrl(user.getCoverUrl()); userModel.setFullName(user.getFullName()); userModel.setEmail(user.getEmail()); userModel.setDescription(user.getDescription()); userModel.setFollowers(user.getFollowers()); return userModel; } * Transform a Collection of {@link User} into a Collection of {@link UserModel}. * * @param usersCollection Objects to be transformed. * @return List of {@link UserModel}. */ public Collection<UserModel> transform(Collection<User> usersCollection) { Collection<UserModel> userModelsCollection; if (usersCollection != null && !usersCollection.isEmpty()) { userModelsCollection = new ArrayList<>(); for (User user : usersCollection) { userModelsCollection.add(transform(user)); } } else { userModelsCollection = Collections.emptyList(); } return userModelsCollection; } }
|
UseCase
根据具体业务封装出一系列UseCase
,方便复用Presenter
中的一些逻辑,为Presenter
减压。
具体来说: Presenter
层不直接使用DataManager
(在Android-CleanArchitecture中被称作Repository
),而是根据需求引入自己需要的UseCase
来使用,UseCase
则会通过DataManager
取数据完成具体业务。为什么要这么做呢?因为Presenter
不同于MVVM
中的ViewModel
,它通常与一个View
层对象是强关联的,V与P一一对应,难以复用,通过把多个Presenter
之间可重用的逻辑抽取成一个个UseCase
,再让Presenter
引入需要的UseCase
,就实现了逻辑的复用。这种做法其实也是进一步细化了Model
层职责的粒度,在UseCase
中可以做一些额外操作减轻Presenter
层的压力,如果你的P层过于臃肿,那么就要考虑加入UseCase
来让Model
层分摊一部分职责了。具体实践可以参考Android官方项目todo-mvp-clean,摘取其中的一段示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
| public class GetStatistics extends UseCase<GetStatistics.RequestValues, GetStatistics.ResponseValue> { private final TasksRepository mTasksRepository; public GetStatistics(@NonNull TasksRepository tasksRepository) { mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!"); } @Override protected void executeUseCase(RequestValues requestValues) { mTasksRepository.getTasks(new TasksDataSource.LoadTasksCallback() { @Override public void onTasksLoaded(List<Task> tasks) { int activeTasks = 0; int completedTasks = 0; for (Task task : tasks) { if (task.isCompleted()) { completedTasks += 1; } else { activeTasks += 1; } } ResponseValue responseValue = new ResponseValue(new Statistics(completedTasks, activeTasks)); getUseCaseCallback().onSuccess(responseValue); } @Override public void onDataNotAvailable() { getUseCaseCallback().onError(); } }); } public static class RequestValues implements UseCase.RequestValues { } public static class ResponseValue implements UseCase.ResponseValue { private final Statistics mStatistics; public ResponseValue(@NonNull Statistics statistics) { mStatistics = checkNotNull(statistics, "statistics cannot be null!"); } public Statistics getStatistics() { return mStatistics; } } }
|
介绍完了优点下面该聊一聊局限性了,Mapper
和UseCase
与所有设计模式的通病一致,会额外创建许多类,对稍大型的项目而言,可能会封装出几十个Mapper
和UseCase
,而对于逻辑简单的小型项目则是起不到什么效果,只会徒增代码量,所以在Model
层是否要引入这些结构需要根据实际场景谨慎考虑,至少这次重构在经过权衡后,我还是没有引入这些结构。
RxJava相关
这次重构也由RxJava升级到了RxJava2,一些主要修改的地方如下:
Observable
不再支持背压,替换为了支持背压的Flowable
, Subscriber
被重命名为了Disposable
,CompositeSubscriber
相应的更名为了 CompositeDisposable
,在RxPresenter
中做了对应修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| public class RxPresenter<T extends BaseView> implements BasePresenter<T> { protected T mView; protected CompositeDisposable mCompositeDisposable; protected void unSubscribe() { if (mCompositeDisposable != null) { mCompositeDisposable.clear(); } } protected void addSubscribe(Disposable subscription) { if (mCompositeDisposable == null) { mCompositeDisposable = new CompositeDisposable(); } mCompositeDisposable.add(subscription); } protected <U> void addRxBusSubscribe(Class<U> eventType, Consumer<U> act) { if (mCompositeDisposable == null) { mCompositeDisposable = new CompositeDisposable(); } mCompositeDisposable.add(RxBus.getDefault().toDefaultFlowable(eventType, act)); } @Override public void attachView(T view) { this.mView = view; } @Override public void detachView() { this.mView = null; unSubscribe(); } }
|
RxJava2中AsyncSubject
、BehaviorSubject
、PublishSubject
、ReplaySubject
、UnicastSubject
保留不变,并新增了一系列支持背压的对应版本AsyncProcessor
、BehaviorProcessor
、PublishProcessor
、ReplayProcessor
、UnicastProcessor
, 在RxBus
中做了对应修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class RxBus { private final FlowableProcessor<Object> bus; private RxBus() { bus = PublishProcessor.create().toSerialized(); } public static RxBus getDefault() { return RxBusHolder.sInstance; } private static class RxBusHolder { private static final RxBus sInstance = new RxBus(); } public void post(Object o) { bus.onNext(o); } public <T> Flowable<T> toFlowable(Class<T> eventType) { return bus.ofType(eventType); } public <T> Disposable toDefaultFlowable(Class<T> eventType, Consumer<T> act) { return bus.ofType(eventType).compose(RxUtil.<T>rxSchedulerHelper()).subscribe(act); } }
|
Subscriber
在RxJava2中由抽象类变成了接口,不能直接继承了,但是官方提供了一系列实现类,所以GeekNews中用于统一处理错误的CommonSubscriber
改为继承了 FlowableSubscriber
的实现类ResourceSubscriber
, 也可以选择继承DisposableSubscriber
, 他们除了FlowableSubscriber
以外还实现了Disposable
接口,在执行订阅方法subscribeWith
后,能够返回一个Disposable
用于将来取消订阅
1 2
| public abstract class DisposableSubscriber<T> implements FlowableSubscriber<T>, Disposable public abstract class ResourceSubscriber<T> implements FlowableSubscriber<T>, Disposable
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| public abstract class CommonSubscriber<T> extends ResourceSubscriber<T> { private BaseView mView; private String mErrorMsg; private boolean isShowErrorState = true; protected CommonSubscriber(BaseView view){ this.mView = view; } protected CommonSubscriber(BaseView view, String errorMsg){ this.mView = view; this.mErrorMsg = errorMsg; } protected CommonSubscriber(BaseView view, boolean isShowErrorState){ this.mView = view; this.isShowErrorState = isShowErrorState; } protected CommonSubscriber(BaseView view, String errorMsg, boolean isShowErrorState){ this.mView = view; this.mErrorMsg = errorMsg; this.isShowErrorState = isShowErrorState; } @Override public void onComplete() { } @Override public void onError(Throwable e) { if (mView == null) { return; } if (mErrorMsg != null && !TextUtils.isEmpty(mErrorMsg)) { mView.showErrorMsg(mErrorMsg); } else if (e instanceof ApiException) { mView.showErrorMsg(e.toString()); } else if (e instanceof HttpException) { mView.showErrorMsg("数据加载失败ヽ(≧Д≦)ノ"); } else { mView.showErrorMsg("未知错误ヽ(≧Д≦)ノ"); LogUtil.d(e.toString()); } if (isShowErrorState) { mView.stateError(); } } }
|
这里也要注意RxJava2中的subscribe
的重载方法不能满足目前框架对于订阅时传入一个Subscriber
返回一个Disposable
的需求,要使用subscribeWith
方法替代
1 2 3 4 5 6 7
| public final Disposable subscribe() public final Disposable subscribe(Consumer<? super T> onNext) public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError) public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete) public final Disposable subscribe(Consumer<? super T> onNext, Consumer<? super Throwable> onError, Action onComplete, Consumer<? super Subscription> onSubscribe) public final void subscribe(Subscriber<? super T> s) public final void subscribe(FlowableSubscriber<? super T> s)
|
1 2 3 4
| public final <E extends Subscriber<? super T>> E subscribeWith(E subscriber) { subscribe(subscriber); return subscriber; }
|
为了和Java8中的Stream API保持命名风格一致,RxJava2将Action0
改名为Action
,Action1
改名为Consumer
,Action2
改名为BiConsumer
, ActionN
改名为Consumer<Object[]>
,将Func0
用java.util.concurrent.Callable<V>
代替, Func1
改名为Function
,Func2
改名为BiFunction
, Func3
- Func9
改名为Function3
-Function9
, FuncN
改名为Function<Object[], R>
,为了兼容在很多处使用操作符的地方都做了修改(吐血
此外RxUtil
中为了兼容也做了一些修改
Observable.OnSubscribe
变为ObservableOnSubscribe
,对应Flowable
的版本则是FlowableOnSubscribe
Observable.Transformer
变为ObservableTransformer
,对应Flowable
的版本则是FlowableTransformer
ObservableOnSubscribe
中负责发射数据的对象由Subscriber
变为ObservableEmitter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| * 统一线程处理 * @param <T> * @return */ public static <T> FlowableTransformer<T, T> rxSchedulerHelper() { return new FlowableTransformer<T, T>() { @Override public Flowable<T> apply(Flowable<T> observable) { return observable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } }; } * 生成Flowable * @param <T> * @return */ public static <T> Flowable<T> createData(final T t) { return Flowable.create(new FlowableOnSubscribe<T>() { @Override public void subscribe(FlowableEmitter<T> emitter) throws Exception { try { emitter.onNext(t); emitter.onComplete(); } catch (Exception e) { emitter.onError(e); } } }, BackpressureStrategy.BUFFER); }
|
这次重构还处理了RxBus
发生错误时,没有恢复订阅的bug。由于RxJava中的订阅者具有触发了一次onError
或onComplete
后就会不再接收事件的特性,且RxBus的事件通常只在Presenter
创建时订阅一次,所以一旦发生错误后续事件就接收不到了。处理这个问题有两种方案:
方案一:在每个操作符中捕获或转化错误,确保订阅者的onError
不会被触发
1 2 3 4 5 6 7 8 9 10 11 12
| .flatMap(new Function<String, Flowable<DailyBeforeListBean>>() { @Override public Flowable<DailyBeforeListBean> apply(String date) { Flowable<DailyBeforeListBean> bean = null; try { bean = mDataManager.fetchDailyBeforeListInfo(date); } catch (Exception e) { e.printStackTrace(); } return bean; } })
|
1 2 3 4 5 6 7 8 9 10 11
| .flatMap(new Function<String, Flowable<DailyBeforeListBean>>() { @Override public Flowable<DailyBeforeListBean> apply(String date) { return mDataManager.fetchDailyBeforeListInfo(date).onErrorResumeNext(new Function<Throwable, Publisher<? extends DailyBeforeListBean>>() { @Override public Publisher<? extends DailyBeforeListBean> apply(@NonNull Throwable throwable) throws Exception { return Flowable.empty(); } }); } })
|
可以像上面的操作符片段中那样用try-catch捕获错误,或者在操作符内部用onErrorResumeNext
将错误消息转化为空Flowable
发射出去,这两种方法都可以在内部消化掉错误。但是如果在每个操作符中都如此处理,代码会非常“不优雅”,而且添加这些处理也很麻烦,这个方案走不通
方案二:在发生错误时重新订阅,目前我用的就是这种方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| private void registerEvent() { addSubscribe(RxBus.getDefault().toFlowable(NightModeEvent.class) .compose(RxUtil.<NightModeEvent>rxSchedulerHelper()) .map(new Function<NightModeEvent, Boolean>() { @Override public Boolean apply(NightModeEvent nightModeEvent) { return nightModeEvent.getNightMode(); } }) .subscribeWith(new CommonSubscriber<Boolean>(mView, "切换模式失败ヽ(≧Д≦)ノ") { @Override public void onNext(Boolean aBoolean) { mView.useNightMode(aBoolean); } @Override public void onError(Throwable e) { super.onError(e); registerEvent(); } }) ); }
|
在onError
处调用方法重新订阅了这个事件,虽然效果不错,添加起来也相对简单,但是显然这个方法也无法让人十分满意,没准就忘记加了呢(= ̄ω ̄=)
那么还有其他方法吗?当然有!那就是:放弃RxBus和EventBus
路人:???
这篇文章放弃RxBus,拥抱RxJava:为什么避免使用EventBus/RxBus或许可以为你解惑,RxBus
作为RxJava的衍生物,功能没有EventBus
强大,并且至今还没有得到官方的支持,JakeWharton也不建议以RxBus
的方式来使用RxJava。
我们在创建下一个新项目前,或许真的应该考虑一下是否要引入RxBus
和EventBus
了╮(╯▽╰)╭
声明:本站所有文章均为原创或翻译,遵循署名-非商业性使用-禁止演绎 4.0 国际许可协议,如需转载请确保您对该协议有足够了解,并附上作者名(Est)及原贴地址