无法使用ViewModelFactory实例化我的ViewModel。

问题描述 投票:0回答:1

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

原因是:java.lang.RuntimeException: 无法创建类 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)    
    }
}

Exception occurs when the viewModel is instantiated at the time the Fragment attempts to bind it, see comment line of code below. 异常发生在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
android kotlin android-databinding
1个回答
1
投票

问题是 by activityViewModels() 并没有得到相同的工厂,将能够创建你的viewmodel。

应该是这样的。

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

EDIT: 或者直接从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
    }

0
投票

问题是 ViewModel by activityViewModels 委托试图在布局通过数据绑定膨胀时创建ViewModel。然而,我的活动类称为 setContentView 在实际创建ViewModelFactory(和viewModel)之前,因此在Fragment尝试获取ViewModel时,没有返回ViewModel。

解决方法是将调用 setContentView 到头来 Activity.onCreate() 职能。

© www.soinside.com 2019 - 2024. All rights reserved.