我无法将ViewModel绑定到片段(使用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尝试绑定viewModel时实例化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
答案 0 :(得分:2)
问题是by activityViewModels()
的工厂不能与创建视图模型的工厂相同。
应该是这样的:
class BlankFragment : Fragment() {
private val viewModel: FooViewModel by activityViewModels {
val application = requireActivity().application
FooViewModelFactory(
id,
FooDatabase.getInstance(application).fooDao,
application)
}
编辑:或者仅从“活动”中提供工厂实例。无论哪种方式,您都将需要它来可靠地获取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
}
答案 1 :(得分:0)
问题是ViewModel by activityViewModels
委托试图通过数据绑定扩大布局时创建ViewModel。但是,在实际创建ViewModelFactory(和viewModel)之前,我的活动类名为setContentView
,因此在Fragment尝试获取它时没有ViewModel返回。
解决方案是将调用setContentView
移动到Activity.onCreate()
函数的末尾。