HiltViewModel注入BaseApplication失败问题排查
我有一个纯Java编写的遗留项目,当前需升级平台,引入Jetpack Compose与Hilt以支持Kotlin和依赖注入。
我创建了Kotlin类ComposeCompat处理Composable创建:
class ComposeCompat(private val app: Activity) { fun getListViewComposable(): ComposeView = app.findViewById<ComposeView>(R.id.compose_list).apply { setContent { val vm: ListViewModel = viewModel() ListView(vm) } } }
在MainActivity中如下调用该类,可正常访问Composable与无依赖的ListViewModel:
private final ComposeCompat compose = new ComposeCompat(this); protected void onCreate(Bundle savedInstanceState) { compose.getListViewComposable(); }
无依赖的HiltViewModel代码如下:
@HiltViewModel class ListViewModel @Inject constructor() : ViewModel() { val state: MutableState<State> = mutableStateOf(State.Loading) fun getState() = state.toString() }
对应的Composable可正常运行:
@Composable fun ListView(vm: ListViewModel) { Log.d("viewModel", "ViewModel starting") Log.d("viewModel", vm.getState()) }
但当为ListViewModel注入BaseApplication后:
@HiltViewModel class ListViewModel @Inject constructor(private val app: BaseApplication) : ViewModel() { val state: MutableState<State> = mutableStateOf(State.Loading) fun getState() = state.toString() }
出现如下错误:
Transmitting stack trace: java.lang.RuntimeException: Cannot create an instance of class com.example.compose.list_view.ListViewModel at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create(ViewModelProvider.kt:204)
我曾怀疑BaseApplication实现有问题,但另一个注入BaseApplication的DialFragmentViewModel在Java Fragment中可正常使用:
ViewModel代码:
@HiltViewModel class DialFragmentViewModel @Inject constructor(private val app: BaseApplication) : ViewModel() { fun onGetApplicationFunction() { app.isServiceRunning("com.example.Service") } }
在Java Fragment中调用方式:
private final DialFragmentViewModel viewModel = new ViewModelProvider(this).get(DialFragmentViewModel.class);
请问问题出在哪里?是否是ComposeCompat无法获取BaseApplication作为依赖注入?
核心问题
Compose中默认的viewModel()方法没有关联Hilt的ViewModelFactory,导致Hilt无法为ViewModel注入依赖。而Java Fragment能正常工作,是因为Hilt已为标注了@AndroidEntryPoint的Fragment自动配置了对应的Factory。
具体解决步骤
为MainActivity添加Hilt注解
确保你的MainActivity标注了@AndroidEntryPoint,让Hilt为其提供依赖注入支持:@AndroidEntryPoint public class MainActivity extends AppCompatActivity { private final ComposeCompat compose = new ComposeCompat(this); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); compose.getListViewComposable(); } }替换Compose中的ViewModel获取方法
将Compose中默认的viewModel()替换为Hilt提供的hiltViewModel(),该方法会自动关联Hilt的Factory,实现依赖注入:class ComposeCompat(private val app: Activity) { fun getListViewComposable(): ComposeView = app.findViewById<ComposeView>(R.id.compose_list).apply { setContent { // 改用hiltViewModel()获取带依赖的ViewModel val vm: ListViewModel = hiltViewModel() ListView(vm) } } }确认BaseApplication的Hilt配置
确保你的BaseApplication已标注@HiltAndroidApp,这是Hilt初始化的必要条件:@HiltAndroidApp class BaseApplication : Application() { // 你的其他业务代码 }
为什么Java Fragment能正常工作?
Hilt会为标注了@AndroidEntryPoint的Fragment自动注入自定义ViewModelProvider.Factory,当你调用new ViewModelProvider(this)时,实际使用的是Hilt提供的Factory,因此能正确创建带依赖的ViewModel。而Compose的默认viewModel()没有绑定这个Factory,必须显式使用hiltViewModel()触发Hilt的注入逻辑。
内容的提问来源于stack exchange,提问作者Chris Pi

