【发布时间】:2020-05-22 20:24:03
【问题描述】:
我无法将 ViewModel 绑定到我的 Fragment(使用 lateinit),因为我在 Activity 中创建 ViewModel 的方式有误。我做错了什么?
原因:java.lang.RuntimeException:无法创建 com.example.foo.FooViewModel 类的实例
这是活动:
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 尝试绑定视图模型时实例化视图模型时发生异常,请参见下面注释的代码行:
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
【问题讨论】:
-
你能发布完整的堆栈跟踪吗?
-
@CommonsWare 堆栈跟踪现已发布。
-
堆栈跟踪表明它没有使用您的工厂。我怀疑您需要为您的
activityViewModels()电话 (activityViewModels { ... }) 提供工厂。 -
“你从哪里得到的?” -- 你引用的错误来自
androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory,而不是你的班级。 “文档表明 activityViewModels() 委托不接受任何参数”——I see an argument。 -
@CommonsWare 我知道了。问题是
ViewModel by activityViewModels在布局通过数据绑定膨胀时试图创建 ViewModel。但是,在声明 ViewModelFactory(和 viewModel)之前,我的活动类称为setContentView,因此在 Fragment 尝试执行数据绑定时没有返回 ViewModel。回溯的神秘程度让我大吃一惊。
标签: android kotlin android-databinding