web-dev-qa-db-ja.com

Androidでビューモデルファクトリが必要なのはなぜですか?

これについては議論してきましたが、viewmodelを直接インスタンス化するのではなく、viewmodelファクトリを作成してviewmodelを作成する理由はわかりません。ビューモデルを作成するだけのファクトリを作成する利点は何ですか?

Factoryを使用せずにそれを実行した簡単な例を示します

ここにkodeinモジュールがあります:

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

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

    bind<MainViewModel>() with provider {
        MainViewModel()
    }
}

ファクトリを使用せずにビューモデルをインスタンス化するアクティビティの一部

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

コンストラクターに使用せずにユースケースを直接インスタンス化する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()
    }
}

だからここでファクトリを使用した例

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

ビューモデルファクトリ:

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

そしてviewmodel:

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

これが完全な最初の例です:

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

そして完全な2番目の例:

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

26
Iban Arriola

自分でViewModelを作成することはできません。 ViewModelを作成するには、Androidによって提供されるViewModelProvidersユーティリティが必要です。

ただし、ViewModelProvidersがインスタンス化できるのは、引数コンストラクタのないViewModelだけです。

したがって、複数の引数を持つViewModelがある場合、MyViewModelのインスタンスが必要なときに使用するViewModelProvidersに渡すことができるファクトリを使用する必要があります。

例えば ​​-

public class MyViewModel extends ViewModel {
    private final MyRepo myrepo;
    public MyViewModel(MyRepo myrepo) {
         this.myrepo = myrepo;
    }
}

このViewModelをインスタンス化するには、ViewModelProvidersがインスタンスを作成するために使用できるファクトリが必要です。

ViewModelProvidersユーティリティは、コンストラクターで渡すオブジェクトと方法を知らないため、引数コンストラクターを持つViewModelのインスタンスを作成できません。

24
Vishal Arora

これについては議論してきましたが、viewmodelを直接インスタンス化するのではなく、viewmodelファクトリを作成してviewmodelを作成する理由はわかりません。ビューモデルを作成するだけのファクトリを作成する利点は何ですか?

なぜなら、Androidは、まだ作成されていない場合にのみ新しいインスタンスを提供しますその特定の指定されたLifecycleOwnerの場合

また、ViewModelが構成の変更後も維持されることを忘れないでください。そのため、電話を回転させる場合、新しいViewModelを作成する必要はありません。

前のアクティビティに戻ってこのアクティビティを再び開くと、前のViewModelはonCleared()を受け取り、新しいアクティビティは新しいViewModelを持つはずです。

あなたが自分でそれをしているのでない限り、あなたはおそらくViewModelProviders.Factoryその仕事をします。

(通常、no-argコンストラクター、ViewModelにはコンストラクター引数があり、ViewModelProviderはデフォルト以外のコンストラクターを使用している場合にコンストラクター引数を入力する方法を知っている必要があります)。

14
EpicPandaForce

要するに、

passinput dataconstructorviewModelに渡す必要がある場合は、 a factory class for viewModel。

例のように:-

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

理由

ViewModelのオブジェクトを直接作成することはできませんできませんlifecyclerOwnerに注意してください。したがって、次のように使用します。

ViewModelProviders.of(this, MyViewModelFactory(repository)).get(MyViewModel::class.Java)
2
Santanu Sur