gpt4 book ai didi

android - MVP 与 MVVM : how to manage alert dialogs in MVVM and improve testability

转载 作者:行者123 更新时间:2023-12-02 16:06:20 25 4
gpt4 key购买 nike

我是 MVP 爱好者,但同时我思想开放,我正在努力提高我对 MVVM 和数据绑定(bind)的了解:

我在这里 fork 了https://github.com/jpgpuyo/MVPvsMVVM

原始存储库 https://github.com/florina-muntenescu/MVPvsMVVM来自@FMuntenescu

我创建了几个分支。在其中一个中,我想根据按钮上执行的点击次数显示具有不同样式的 2 个不同的警报对话框:

  • 偶数次点击 -> 显示标准对话框
  • 奇数次点击 -> 显示 droidcon 对话框

您可以在这里找到分支机构: https://github.com/jpgpuyo/MVPvsMVVM/tree/multiple_dialogs_databinding_different_style

我在 View 模型中创建了 2 个可观察字段,并添加了 1 个绑定(bind)适配器。

Activity :

private void setupViews() {
buttonGreeting = findViewById(R.id.buttonGreeting);
buttonGreeting.setOnClickListener(v -> mViewModel.onGreetingClicked());
}

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
app:greetingType="@{viewModel.greetingType}"
app:greetingMessage="@{viewModel.greetingMessage}">

View 模型:

public ObservableField<String> greetingMessage = new ObservableField<>();
public ObservableField<GreetingType> greetingType = new ObservableField<>();

public void onGreetingClicked() {
numberOfClicks++;
if (numberOfClicks % 2 == 0) {
mSubscription.add(mDataModel.getStandardGreeting()
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(greeting -> {
greetingMessage.set(greeting);
greetingType.set(GreetingType.STANDARD);
}));
} else {
mSubscription.add(mDataModel.getDroidconGreeting()
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(greeting -> {
greetingMessage.set(greeting);
greetingType.set(GreetingType.DROIDCON);
}));
}
}

MVVMBindingAdapter:

@BindingAdapter({"greetingType", "greetingMessage"})
public static void showAlertDialog(View view, GreetingType greetingType,
String greetingMessage) {
if (GreetingType.STANDARD.equals(greetingType)){
new DialogHelper().showStandardGreetingDialog(view.getContext(),
greetingMessage, greetingMessage);
} else if(GreetingType.DROIDCON.equals(greetingType)) {
new DialogHelper().showDroidconGreetingDialog(view.getContext(),
greetingMessage, greetingMessage);
}
}

使用 MVVM,不确定如何实现它以通过 java 单元测试进行完全可测试。我创建了一个绑定(bind)适配器,但是:

  • 我需要在绑定(bind)适配器中使用 if/else 来显示一个或另一个对话框。

  • 我不知道如何将对话框助手注入(inject)绑定(bind)适配器,因此无法使用单元测试进行验证,除非使用 powermock。

我为每个对话框添加了不同的样式,因为如果我不添加样式,我们可以认为对话框的标题和消息是从数据层检索的,但考虑到 android 样式来自数据会很奇怪层。

是否可以在 MVVM 中注入(inject)一个对话框助手来解决这个问题并使代码可测试?

哪一种是使用 MVVM 管理警报对话框的最佳方式?

最佳答案

我用于MVVM的解决方案是混合的,如下。

来自Medium帖子中提到的Jose Alcérreca的文章LiveData with SnackBar, Navigation and other events (the SingleLiveEvent case)在对 Show Dialog from ViewModel in Android MVVM Architecture 的 SO 答复中提到,我选择第四个选项“推荐:使用事件包装器”。原因是如果需要的话我可以查看消息。另外,我还从 this comment in Jose's Gist 添加了 observeEvent() 扩展方法。 .

我的最终代码是:

import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer

/**
* Used as a wrapper for data that is exposed via a LiveData that represents an event.
* See:
* https://medium.com/androiddevelopers/livedata-with-snackbar-navigation-and-other-events-the-singleliveevent-case-ac2622673150
* https://gist.github.com/JoseAlcerreca/e0bba240d9b3cffa258777f12e5c0ae9
*/
open class LiveDataEvent<out T>(private val content: T) {

@Suppress("MemberVisibilityCanBePrivate")
var hasBeenHandled = false
private set // Allow external read but not write

/**
* Returns the content and prevents its use again.
*/
fun getContentIfNotHandled(): T? {
return if (hasBeenHandled) {
null
} else {
hasBeenHandled = true
content
}
}

/**
* Returns the content, even if it's already been handled.
*/
fun peekContent(): T = content
}

inline fun <T> LiveData<LiveDataEvent<T>>.observeEvent(owner: LifecycleOwner, crossinline onEventUnhandledContent: (T) -> Unit) {
observe(owner, Observer { it?.getContentIfNotHandled()?.let(onEventUnhandledContent) })
}

用法如下(我的例子是数据同步完成时触发事件):

class ExampleViewModel() : ViewModel() {
private val _synchronizationResult = MutableLiveData<LiveDataEvent<SyncUseCase.Result>>()
val synchronizationResult: LiveData<LiveDataEvent<SyncUseCase.Result>> = _synchronizationResult

fun synchronize() {
// do stuff...
// ... when done we get "result"
_synchronizationResult.value = LiveDataEvent(result)
}
}

并通过使用 observeEvent() 来使用它,以获得漂亮、简洁的代码:

exampleViewModel.synchronizationResult.observeEvent(this) { result ->
// We will be delivered "result" only once per change
}

关于android - MVP 与 MVVM : how to manage alert dialogs in MVVM and improve testability,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56892366/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com