You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Dagger初始化ViewModel时遇未初始化异常,求解决方案

Dagger框架中Fragment未初始化ViewModel导致运行时异常

核心问题

运行时抛出kotlin.UninitializedPropertyAccessException,提示lateinit属性stringViewModel未初始化,进而引发Fragment加载失败,最终导致Activity启动异常。

完整报错日志

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>

问题原因及解决方案

问题根源

  1. StringFragment未执行注入逻辑:Fragment的onAttach方法中注入代码被注释,导致stringViewModel从未被初始化。
  2. Activity初始化顺序错误:MainActivity中先调用setContentView加载布局(此时Fragment会被实例化并执行onCreateView),之后才执行自身的注入,即使Fragment有注入逻辑,也会因为Activity还未完成注入而无法获取Component。

修复步骤

1. 修复StringFragment的注入逻辑

取消onAttach方法中的注释,调用AppComponent注入Fragment:

override fun onAttach(context: Context) {
    super.onAttach(context)
    // 从Context获取AppComponent并注入当前Fragment
    context.appComponent.inject(this)
}

2. 调整MainActivity的初始化顺序

先执行注入,再加载布局,确保Component在Fragment实例化前已准备好:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // 先注入,再加载布局
    appComponent.inject(this)
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
}

3. 额外优化(可选)

ViewModel通常建议通过ViewModelProvider结合Dagger来注入,避免直接依赖Dagger注入ViewModel(因为ViewModel有自己的生命周期管理)。可以修改AppModule提供ViewModel的方式,或者使用ViewModelFactory:

// 示例:创建ViewModelFactory
class StringViewModelFactory @Inject constructor(
    private val stringUseCase: StringUseCase
) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(StringViewModel::class.java)) {
            @Suppress("UNCHECKED_CAST")
            return StringViewModel(stringUseCase) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

然后在Fragment中通过ViewModelProvider获取:

private val stringViewModel: StringViewModel by viewModels {
    (requireContext().appComponent as AppComponent).provideStringViewModelFactory()
}

(需要在AppComponent中添加fun provideStringViewModelFactory(): StringViewModelFactory方法)

内容的提问来源于stack exchange,提问作者Semenov Nilolay

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 12:20:28