Android MVVM startActivity 的最佳实践
Best practice for Android MVVM startActivity
我正在使用 MVVM 和 DataBinding 构建一个 Android 应用程序。我的 ViewModel 中有一个启动 Activity 的函数。
在 ViewModel 中调用 onClick 可以吗?
像这样。
public class MyViewModel {
public void onClick(View view, long productId) {
Context context = view.getContext();
Intent intent = new Intent(context, ProductDetailActivity.class);
intent.putExtra("productId", productId);
context.startActivity(intent);
}
}
在我的 XML 中:
...
android:onClick="@{(v) -> viewModel.onClick(v, viewModel.product.id)}">
或者将它移动到视图并从 EventBus 或 Rx 调用它并且在我的 ViewModel 中只有 POJO 是最佳实践吗?
将它放在 ViewModel
中绝对完美,但是您需要从 Activity
/Fragment
.
设置您的 ViewModel
您可以通过以下链接了解 MVVM 架构。
Approaching Android with MVVM
Android MVVM
https://github.com/ivacf/archi
People-MVVM
MVVM on Android: What You Need to Know
你的问题的答案是你的目标是什么?
如果您想使用 MVVM 来分离关注点,以便您可以对 Viewmodel
进行单元测试,那么您应该尝试保留所有需要 Context
与您的 Viewmodel
分开。 Viewmodel
包含您应用的核心业务逻辑,不应有任何外部依赖项。
不过我喜欢你要去的地方 :) 如果打开 Activity 的决定在于视图,那么很难为它编写 JUnit 测试。但是,您可以将一个对象传递给执行 startActivity()
调用的 Viewmodel
。现在,在您的 单元测试 中,您可以简单地模拟此对象并验证是否打开了正确的 Activity
我的方法是,在您的 ViewModel 中:
val activityToStart = MutableLiveData<Pair<KClass<*>, Bundle?>>()
这允许您检查 Activity 的 class 是否已启动,以及 Bundle 中传递的数据。然后,在您的 Activity 中,您可以添加此代码:
viewModel.activityToStart.observe(this, Observer { value ->
val intent = Intent(this, value.first.java)
if(value.second != null)
intent.putExtras(value.second)
startActivity(intent)
})
正如 MVVM 的原则所指出的,只有 View (activity/fragment) 持有对 ViewModel 的引用,而 ViewModel 不应持有对任何 View 的引用。
在你的情况下,要开始 activity,我会这样做:
MyViewModel.class
public class MyViewModel {
public static final int START_SOME_ACTIVITY = 123;
@Bindable
private int messageId;
public void onClick() {
messageId = START_SOME_ACTIVITY;
notifyPropertyChanged(BR.messageId); //BR class is automatically generated when you rebuild the project
}
public int getMessageId() {
return messageId;
}
public void setMessageId(int message) {
this.messageId = messageId;
}
}
在你的MainActivity.class
@BindingAdapter({"showMessage"})
public static void runMe(View view, int messageId) {
if (messageId == Consts.START_SOME_ACTIVITY) {
view.getContext().startActivity(new Intent(view.getContext(), SomeActivity.class));
}
}
@Override
protected void onPause() {
super.onPause();
finish(); //only call if you want to clear this activity after go to other activity
}
最后,在你的activity_main.xml
<Button
android:onClick="@{()-> myViewModel.onClick()}"
bind:showMessage="@{myViewModel.messageId}" />
根据数据绑定文档。
有两种方法可以做到这一点:
1- MethodReferences :您必须将视图作为参数传递给函数,否则会出现编译时错误。
如果您将使用这种方式,请在此处做一个单独的 class 作为示例来处理此类事件。
我的处理程序
public class MyHandler {
public void onClick(View view, long productId) {
Context context = view.getContext();
Intent intent = new Intent(context, ProductDetailActivity.class);
intent.putExtra("productId", productId);
context.startActivity(intent);
}
}
XML
<data>
<variable
name="viewModel"
type="com.example.ViewModel"
<variable
name="myHandler"
type="com.example.MyHandler" />
</data>android:onClick="@{myHandler.onClick(viewModel.product.id)}">
2- Listener bindings :这里的例子不需要传递视图。
但是如果你想要 startActivity 让你的 viewModel 扩展 AndroidViewModel 并且你将使用应用程序对象。
视图模型
public class MyViewModel extends AndroidViewModel {
public void onClick(long productId) {
Intent intent = new Intent(getApplication(), ProductDetailActivity.class);
intent.putExtra("productId", productId);
context.startActivity(intent);
}
}
XML
android:onClick="@{() -> viewModel.onClick(viewModel.product.id)}">
在 MVVM 中,我们可以为这个事件使用 LiveData
。因为当 activity/Fragment
销毁时 ViewModel 还活着!所以最好的办法是 LiveData
1.Create Class 从 ViewModel
:
调用 Event
和 Extends
class Event : ViewModel() {
2.create 来自 LiveData 的字段:
private val _showSignIn = MutableLiveData<Boolean?>()
3.create 这个私有字段的方法:
val showSignIn: LiveData<Boolean?>
get() = _showSignIn
4.create 您可以在 liveData 上设置值的方法:
fun callSignIn() {
_showSignIn.value = true
}
最终事件Class:
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class Event : ViewModel() {
private val _showSignIn = MutableLiveData<Boolean?>()
val showSignIn: LiveData<Boolean?>
get() = _showSignIn
fun callSignIn() {
_showSignIn.value = true
}
- 调用您的 activity 或片段中的方法 :
来自 eventViewModel 的实例:
private val eventViewModel = Event()
呼叫观察员:
eventViewModel.showSignIn.observe(this, Observer {
startActivity(Intent(this, MainActivity::class.java))
})
如果您使用 data binding
,您可以在 onClick
XML 中调用 callSignIn()
:
在变量标签中:
<variable
name="eventViewModel"
type=packageName.Event" />
android:onClick="@{() -> eventViewModel.callSignIn()}"
注意:不要忘记在 activity/fragment
中设置绑定:
binding.eventViewModel = eventViewModel
我搜索最好的方法并找到它。我希望能帮助别人
我正在使用 MVVM 和 DataBinding 构建一个 Android 应用程序。我的 ViewModel 中有一个启动 Activity 的函数。 在 ViewModel 中调用 onClick 可以吗?
像这样。
public class MyViewModel {
public void onClick(View view, long productId) {
Context context = view.getContext();
Intent intent = new Intent(context, ProductDetailActivity.class);
intent.putExtra("productId", productId);
context.startActivity(intent);
}
}
在我的 XML 中:
...
android:onClick="@{(v) -> viewModel.onClick(v, viewModel.product.id)}">
或者将它移动到视图并从 EventBus 或 Rx 调用它并且在我的 ViewModel 中只有 POJO 是最佳实践吗?
将它放在 ViewModel
中绝对完美,但是您需要从 Activity
/Fragment
.
ViewModel
您可以通过以下链接了解 MVVM 架构。
Approaching Android with MVVM
Android MVVM
https://github.com/ivacf/archi
People-MVVM
MVVM on Android: What You Need to Know
你的问题的答案是你的目标是什么?
如果您想使用 MVVM 来分离关注点,以便您可以对 Viewmodel
进行单元测试,那么您应该尝试保留所有需要 Context
与您的 Viewmodel
分开。 Viewmodel
包含您应用的核心业务逻辑,不应有任何外部依赖项。
不过我喜欢你要去的地方 :) 如果打开 Activity 的决定在于视图,那么很难为它编写 JUnit 测试。但是,您可以将一个对象传递给执行 startActivity()
调用的 Viewmodel
。现在,在您的 单元测试 中,您可以简单地模拟此对象并验证是否打开了正确的 Activity
我的方法是,在您的 ViewModel 中:
val activityToStart = MutableLiveData<Pair<KClass<*>, Bundle?>>()
这允许您检查 Activity 的 class 是否已启动,以及 Bundle 中传递的数据。然后,在您的 Activity 中,您可以添加此代码:
viewModel.activityToStart.observe(this, Observer { value ->
val intent = Intent(this, value.first.java)
if(value.second != null)
intent.putExtras(value.second)
startActivity(intent)
})
正如 MVVM 的原则所指出的,只有 View (activity/fragment) 持有对 ViewModel 的引用,而 ViewModel 不应持有对任何 View 的引用。
在你的情况下,要开始 activity,我会这样做:
MyViewModel.class
public class MyViewModel {
public static final int START_SOME_ACTIVITY = 123;
@Bindable
private int messageId;
public void onClick() {
messageId = START_SOME_ACTIVITY;
notifyPropertyChanged(BR.messageId); //BR class is automatically generated when you rebuild the project
}
public int getMessageId() {
return messageId;
}
public void setMessageId(int message) {
this.messageId = messageId;
}
}
在你的MainActivity.class
@BindingAdapter({"showMessage"})
public static void runMe(View view, int messageId) {
if (messageId == Consts.START_SOME_ACTIVITY) {
view.getContext().startActivity(new Intent(view.getContext(), SomeActivity.class));
}
}
@Override
protected void onPause() {
super.onPause();
finish(); //only call if you want to clear this activity after go to other activity
}
最后,在你的activity_main.xml
<Button
android:onClick="@{()-> myViewModel.onClick()}"
bind:showMessage="@{myViewModel.messageId}" />
根据数据绑定文档。 有两种方法可以做到这一点:
1- MethodReferences :您必须将视图作为参数传递给函数,否则会出现编译时错误。
如果您将使用这种方式,请在此处做一个单独的 class 作为示例来处理此类事件。
我的处理程序
public class MyHandler {
public void onClick(View view, long productId) {
Context context = view.getContext();
Intent intent = new Intent(context, ProductDetailActivity.class);
intent.putExtra("productId", productId);
context.startActivity(intent);
}
}
XML
<data>
<variable
name="viewModel"
type="com.example.ViewModel"
<variable
name="myHandler"
type="com.example.MyHandler" />
</data>android:onClick="@{myHandler.onClick(viewModel.product.id)}">
2- Listener bindings :这里的例子不需要传递视图。
但是如果你想要 startActivity 让你的 viewModel 扩展 AndroidViewModel 并且你将使用应用程序对象。
视图模型
public class MyViewModel extends AndroidViewModel {
public void onClick(long productId) {
Intent intent = new Intent(getApplication(), ProductDetailActivity.class);
intent.putExtra("productId", productId);
context.startActivity(intent);
}
}
XML
android:onClick="@{() -> viewModel.onClick(viewModel.product.id)}">
在 MVVM 中,我们可以为这个事件使用 LiveData
。因为当 activity/Fragment
销毁时 ViewModel 还活着!所以最好的办法是 LiveData
1.Create Class 从 ViewModel
:
Event
和 Extends
class Event : ViewModel() {
2.create 来自 LiveData 的字段:
private val _showSignIn = MutableLiveData<Boolean?>()
3.create 这个私有字段的方法:
val showSignIn: LiveData<Boolean?>
get() = _showSignIn
4.create 您可以在 liveData 上设置值的方法:
fun callSignIn() {
_showSignIn.value = true
}
最终事件Class:
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
class Event : ViewModel() {
private val _showSignIn = MutableLiveData<Boolean?>()
val showSignIn: LiveData<Boolean?>
get() = _showSignIn
fun callSignIn() {
_showSignIn.value = true
}
- 调用您的 activity 或片段中的方法 :
来自 eventViewModel 的实例:
private val eventViewModel = Event()
呼叫观察员:
eventViewModel.showSignIn.observe(this, Observer {
startActivity(Intent(this, MainActivity::class.java))
})
如果您使用 data binding
,您可以在 onClick
XML 中调用 callSignIn()
:
在变量标签中:
<variable
name="eventViewModel"
type=packageName.Event" />
android:onClick="@{() -> eventViewModel.callSignIn()}"
注意:不要忘记在 activity/fragment
中设置绑定:
binding.eventViewModel = eventViewModel
我搜索最好的方法并找到它。我希望能帮助别人