我尝试学习如何使用Dagger2。请提供以下例外帮助:
例外: UninitializedPropertyAccessException:lateinit属性旅行尚未 已初始化
MainActivityViewModel:
class MainActivityViewModel : ViewModel() {
private lateinit var tripsLiveData: MutableLiveData<List<Trip>>
@Inject
lateinit var trips : List<Trip>
fun getTrips() : LiveData<List<Trip>> {
if (!::tripsLiveData.isInitialized){
tripsLiveData = MutableLiveData()
tripsLiveData.value = trips
}
return tripsLiveData
}
}
TripModule:
@Module
class TripModule{
@Provides
fun provideTrips(): List<Trip> {
var list = ArrayList<Trip>()
list.add(Trip(100,10))
list.add(Trip(200,20))
return list
}
}
AppComponent:
@Singleton
@Component(modules = [
AndroidSupportInjectionModule::class,
ActivityBuilder::class,
TripModule::class])
interface AppComponent{
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
fun inject(app: MyApplication)
}
MainActivity:
class MainActivity : AppCompatActivity() {
@Inject
lateinit var tripsAdapter: TripsAdapter
override fun onCreate(savedInstanceState: Bundle?) {
// Inject external dependencies
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupRecyclerView();
setUpViewModel();
}
private fun setupRecyclerView() {
recycler_view.apply {
layoutManager = LinearLayoutManager(context)
adapter = tripsAdapter
}
}
private fun setUpViewModel(){
val model = ViewModelProviders.of(this).get(MainActivityViewModel::class.java)
model.getTrips().observe(this, Observer { tripsAdapter.trips = it!! })
}
}
答案 0 :(得分:1)
如果要让视图模型成为dagger图的一部分,则需要做几件事-使用dagger的多重绑定(只需一次,对于较新的视图模型,会更容易)。您将创建一个新的viewmodel工厂,该工厂将负责实例化viewmodel。该工厂将成为匕首图的一部分,因此将引用通过匕首提供的任何内容。然后,您可以通过@Inject constructor(anyParameterFromDagger: Param)
或@Inject lateinit var someParam: Param
在视图模型主体中进行构造函数注入。
1)为视图模型类创建限定符
@MustBeDocumented
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
@MapKey
annotation class ViewModelKey(val value: KClass<out ViewModel>)
2)创建一个viewmodel工厂,该工厂从dagger的多重绑定中获取值
@Singleton
class DaggerViewModelFactory @Inject constructor(
private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(modelClass: Class<T>): T {
var creator: Provider<out ViewModel>? = creators[modelClass]
if (creator == null) {
for ((key, value) in creators) {
if (modelClass.isAssignableFrom(key)) {
creator = value
break
}
}
}
if (creator == null) {
throw IllegalArgumentException("unknown model class $modelClass")
}
try {
return creator.get() as T
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
3)具有dagger模块,该模块将提供工厂(从第2点开始),然后提供您的视图模型
abstract class YourDaggerModuleWhichThenNeedToBePartOfYourGraphAsIncluded {
@Binds
abstract fun bindViewModelFactory(factory: DaggerViewModelFactory): ViewModelProvider.Factory // this needs to be only one for whole app (therefore marked as `@Singleton`)
@Binds
@IntoMap
@ViewModelKey(MainActivityViewModel::class)
abstract fun bindMainActivityViewModel(vm: MainActivityViewModel): ViewModel // for every viewmodel you have in your app, you need to bind them to dagger
}
4)在活动中,当您获取视图模型时,您需要使用匕首中的工厂:(在以下代码中,位置更改为标记为// TODO
的地方)
class MainActivity : AppCompatActivity() {
@Inject
lateinit var tripsAdapter: TripsAdapter
@Inject
lateinit var viewModelFactory: ViewModelProvider.Factory // TODO this was added to the activity
override fun onCreate(savedInstanceState: Bundle?) {
// Inject external dependencies
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupRecyclerView();
setUpViewModel();
}
private fun setupRecyclerView() {
recycler_view.apply {
layoutManager = LinearLayoutManager(context)
adapter = tripsAdapter
}
}
private fun setUpViewModel(){
val model = ViewModelProviders.of(this, viewModelFactory)[MainActivityViewModel::class.java] // TODO this was changed
model.getTrips().observe(this, Observer { tripsAdapter.trips = it!! })
}
}
我没有提供将模块添加到dagger组件的代码,因为我希望这是您已经做过的事情。
您可以阅读有关此内容的更多信息,例如在此medium article中(我不是本文的作者):
答案 1 :(得分:0)
@Provides fun provideTrips(): List<Trip> {
从某种意义上说,我有点警惕,我怀疑直接为您提供此服务确实是Dagger的工作,但我只是认为这是理所当然的事情,而现在忽略它。
您的代码应为:
class MainActivityViewModel @Inject constructor(
trips: List<Trip>
): ViewModel() {
private val tripsLiveData: MutableLiveData<List<Trip>> = MutableLiveData()
init {
tripsLiveData.setValue(trips)
}
fun getTrips() : LiveData<List<Trip>> = tripsLiveData
}
@Module
class TripModule{
@Provides
// @Singleton // <-- possibly should be here?
fun provideTrips(): List<Trip> = listOf(
Trip(100,10),
Trip(200,20)
)
}
@Singleton
@Component(modules = [
AndroidSupportInjectionModule::class,
ActivityBuilder::class,
TripModule::class
])
interface AppComponent{
@Component.Builder
interface Builder {
@BindsInstance
fun application(application: Application): Builder
fun build(): AppComponent
}
fun inject(app: MyApplication)
}
class MainActivity : AppCompatActivity() {
@Inject
lateinit var viewModelProvider: Provider<MainActivityViewModel>
// this is specifically not marked with `@Inject`
lateinit var viewModel: MainActivityViewModel
private val tripsAdapter = TripsAdapter()
override fun onCreate(savedInstanceState: Bundle?) {
// Inject external dependencies
AndroidInjection.inject(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setupRecyclerView();
setUpViewModel();
}
private fun setupRecyclerView() {
recycler_view.apply {
layoutManager = LinearLayoutManager(context)
adapter = tripsAdapter
}
}
private fun setUpViewModel(){
viewModel = ViewModelProviders.of(this, object: ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
if(modelClass == MainActivityViewModel::class.java) {
@Suppress("UNCHECKED_CAST")
return viewModelProvider.get() as T
}
throw IllegalArgumentException("Unexpected argument: $modelClass")
}
}).get(MainActivityViewModel::class.java)
viewModel.getTrips().observe(this, Observer {
val trips = it ?: return@observe
tripsAdapter.trips = trips
})
}
}
同样,您应该使用构造函数注入,仅在实际需要时使用Provider<T>
从Dagger获取ViewModel,否则通过ViewModelProviders.Factory
对其进行初始化,以便实际上从Dagger获取ViewModel