问题描述
我正在尝试在Android中实现MVI体系结构,但不想使用Mosby Library。 我想先学习基础知识。
我正在构建一个示例应用程序,在该应用程序中,当我按下按钮时,textview中的文本会发生变化(最初,文本是其他内容)。 这是MainActivity和MainPresenter的代码。
class MainActivity : AppCompatActivity(), MainContract.View {
lateinit var mPresenter: MainContract.Presenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mPresenter = MainPresenter()
mPresenter.attachPresenter(this)
bind()
}
@SuppressLint("CheckResult")
private fun bind() {
mPresenter.states().subscribe({ state ->
render(state)
}, {
Log.e("error", "Error is: ", it)
it.printStackTrace()
})
mPresenter.addIntents(intents())
}
override fun intents(): Observable<MainIntent> {
return Observable.merge(
initialIntent(),
clickIntent()
)
}
override fun render(state: MainViewState) {
btn_show.isEnabled = state.isEnabledButton
helloWorldTextView.text = state.message
loadingIndicator.visibility = if (state.isLoading) View.VISIBLE else View.GONE
}
private fun initialIntent(): Observable<MainIntent.InitialIntent> = Observable.just(MainIntent.InitialIntent)
private fun clickIntent(): Observable<MainIntent.ClickIntent> {
return btn_show.clicks().map { MainIntent.ClickIntent("Eureka") }
}
}
class MainPresenter : MainContract.Presenter {
private val intentsSubject: PublishSubject<MainIntent> = PublishSubject.create()
override fun states(): Observable<MainViewState> {
return statesObservable
}
private lateinit var view: MainContract.View
override fun attachPresenter(view: MainContract.View) {
this.view = view
}
@SuppressLint("CheckResult")
override fun addIntents(intents: Observable<MainIntent>) {
intents.subscribe(intentsSubject)
}
private val reducer =
BiFunction { previousState: MainViewState, result: MainResult ->
when (result) {
is MainResult.InitialResult.InFlight -> previousState.copy(
isLoading = true,
message = "Initial Result",
isEnabledButton = false
)
is MainResult.InitialResult.Success -> previousState.copy(
isLoading = true,
message = "Initial Success",
isEnabledButton = true
)
is MainResult.InitialResult.Error -> previousState.copy(
isLoading = false,
message = "Error Initially",
isEnabledButton = true
)
is MainResult.ClickedResult.Success -> previousState.copy(
isLoading = false,
message = System.currentTimeMillis().toString(),
isEnabledButton = true
)
is MainResult.ClickedResult.Error -> previousState.copy(
isLoading = false,
message = "Error Clicked",
isEnabledButton = true
)
is MainResult.ClickedResult.InFlight -> previousState.copy(
isLoading = true,
message = "Clicked In Flight",
isEnabledButton = false
)
}
}
private fun actionFromIntent(intent: MainIntent): MainAction {
if (intent is MainIntent.InitialIntent) {
return MainAction.InitialAction
} else if (intent is MainIntent.ClickIntent) {
return MainAction.ClickedAction("Hello")
} else {
return MainAction.InitialAction
}
}
private var actionProcessor: ObservableTransformer<MainAction, MainResult> = ObservableTransformer { actions ->
actions.publish { shared ->
Observable.merge<MainResult>(
shared.ofType(MainAction.InitialAction::class.java).compose(initialActionProcessor),
shared.ofType(MainAction.ClickedAction::class.java).compose(clickActionProcessor)
)
}
}
private val initialActionProcessor =
ObservableTransformer<MainAction.InitialAction, MainResult.InitialResult> { action: Observable<MainAction.InitialAction> ->
action.switchMap {
Observable.just("hello initially")
.map { MainResult.InitialResult.Success(it) }
.cast(MainResult.InitialResult::class.java)
.onErrorReturn { MainResult.InitialResult.Error(it.message!!) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.startWith { MainResult.InitialResult.InFlight }
}
}
private val clickActionProcessor =
ObservableTransformer<MainAction.ClickedAction, MainResult.ClickedResult> { action: Observable<MainAction.ClickedAction> ->
Observable.just("Click").map { message ->
MainResult.ClickedResult.Success(message)
}.cast(MainResult.ClickedResult::class.java)
.onErrorReturn { MainResult.ClickedResult.Error("Error") }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.startWith { MainResult.ClickedResult.InFlight }
}
private val statesObservable: Observable<MainViewState> = compose()
private fun compose(): Observable<MainViewState> {
return intentsSubject
.map {
actionFromIntent(it)
}
.compose(actionProcessor)
.scan(MainViewState.idle(), reducer)
.distinctUntilChanged()
.replay(1)
.autoConnect(0)
}
}
问题在于仅触发了Inital事件,而没有其他事件。 该代码不响应点击,渲染最初仅被调用一次。
另外,如果我从actionProcessors代码中删除startWith{}
, 则会响应单击,但仅响应一次。
在那之后,什么也没有发生。
有人看到代码问题吗? 我已经尝试解决这个问题已有一段时间了。
1楼
我之前的回复:
这不是您问题的直接答案。 但是,如果您实施以下内容,则可能不会遇到您实际提出的问题,并且将拥有更简单的MVI解决方案。
你可能尝试合并 , 和想法。
在这里看看: : 这很简单。 第1部分和第2部分应该足够了。
我尝试了第一种方法,但被最初的复杂性所排斥。 在第二种方法中,您没有操作,意图,结果,但有消息。 这很容易推断。
还有一个新的MVI课程-但尚未检查。
当前方法:
我试过提到Elm Architecture,但是它并不完整。 至少有两个问题:
- 某一时刻只有一个请求可以通过队列。 一些RxJava应该可以做到这一点(groupBy有2个流:ui,可能是背景)。
-
并行请求将更新相同的状态,所以你应该区分
DataStates
您的内部UiState
。 因此,UI的不同部分具有不同的状态。
在编写我们意识到的实际补丁之前,这并不是去ATM的方法:宣布的Composables可以做MVI的窍门:平滑而精确的数据过渡到UI的特定部分。
免责声明:主持人删除了我的答案,该答案是实际答案。 更重要的是,我的移动到评论的答案被缩减了,这使其看起来还没有完成。 这就是为什么该帖子再次出现。 亲爱的主持人看完后,可以删除免责声明,谢谢:)