android - 无法使用 ViewModelFactory 实例化我的 ViewModel

标签 android kotlin android-databinding

我无法将 ViewModel 绑定(bind)到我的 Fragment(使用 lateinit),因为我在 Activity 中创建 ViewModel 的方式出错了。我做错了什么?

Caused by: java.lang.RuntimeException: Cannot create an instance of class com.example.foo.FooViewModel

这是 Activity :

class FooActivity : AppCompatActivity() {

    private lateinit var viewModel: FooViewModel

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_foo)

        val id = intent.getLongExtra(FOO_ID, 1L)

        val viewModelFactory = FooViewModelFactory(
            id,
            FooDatabase.getInstance(application).fooDao,
            application)
        viewModel = ViewModelProviders.of(
            this, viewModelFactory).get(FooViewModel::class.java)    
    }
}

当 Fragment 尝试绑定(bind) viewModel 时实例化它时,会发生异常,请参阅下面注释的代码行:

class BlankFragment : Fragment() {

    private val viewModel: FooViewModel by activityViewModels()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        super.onCreateView(inflater, container, savedInstanceState)
        val binding = FragmentBlankBinding.inflate(inflater)
        binding.setLifecycleOwner(this)

        // EXCEPTION OCCURS HERE
        binding.viewModel = viewModel

        return binding.root
    }
}

这是 ViewModel 和 ViewModelFactory 类的代码:

class FooViewModelFactory (
    private val id: Long,
    private val fooDao: FooDao,
    private val application: Application) : ViewModelProvider.Factory {

    @Suppress("unchecked_cast")
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(FooViewModel::class.java)) {
             return FooViewModel(
                  id,
                  fooDao,
                  application
             ) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

class FooViewModel(id: Long, fooDao : FooDao, app: Application ): AndroidViewModel(app) {

    private val _foo = MutableLiveData<Foo>()
    val foo: LiveData<Foo>
        get() = _foo

    // infrastructure needed to get the Foo from the database
    private val _database = fooDao
    private val _fooid = id
    private var viewModelJob = Job()
    // database queries in the IO thread to avoid locking up the UI
    private val ioScope = CoroutineScope(viewModelJob + Dispatchers.IO)

    init {
        // commenting this out and doing nothing doesn't affect exception in question.
        GlobalScope.launch{ getFoo()}
    }

    private fun getFoo() = ... // code to fetch Foo from database

}

编辑:堆栈跟踪。

2020-05-22 22:10:05.018 8599-8599/com.example.foo E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.foo, PID: 8599
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.foo/com.example.foo.FooActivity}: android.view.InflateException: Binary XML file line #19: Binary XML file line #19: Error inflating class fragment
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3037)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3172)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1906)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6863)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: android.view.InflateException: Binary XML file line #19: Binary XML file line #19: Error inflating class fragment
     Caused by: android.view.InflateException: Binary XML file line #19: Error inflating class fragment
     Caused by: java.lang.RuntimeException: Cannot create an instance of class com.example.foo.FooViewModel
        at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:269)
        at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:106)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:185)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150)
        at androidx.lifecycle.ViewModelLazy.getValue(ViewModelProvider.kt:54)
        at androidx.lifecycle.ViewModelLazy.getValue(ViewModelProvider.kt:41)
        at com.example.foo.BlankFragment.getViewModel(Unknown Source:2)
        at com.example.foo.BlankFragment.onCreateView(BlankFragment.kt:27)
        at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2698)
        at androidx.fragment.app.FragmentStateManager.ensureInflatedView(FragmentStateManager.java:218)
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1183)
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1356)
        at androidx.fragment.app.FragmentLayoutInflaterFactory.onCreateView(FragmentLayoutInflaterFactory.java:109)
        at androidx.fragment.app.FragmentController.onCreateView(FragmentController.java:135)
        at androidx.fragment.app.FragmentActivity.dispatchFragmentsOnCreateView(FragmentActivity.java:356)
        at androidx.fragment.app.FragmentActivity.onCreateView(FragmentActivity.java:335)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:780)
        at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:730)
        at android.view.LayoutInflater.rInflate(LayoutInflater.java:863)
        at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:515)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
        at android.view.LayoutInflater.inflate(LayoutInflater.java:374)
        at androidx.appcompat.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:469)
        at androidx.appcompat.app.AppCompatActivity.setContentView(AppCompatActivity.java:140)
        at com.example.foo.FooActivity.onCreate(FooActivity.kt:29)
        at android.app.Activity.performCreate(Activity.java:7149)
        at android.app.Activity.performCreate(Activity.java:7140)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1288)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3017)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3172)
2020-05-22 22:10:05.019 8599-8599/com.example.foo E/AndroidRuntime:     at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1906)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6863)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
     Caused by: java.lang.NoSuchMethodException: <init> [class android.app.Application]
        at java.lang.Class.getConstructor0(Class.java:2327)
        at java.lang.Class.getConstructor(Class.java:1725)
        at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:267)
            ... 40 more

最佳答案

问题是 by ActivityViewModels() 没有获得能够创建 View 模型的同一工厂。

应该是这样的:

class BlankFragment : Fragment() {
    private val viewModel: FooViewModel by activityViewModels {
        val application = requireActivity().application
        FooViewModelFactory(
            id,
            FooDatabase.getInstance(application).fooDao,
            application)
    }

编辑:或者只提供 Activity 中的工厂实例。无论哪种方式,您都需要它来可靠地获取 FooViewModel 实例。

class FooActivity : AppCompatActivity() {

    val viewModelFactory by lazy { 
        FooViewModelFactory(
            intent.getLongExtra(FOO_ID, 1L),
            FooDatabase.getInstance(application).fooDao,
            application)
    }

    private val viewModel: FooViewModel by viewModels { viewModelFactory }

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_foo)
    }
}

还有

class BlankFragment : Fragment() {
    private val viewModel: FooViewModel by activityViewModels {
        (requireActivity() as FooActivity).viewModelFactory
    }

关于android - 无法使用 ViewModelFactory 实例化我的 ViewModel,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61963285/

相关文章:

java - Android 数据绑定(bind)导致包含布局出现问题

java - android - httpsurlconnection 'get' 405 - 方法不允许

java - 窗口 View 泄露

Android Studio 北极狐 : after update gradle error

Kotlin,减少重复代码

android - 如何正确地将变量放入 url 中。改造2.0

android - 如何使 flutter 应用程序响应?

kotlin - 在Kotlin中,如何获取数组的前n个元素

android - 如何将微调器选择的值绑定(bind)到 arraylist 中的值

android - 如何抑制由未能生成的类引起的代码生成错误?