Android MVVM startActivity的最佳实践

时间:2016-11-07 21:53:25

标签: android mvvm android-databinding decoupling

我正在使用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)}">

或者将它移到View并从EventBus或Rx调用它并在我的ViewModel中只有POJO是最佳做法吗?

6 个答案:

答案 0 :(得分:18)

你的问题的答案是你的目标是什么?

如果您想使用 MVVM 分离关注点,以便对Viewmodel进行单元测试,那么您应该尝试将需要Context的所有内容与您的Viewmodel分开ViewmodelViewmodel包含您应用的核心业务逻辑,并且不应具有外部依赖关系。

但是我喜欢你要去的地方:)如果决定打开哪个Activity就在View中,那么为它编写JUnit测试是非常困难的。但是,您可以将对象传递到执行startActivity()调用的Activity。现在,在单元测试中,您只需模拟此对象并验证是否已打开正确的 =INDEX(SLAVE!C2:C4;MATCH(CONCATENATE(MASTER!A2;MASTER!B2);SLAVE!D2:D4;0))

答案 1 :(得分:8)

将它放在ViewModel内是绝对完美的,但是您需要从ViewModel / Activity设置Fragment

以下是学习MVVM架构时可以遵循的一些链接。

Approaching Android with MVVM
Android MVVM
https://github.com/ivacf/archi
People-MVVM
MVVM on Android: What You Need to Know

答案 2 :(得分:6)

我的操作方式是在您的ViewModel中

val activityToStart = MutableLiveData<Pair<KClass<*>, Bundle?>>()

这使您可以检查启动的Activity的类以及在Bundle中传递的数据。然后,您可以在“活动”中添加以下代码:

viewModel.activityToStart.observe(this, Observer { value ->
    val intent = Intent(this, value.first.java)
    if(value.second != null)
        intent.putExtras(value.second)
    startActivity(intent)
})

答案 3 :(得分:2)

MVVM的原理指出,只有视图(活动/片段)保留对ViewModel的引用,而ViewModel不应保留对任何View的引用。

对于您来说,要开始一项活动,我会这样做:

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}" />

答案 4 :(得分:1)

在MVVM中,我们可以将LiveData用于此Event。因为activity/Fragment被销毁时ViewModel仍然有效!所以最好的方法是LiveData

1。从Event创建类调用ExtendsViewModel

class Event : ViewModel() {

2。从LiveData创建字段:

private val _showSignIn = MutableLiveData<Boolean?>()

此私有字段的3.create方法:

val showSignIn: LiveData<Boolean?>
    get() = _showSignIn

4.create方法,您可以在liveData上设置值:

 fun callSignIn() {
        _showSignIn.value = true
    }

最终活动类别:

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
        }
  1. 在您的活动或片段中调用方法:

eventViewModel中的实例:

 private val eventViewModel = Event()

打电话给观察员:

 eventViewModel.showSignIn.observe(this, Observer {
            startActivity(Intent(this, MainActivity::class.java))
        })

,如果您使用data binding,则可以在callSignIn() XML中调用onClick

在变量标记中:

<variable
            name="eventViewModel"
            type=packageName.Event" />

 android:onClick="@{() -> eventViewModel.callSignIn()}" 

注意:不要忘记在activity/fragment中设置绑定:

  binding.eventViewModel = eventViewModel

我搜索最佳方法并找到它。我希望可以帮助某人

答案 5 :(得分:0)

根据数据绑定文档。 有两种方法可以做到:

1- MethodReferences:您必须将视图作为参数传递给函数,否则会出现编译时错误。
如果您将使用这种方式,请在此处做一个单独的类作为示例来处理此类事件。

MyHandler

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:这里不需要通过视图作为示例。
但是,如果要启动Activity,则将viewModel扩展为AndroidViewModel,然后将使用applicaion对象。

ViewModel

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)}">