为什么Android中需要viewmodel工厂?

Iba*_*ola 14 android mvvm viewmodel kotlin kodein

我们一直在讨论这个问题,但是我们不知道创建视图模型工厂来创建视图模型而不是直接实例化视图模型的原因。创建仅创建视图模型的工厂有什么好处?

我只是举了一个简单的示例,说明我没有工厂如何做到这一点

这是kodein模块:

val heroesRepositoryModel = Kodein {
    bind<HeroesRepository>() with singleton {
        HeroesRepository()
    }

    bind<ApiDataSource>() with singleton {
        DataModule.create()
    }

    bind<MainViewModel>() with provider {
        MainViewModel()
    }
}
Run Code Online (Sandbox Code Playgroud)

我不使用工厂实例化viewmodel的Activity部分

class MainActivity : AppCompatActivity() {
    private lateinit var heroesAdapter: HeroAdapter
    private lateinit var viewModel: MainViewModel
    private val heroesList = mutableListOf<Heroes.MapHero>()
    private var page = 0
    private var progressBarUpdated = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        viewModel = ViewModelProviders.of(this)
                .get(MainViewModel::class.java)
        initAdapter()
        initObserver()
        findHeroes()
    }
Run Code Online (Sandbox Code Playgroud)

我直接实例化用例的ViewModel,而无需在构造函数中使用它

class MainViewModel : ViewModel(), CoroutineScope {

    private val heroesRepository: HeroesRepository = heroesRepositoryModel.instance()
    val data = MutableLiveData<List<Heroes.MapHero>>()

    private var job: Job = Job()
    override val coroutineContext: CoroutineContext
        get() = uiContext + job

    fun getHeroesFromRepository(page: Int) {
        launch {
            try {
                val response = heroesRepository.getHeroes(page).await()
                data.value = response.data.results.map { it.convertToMapHero() }
            } catch (e: HttpException) {
                data.value = null
            } catch (e: Throwable) {
                data.value = null
            }
        }
    }

    override fun onCleared() {
        super.onCleared()
        job.cancel()
    }
}
Run Code Online (Sandbox Code Playgroud)

所以这里有一个使用工厂的例子

class ListFragment : Fragment(), KodeinAware, ContactsAdapter.OnContactListener {

    override val kodein by closestKodein()

    private lateinit var adapterContacts: ContactsAdapter

    private val mainViewModelFactory: MainViewModelFactory by instance()
    private val mainViewModel: MainViewModel by lazy {
        activity?.run {
            ViewModelProviders.of(this, mainViewModelFactory)
                .get(MainViewModel::class.java)
        } ?: throw Exception("Invalid Activity")
    }

    override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_list, container, false)
    }
Run Code Online (Sandbox Code Playgroud)

viewmodelfactory:

class MainViewModelFactory (private val getContacts: GetContacts) : ViewModelProvider.Factory {

    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(MainViewModel::class.java)) {
            return MainViewModel(getContacts) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}
Run Code Online (Sandbox Code Playgroud)

和视图模型:

class MainViewModel(private val getContacts: GetContacts) : BaseViewModel() {
    lateinit var gamesList: LiveData<PagedList<Contact>>
    var contactsSelectedData: MutableLiveData<List<Contact>> = MutableLiveData()
    var contactsSelected: ArrayList<Contact> = ArrayList()
    private val pagedListConfig by lazy {
        PagedList.Config.Builder()
                .setEnablePlaceholders(false)
                .setInitialLoadSizeHint(PAGES_CONTACTS_SIZE)
                .setPageSize(PAGES_CONTACTS_SIZE)
                .setPrefetchDistance(PAGES_CONTACTS_SIZE*2)
                .build()
    }
Run Code Online (Sandbox Code Playgroud)

这是完整的第一个示例:

https://github.com/ibanarriolaIT/Marvel/tree/mvvm

以及完整的第二个示例:

https://github.com/AdrianMeizoso/Payment-App

San*_*Sur 24

简而言之,

如果我们需要通过一些input data constructor viewModel,我们需要创建一个factory class用于视图模型。

例如:-

class MyViewModelFactory constructor(private val repository: DataRepository): ViewModelProvider.Factory {

     override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return if (modelClass.isAssignableFrom(MyViewModel::class.java!!)) {
            MyViewModel(this.repository) as T
        } else {
            throw IllegalArgumentException("ViewModel Not Found")
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

原因

我们不能直接创建 的对象,ViewModel因为它不会意识到lifecyclerOwner。所以我们使用:-

ViewModelProviders.of(this, MyViewModelFactory(repository)).get(MyViewModel::class.java)
Run Code Online (Sandbox Code Playgroud)


Vis*_*ora 13

我们不能自行创建ViewModel。我们需要Android提供的ViewModelProviders实用程序来创建ViewModels。

但是ViewModelProviders只能实例化没有arg构造函数的ViewModels。

因此,如果我有一个带有多个参数的ViewModel,则需要使用一个Factory,可以将其传递给ViewModelProviders,以便在需要MyViewModel实例时使用。

例如 -

public class MyViewModel extends ViewModel {
    private final MyRepo myrepo;
    public MyViewModel(MyRepo myrepo) {
         this.myrepo = myrepo;
    }
}
Run Code Online (Sandbox Code Playgroud)

要实例化此ViewModel,我需要有一个工厂,ViewModelProviders可以使用该工厂来创建其实例。

ViewModelProviders实用程序无法使用参数构造函数创建ViewModel的实例,因为它不知道如何以及在构造函数中传递哪些对象。

  • 对于依赖倒置,类不应该创建它需要的依赖。应该向它提供这些依赖项。这里我们有一个构造函数注入的例子,我们通过对象构造函数注入我们的 repo。 (4认同)
  • 但是我正在视图模型本身中实例化存储库,因此构造函数没有任何参数。那么这里的问题是,在活动中实例化存储库是否有优势,创建工厂以将存储库传递给视图模型,或者只是在视图模型中实例化存储库并使用 ViewModelProviders 而无需创建自己的工厂。 (2认同)
  • “我在视图模型本身中实例化存储库”,然后您违反了DI原理,使我想知道为什么使用Kodein。 (2认同)

小智 11

当我们只是使用ViewModel时,我们无法将参数传递给该 ViewModel

class GameViewModel() : ViewModel() {

    init {
        Log.d(TAG, "GameViewModel created")
    }
}
Run Code Online (Sandbox Code Playgroud)

然而,在某些情况下,我们需要将自己的参数传递给 ViewModel。这可以使用ViewModelFactory来完成。

class ScoreViewModel(finalScore: Int) : ViewModel() {

    val score = finalScore

    init {
        Log.d(TAG, "Final score: $finalScore")
    }
}
Run Code Online (Sandbox Code Playgroud)

为了实例化这个 ViewModel,我们需要一个ViewModelProvider.Factory,因为简单的 ViewModel 无法实例化它。

class ScoreViewModelFactory(private val finalScore: Int) : ViewModelProvider.Factory {

    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(ScoreViewModel::class.java)) {
            return ScoreViewModel(finalScore) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}
Run Code Online (Sandbox Code Playgroud)

当涉及到使用 ViewModelProvider 实例化此 ViewModel 的对象时,我们将 ViewModelFactory 作为参数传递,其中包含有关我们想要传递的自定义参数的信息。它是这样的:

viewModelFactory = ScoreViewModelFactory(score)
viewModel = ViewModelProvider(this,viewModelFactory).get(ScoreViewModel::class.java)
Run Code Online (Sandbox Code Playgroud)

这就是工厂方法存在的原因。


Epi*_*rce 6

我们一直在讨论这个问题,但是我们不知道创建视图模型工厂来创建视图模型而不是直接实例化视图模型的原因。创建仅创建视图模型的工厂有什么好处?

因为Android只会为尚未为特定给定LifecycleOwner创建的新实例提供给您。

我们也不要忘记ViewModels在配置更改后保持活动状态,因此,如果旋转手机,则不应创建新的ViewModel。

如果您要返回上一个活动,然后重新打开该活动,则应收到先前的ViewModel onCleared(),而新的活动应具有一个新的ViewModel。

除非您自己这样做,否则您应该只相信ViewModelProviders.Factory可以完成其工作。

(并且您需要工厂,因为您通常不仅仅具有no-arg构造函数,您的ViewModel具有构造函数参数,并且ViewModelProvider在使用非默认构造函数时必须知道如何填写构造函数参数)。

  • 这是唯一强调仅设置“初始状态”一次而不是每次旋转设备的重要性的答案。这就是为什么创建定制工厂很重要的本质。 (2认同)