我开始学习dagger了。我试图从ViewModel中获取字符串,出现错误:
FATAL EXCEPTION: main
Process: com.sem.daggersimple, PID: 7882
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.sem.daggersimple/com.sem.daggersimple.presentation.MainActivity}: android.view.InflateException: Binary XML file line #26 in com.sem.daggersimple:layout/activity_main: Binary XML file line #26 in com.sem.daggersimple:layout/activity_main: Error inflating class fragment
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3450)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3602)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2067)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7697)
...
Caused by: android.view.InflateException: Binary XML file line #26 in com.sem.daggersimple:layout/activity_main: Binary XML file line #26 in com.sem.daggersimple:layout/activity_main: Error inflating class fragment
Caused by: android.view.InflateException: Binary XML file line #26 in com.sem.daggersimple:layout/activity_main: Error inflating class fragment
Caused by: kotlin.UninitializedPropertyAccessException: lateinit property stringViewModel has not been initialized
at com.sem.daggersimple.presentation.StringFragment.getStringViewModel(StringFragment.kt:24)
at com.sem.daggersimple.presentation.StringFragment.onCreateView(StringFragment.kt:43)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2963)
...
at com.sem.daggersimple.presentation.MainActivity.onCreate(MainActivity.kt:20)
MainActivity
:
class MainActivity : AppCompatActivity() {
@Inject
lateinit var stringViewModel: StringViewModel
private var binding: ActivityMainBinding? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
appComponent.inject(this)
}
}
StringFragment
:
class StringFragment : Fragment() {
private var binding : FragmentStringBinding? = null
@Inject
lateinit var stringViewModel: StringViewModel
override fun onAttach(context: Context) {
super.onAttach(context)
// Grabs the registrationComponent from the Activity and injects this Fragment
// (activity as MainActivity).
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_string, container, false)
// stringViewModel.getString.
Log.d("stringViewModel","stringViewModel = " + stringViewModel.getString.get(0))
binding?.textView?.text = stringViewModel.getString
return binding?.root
}
}
StringRepository
:
class StringRepository @Inject constructor() {
override fun toString() : String = "какой-то текст"
}
StringUseCase
:
class StringUseCase @Inject constructor(private val repository: StringRepository) {
fun getString() : String {
return repository.toString()
}
}
StringViewModel
:
class StringViewModel @Inject constructor(private val stringUseCase: StringUseCase) : ViewModel() {
val getString = stringUseCase.getString()
}
App
:
class App : Application() {
lateinit var appComponent: AppComponent
override fun onCreate() {
super.onCreate()
appComponent = DaggerAppComponent.create()
}
}
val Context.appComponent: AppComponent
get() = when(this) {
is App -> appComponent
else -> this.applicationContext.appComponent
}
AppComponent
:
@Component(modules = [AppModule::class])
interface AppComponent {
fun inject(mainActivity: MainActivity)
fun inject(stringFragment: StringFragment)
}
AppModule
:
@Module
object AppModule {
@Provides
fun provideStringViewModel(stringUseCase: StringUseCase) : StringViewModel {
return StringViewModel(stringUseCase)
}
@Provides
fun provideStringUseCase(repository: StringRepository) : StringUseCase {
return StringUseCase(repository)
}
@Provides
fun provideStringRepository() : StringRepository {
return StringRepository()
}
}
activity_main.xml
:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".presentation.MainActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="@+id/framelayout"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<fragment
android:id="@+id/rd_fragment"
android:name="com.sem.daggersimple.presentation.StringFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
fragment_string.xml
:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".presentation.StringFragment">
<!-- TODO: Update blank fragment layout -->
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/hello_blank_fragment" />
</layout>
附言:我不使用Hilt
不能将var
用于延迟初始化委派属性。您应该使用val
。您可以使用其中任何一个来初始化视图模型。
val stringViewModel by viewModels<StringViewModel>()
val stringViewModel :StringViewModel by viewModels()