Dagger 上的 ViewModel 注入失败 +科特林安卓
class MovieListFragment : Fragment() {
@Inject
lateinit var movieListView: MovieListViewModel
private lateinit var movieListAdapter: MovieListAdapter
private lateinit var binding: ListFragmentBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
DaggerMovieComponent.builder().appComponent(MovieListApp.component()).fragmentModule(FragmentModule(this)).build().inject(this)
}
这是我试图注入ViewModel的班级。
@Module (includes = [FragmentModule::class])
class MovieListModule(fragment: Fragment) {
private lateinit var movieListView : MovieListViewModel
@Provides
fun getMovieListViewModel(fragment: Fragment): MovieListViewModel {
movieListView = ViewModelProvider(fragment).get(MovieListViewModel::class.java)
return movieListView
}
} 这是具有模块的类,最后,
@Singleton
@Component(modules = [MovieModule::class,MovieListModule::class], dependencies = [AppComponent::class]))
interface MovieComponent {
fun inject(movieListViewModel : MovieListViewModel)
fun inject(movieDetailViewModel: MovieDetailViewModel)
fun inject(fragment : Fragment)
}
这是我的组件接口。
该应用程序崩溃了,说应该注入的后期视图模型不是初始化的。有办法解决吗?
先感谢您。
错误消息:
2022-03-30 15:41:40.749 18607-18607/com.example.polyapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.polyapp, PID: 18607
java.lang.RuntimeException: Unable to create application com.example.polyapp.MovieListApp: java.lang.IllegalStateException: com.example.polyapp.movieDatabaseFeature.di.AppComponent must be set
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7487)
at android.app.ActivityThread.access$1700(ActivityThread.java:310)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2283)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8641)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:567)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1133)
Caused by: java.lang.IllegalStateException: com.example.polyapp.movieDatabaseFeature.di.AppComponent must be set
at dagger.internal.Preconditions.checkBuilderRequirement(Preconditions.java:95)
at com.example.polyapp.movieDatabaseFeature.di.DaggerMovieComponent$Builder.build(DaggerMovieComponent.java:101)
at com.example.polyapp.MovieListApp.onCreate(MovieListApp.kt:15)
at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1211)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7482)
at android.app.ActivityThread.access$1700(ActivityThread.java:310)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2283)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8641)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:567)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1133)
class MovieListFragment : Fragment() {
@Inject
lateinit var movieListView: MovieListViewModel
private lateinit var movieListAdapter: MovieListAdapter
private lateinit var binding: ListFragmentBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
DaggerMovieComponent.builder().appComponent(MovieListApp.component()).fragmentModule(FragmentModule(this)).build().inject(this)
}
This is the class I'm trying to have my viewmodel injected.
@Module (includes = [FragmentModule::class])
class MovieListModule(fragment: Fragment) {
private lateinit var movieListView : MovieListViewModel
@Provides
fun getMovieListViewModel(fragment: Fragment): MovieListViewModel {
movieListView = ViewModelProvider(fragment).get(MovieListViewModel::class.java)
return movieListView
}
}
This is the class that has the module and lastly,
@Singleton
@Component(modules = [MovieModule::class,MovieListModule::class], dependencies = [AppComponent::class]))
interface MovieComponent {
fun inject(movieListViewModel : MovieListViewModel)
fun inject(movieDetailViewModel: MovieDetailViewModel)
fun inject(fragment : Fragment)
}
This is my component interface.
The app crashes, saying that the lateinit viewmodel that was supposed to be injected is not initialised. Is there a way around this?
Thank you in advance.
The error message:
2022-03-30 15:41:40.749 18607-18607/com.example.polyapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.polyapp, PID: 18607
java.lang.RuntimeException: Unable to create application com.example.polyapp.MovieListApp: java.lang.IllegalStateException: com.example.polyapp.movieDatabaseFeature.di.AppComponent must be set
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7487)
at android.app.ActivityThread.access$1700(ActivityThread.java:310)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2283)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8641)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:567)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1133)
Caused by: java.lang.IllegalStateException: com.example.polyapp.movieDatabaseFeature.di.AppComponent must be set
at dagger.internal.Preconditions.checkBuilderRequirement(Preconditions.java:95)
at com.example.polyapp.movieDatabaseFeature.di.DaggerMovieComponent$Builder.build(DaggerMovieComponent.java:101)
at com.example.polyapp.MovieListApp.onCreate(MovieListApp.kt:15)
at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1211)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:7482)
at android.app.ActivityThread.access$1700(ActivityThread.java:310)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2283)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8641)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:567)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1133)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
Android上的ViewModel注入非常棘手,因为使用
ViewModelProvider
创建了ViewModels,以确保它们生存的配置更改。如果它们使用viewModelProvider
创建,那么如何用匕首创建它?幸运的是,他们俩都提供了可以融合在一起的API来解决您的问题。匕首具有多键,
viewModelProvider
有view> viewmodelprovider.factory
api。通过首先在地图上查找注射,可以使我们可以更细微的调整。viewModelProvider.factory
将告诉viewModelProvider
如何构造viewModel,该模型允许您指定构造函数参数。步骤和说明
为您的MovielistViewModel创建一个多插件。我们不想将您的MovielistViewModel直接注入片段中,而是希望将其包装在称为多重点的特殊匕首功能中。这将使您可以将您的MovielistViewModel放入可以在运行时注入并更细微操纵的地图(请记住
view> view> view> view> view> view> view>我提到的
factery ?)。创建
viewmodelprovider.factory
,该使用步骤2中提到的地图。这使用了一种特殊的匕首类型,称为provider
。提供商包装您的注入类型,并且在调用provider.get()
检索您的对象之前,请勿构造它。<
私人VAL ViewModelProviders:Map&lt; class&lt; out viewModel&gt;,提供商&lt; viewModel&gt;&gt;&gt;
):viewModelProvider.factory
{
覆盖有趣&lt; t:viewModel?&gt;创建(ModelClass:class&lt; t&gt;):t
{
返回viewModelProviders [modelClass]?get()作为t
}
}
在片段和活动中使用自定义
viewModelProvider.factory
。在
私有Lateinit var ViewModel:MovielistViewModel
@Inject lateinit var Factory:DaggerViewModelfactory
覆盖乐趣(Savedinstancestate:捆绑包?)
{
super.oncreate(savedinstancestate)
ViewModel = ViewModelProvider(this,Factory).get(MovielistViewModel :: class.java)
}
}
为了简单起见,如果
daggerviewModelfactory
返回modelsclass
,我没有丢下错误,但是您应该添加一个,以防万一您忘记将ViewModel绑定到多点。希望这会有所帮助。
ViewModel injection on Android is tricky because ViewModels are created using
ViewModelProvider
to ensure they survive configuration changes. If they're created withViewModelProvider
, then how do you create it with Dagger? Luckily they both provide API's that can mesh together to solve your problem.Dagger has Multibindings, and
ViewModelProvider
has it'sViewModelProvider.Factory
API. Multibindings allow us to more finely tune when injection occurs by looking it up on a map first. TheViewModelProvider.Factory
will tell theViewModelProvider
how to construct your ViewModel which allows for you to specify constructor parameters.Here are the steps and explanations:
Annotate your MovieListViewModel constructor with @Inject. This will tell Dagger to put your MovieListViewModel on its' graph provided it can satisfy the constructor parameters(we won't be injecting it directly, we just need it on the Dagger graph). If there are no parameters, Dagger will handle it just fine.
Create a Multibinding for your MovieListViewModel. Instead of directly injecting your MovieListViewModel into the fragment, we want to wrap it in a special Dagger feature called a Multibinding. This will allow you to put your MovieListViewModel into a Map which can be injected and more finely manipulated at runtime(remember that
ViewModelProvider.Factory
I mentioned?).Create a
ViewModelProvider.Factory
that injects and uses the Map mentioned in step 2. This uses a special Dagger type called aProvider
. Providers wrap your injected type and do not construct it until you callProvider.get()
to retrieve your object.Use your custom
ViewModelProvider.Factory
in your Fragments and Activities.For the sake of simplicity, I did not throw an error if the
DaggerViewModelFactory
returns null for themodelClass
, but you should add one in case you forget to bind your ViewModel into the Multibinding.Hope this helps.