Android中通过Dagger依赖注入创建Fragment的实现方案咨询
合理性评估
你当前的实现存在明显的不合理性,核心问题如下:
- 不符合Android Fragment的生命周期规范:系统在Fragment重建(如屏幕旋转、进程杀死恢复)时会反射调用无参构造方法,你手动通过构造传入ViewModel的方式,在重建场景下会出现ViewModel为空的崩溃问题。
- ViewModel传递逻辑错误:Activity级别的共享ViewModel不需要手动传递给Fragment,Fragment可直接通过ViewModelProvider从宿主Activity获取,手动传参属于多余操作,还会增加状态丢失风险。
- Dagger使用逻辑混乱:你同时给Fragment加了
@Inject构造、给Activity的ViewModel加了@Inject注解,却又手动调用Dagger组件创建ViewModel实例、手动调用newInstance创建Fragment,完全没有用到Dagger的依赖注入能力,属于冗余设计。 - Activity中重复创建Dagger组件:多次调用
DaggerRetroComponent.create()会生成多个独立的组件实例,无法保证单例依赖的全局唯一性。
Dagger注入实现方案
完全可以通过Dagger实现该逻辑,改造后可以解决上述所有问题,改造步骤如下:
1. 调整Dagger配置
新增Android注入相关依赖,在Dagger Component中引入AndroidInjectionModule,配置Activity、Fragment的注入绑定,同时通过ViewModelFactory提供ViewModel实例。
示例配置:
@Singleton @Component(modules = [ AndroidInjectionModule::class, AndroidSupportInjectionModule::class, ViewModelModule::class, FragmentModule::class ]) interface AppComponent : AndroidInjector<MyApplication> { @Component.Factory interface Factory { fun create(@BindsInstance context: Context): AppComponent } } // Fragment绑定Module @Module abstract class FragmentModule { @ContributesAndroidInjector abstract fun contributeCreditScoreFragment(): CreditScoreFragment } // ViewModel提供Module @Module abstract class ViewModelModule { @Binds abstract fun bindViewModelFactory(factory: DaggerViewModelFactory): ViewModelProvider.Factory @Binds @IntoMap @ViewModelKey(MainActivityViewModel::class) abstract fun bindMainActivityViewModel(viewModel: MainActivityViewModel): ViewModel }
2. 改造MainActivity
移除手动创建ViewModel、手动创建Fragment的逻辑,统一走Dagger注入:
class MainActivity : AppCompatActivity() { @Inject lateinit var viewModel: MainActivityViewModel @Inject lateinit var creditScoreFragment: CreditScoreFragment override fun onCreate(savedInstanceState: Bundle?) { // 注入要放在super.onCreate之前 AndroidInjection.inject(this) super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) setupFragment() } private fun setupFragment() { // 仅首次创建时添加Fragment,避免配置变更后重复添加 if (supportFragmentManager.findFragmentById(android.R.id.content) == null) { supportFragmentManager.beginTransaction() .replace(android.R.id.content, creditScoreFragment) .commit() } } }
3. 改造CreditScoreFragment
移除构造参数的ViewModel、移除newInstance方法,直接从宿主Activity获取共享ViewModel:
class CreditScoreFragment @Inject constructor() : Fragment() { private var _binding: FragmentCreditScoreBinding? = null private val binding get() = _binding!! // 直接从宿主Activity获取共享ViewModel,无需手动传参 private val viewModel: MainActivityViewModel by viewModels({ requireActivity() }) override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentCreditScoreBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) initViewModel() initView() } private fun initViewModel() { viewModel.getCreditReportObserver().observe(viewLifecycleOwner, Observer<CreditReport> { showScoreUI(true) binding.score.text = it.creditReportInfo.score.toString() binding.maxScoreValue.text = "out of ${it.creditReportInfo.maxScoreValue}" initDonutView( it.creditReportInfo.score.toFloat(), it.creditReportInfo.maxScoreValue.toFloat() ) }) viewModel.getServerErrorLiveDataObserver().observe(viewLifecycleOwner, Observer<Boolean> { if(it) { showScoreUI(false) showToastMessage() } }) viewModel.getCreditReport() } private fun initView() { binding.btnRefresh.setOnClickListener { viewModel.getCreditReport() } } private fun initDonutView(score: Float, maxScoreValue: Float) { val section1 = DonutSection( name = "section_1", color = Color.parseColor("#FB1D32"), amount = score ) binding.donutView.cap = maxScoreValue binding.donutView.submitData(listOf(section1)) } fun showScoreUI(show: Boolean) { binding.yourCreditScore.isVisible = show binding.maxScoreValue.isVisible = show binding.donutView.isVisible = show binding.score.isVisible = show } fun showToastMessage() { Toast.makeText(context, "Error connecting to server", Toast.LENGTH_LONG).show() } override fun onDestroyView() { super.onDestroyView() _binding = null } }
简化替代方案(可选)
如果项目可以接入Dagger Hilt,实现会更简洁:
- 给Application、MainActivity、CreditScoreFragment都加
@AndroidEntryPoint注解 - 给MainActivityViewModel加
@HiltViewModel注解 - 不需要额外配置Dagger组件、Module,直接在Activity和Fragment中通过
by viewModels()获取ViewModel即可,不需要手动注入Fragment。
内容的提问来源于stack exchange,提问作者BRDroid
相关产品推荐
相关产品推荐

