如何使用Android的架构组件完成ViewModel中的Activity?

wnavrhmk  于 2023-06-04  发布在  Android
关注(0)|答案(5)|浏览(163)

我试图找出如何以最好的方式完成一个活动从ViewModel。我找到了一种方法,使用LiveData对象并发出“信号”。
我怀疑这个解决方案有开销。那么是正确的解决方案还是我应该使用更准确的?
所以去例子:让我们假设在一个应用程序中有一个活动MainActivity和视图模型,如下所示:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val model = ViewModelProviders.of(this).get(MainViewModel::class.java)

        model.shouldCloseLiveData.observe(this, Observer { finish() })

    }
 }

作为MainActivity的伴侣,是一个MainViewModel,如下所示:

class MainViewModel(app: Application) : AndroidViewModel(app) {

  val shouldCloseLiveData = MutableLiveData<Void>()

  fun someAction(){
    shouldCloseLiveData.postValue(null)
  }

}
8yoxcaq7

8yoxcaq71#

我和你一样认为这个解决方案看起来不整洁,原因有两个。* 首先 * 使用MutableLiveData对象来通知事件是一种解决方法。没有数据更改。* 第二 * 将LiveData暴露到视图模型的外部通常违反了 * 封装 * 的原则。
我仍然对这个丑陋的android概念感到惊讶。它们应该提供一个选项来观察视图模型,而不是内部的LiveData对象。
我尝试使用WeakReference s来实现观察者模式。这个不稳定。以不可预知的方式,WeakReference的引用丢失(null),在这种情况下,不可能调用finish()。这是令人惊讶的,因为我不认为活动是垃圾收集,而运行。
所以这是一个部分的答案排除。作为WeakReference实现的观察者模式似乎无法替代您的建议。
我想知道如果我在onStop()onDestroy()期间删除引用,通过硬引用实现观察者模式是否合法。我问了这个问题here

9udxz4iz

9udxz4iz2#

您可以使用单一事件,如您在此处看到的实现:https://gist.github.com/JoseAlcerreca/5b661f1800e1e654f07cc54fe87441af#file-event-kt
因此,您不必更改活动的实现

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val model = ViewModelProviders.of(this).get(MainViewModel::class.java)

        model.shouldCloseLiveData.observe(this, Observer { finish() })

    }
 }

视图模型将类似于

class MainViewModel : ViewModel() {

  private val _shouldCloseLiveData = MutableLiveData<Event<Boolean>>()
  val shouldCloseLiveData: LiveData<Event<Boolean>> = _shouldCloseLiveData
  
  fun someAction(){
      _shouldCloseLiveData.postValue(Event(true))
  }

}
0x6upsns

0x6upsns3#

我遇到了类似的问题:我有两个Activity(A和B),它们的视图模型连接到一个对象(数据库中的一个表):从一个可观察的实时数据,我必须导航到另一个活动B(从A到B)。问题是,在调用新活动B之后,B中的可观察对象改变了被观察对象中的值。活动A仍然活动,其活动数据再次调用导航代码到B。在一个无限循环中
经过一些研究,我意识到运行finish方法并不意味着该活动真的是destroyed
解决方案是,在可观察代码中,从实时数据中删除与特定活动相关的可观察数据。

liveData.removeObservers(activity);

我在下面的代码片段中展示了它。它是用Java写的,但我认为你读起来没有问题。有了这个,我解决了我的问题。

public class LockActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...    

        mViewModel = ViewModelProviders.of(this).get(ConfigurationViewModel.class);

        LiveData<Configurazione> liveData = mViewModel.getConfiguration();
        liveData.observe(this, config-> {
            // this code will be executed even another activity is in front of
            // screen
            boolean validToken = (config.getToken()!=null);

            if (!tokenValido) {
                intent = LoginActivity.createIntent(this);
            } else {
                intent = MainActivity.createIntent(this);
            }

            // this line remove the observable, so even activity will be destroied with calm, it is not a problem, the code is no more executed
            liveData.removeObservers(this);
        });
    }

    ...
}

我认为您可以很容易地在代码中适应这种情况。希望能帮上忙。

0aydgbwb

0aydgbwb4#

编辑:

更好的方法是使用KotlinChannels。我将在这个示例中使用LiveData,但您也可以直接使用Flow。
视图模型:

class MyViewModel : ViewModel() {

    private val _event = Channel<MyEvent>()
    
    val event = _event.receiveAsFlow().asLiveData(Dispatchers.Main)

    fun someAction() {
        _event.send(MyEvent.FINISH)
    }
}

enum class MyEvent {
    FINISH
}

在活动方面:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val model = ViewModelProvider(this).get(MyViewModel::class.java)

        model.event.observe(this) {
            if (it == MyEvent.FINISH) {
                finish()
            }
        }

        myButton.setOnClickListener {
            model.someAction()
        }
    }
}

上一个答案(不太好):

我同意,似乎没有一个很好的解决方案,你的建议确实工作得很好。但我建议如下。
因为你使用的是Kotlin,你可以像这样从你的activity传递一个函数到viewmodel:
视图模型:

class MainViewModel(app: Application) : AndroidViewModel(app) {
    
    fun someAction(block: () -> Unit) {
        // do stuff
        block()
    }
}

Activity:这里使用button(和clicklistener)作为示例,但它可以出现在activity代码中的任何位置。

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val model = ViewModelProviders.of(this).get(MainViewModel::class.java)

        myButton.setOnClickListener {
            model.someAction() {
                finish()
            }
        }
    }
}

block函数本质上将充当回调。

velaa5lx

velaa5lx5#

单线法

<?xml version="1.0" encoding="utf-8"?>
<layout>

    <data>

        <import type="android.app.Activity" />

        <import type="androidx.databinding.DataBindingUtil" />

        <import type="androidx.databinding.ViewDataBinding" />
    </data>

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{(v)->((Activity)(((ViewDataBinding)DataBindingUtil.findBinding(v)).lifecycleOwner)).finish()}"
            android:text="Exit" />
    </LinearLayout>
</layout>

或简化布局表达式,将代码移动到帮助器类

public class DataBindingHelper {
    public static Activity findActivity(View v) {
        final ViewDataBinding binding = DataBindingUtil.findBinding(v);
        return binding != null ? (Activity) binding.getLifecycleOwner() : null;
    }
}
<?xml version="1.0" encoding="utf-8"?>
<layout>

    <data>

        <import type="com.xxx.settingitemmoretest.DataBindingHelper" />

        <import type="android.app.Activity" />

        <variable
            name="viewModel"
            type="com.xxx.settingitemmoretest.MainActivity.ViewModel" />

    </data>

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context=".MainActivity">

        <ToggleButton
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:checked="@{viewModel.shouldCloseLiveData}"
            android:onCheckedChanged="@{(v, p)-> p ? DataBindingHelper.findActivity(v).finish(): void}"
            android:text="Stub Useless Button"
            android:visibility="gone" />

        <ToggleButton
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:checked="@{viewModel.shouldCloseLiveData}"
            android:onCheckedChanged="@{(v, p)-> p ? ((Activity)context).finish(): void}"
            android:text="Stub Useless Button"
            android:visibility="gone" />
    </LinearLayout>
</layout>

或使用绑定适配器

public class ViewGroupBindingAdapter {

    @BindingAdapter({"android:shouldClose"})
    public static void setShouldClose(ViewGroup viewGroup, boolean shouldClose) {
        if(shouldClose){
            final ViewDataBinding binding = DataBindingUtil.getBinding(viewGroup);
            if (binding != null) {
                ((Activity) binding.getLifecycleOwner()).finish();
            }
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<layout>

    <data>

        <variable
            name="viewModel"
            type="com.xxx.settingitemmoretest.MainActivity.ViewModel" />

    </data>

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:shouldClose="@{viewModel.shouldCloseLiveData}"
        tools:context=".MainActivity">
     </LinearLayout>
</layout>

相关问题