核心问题
在 Fragment 中使用 ComposeView 时,Compose 代码中调用 viewModel() 获取的 ViewModel,其 Owner 是谁?
结论
Owner 就是该 Fragment 本身
详细分析
1. Fragment 自动设置 ViewTreeViewModelStoreOwner
在 AndroidX Fragment 库中,Fragment 会在创建 View 时自动设置:
csharp
// Fragment.java (AndroidX Fragment)
void performCreateView(...) {
mViewLifecycleOwner = new ViewLifecycleOwner(this);
// 关键:设置 ViewTreeViewModelStoreOwner
ViewTreeLifecycleOwner.set(mView, mViewLifecycleOwner);
ViewTreeViewModelStoreOwner.set(mView, mViewLifecycleOwner);
// mViewLifecycleOwner 实际上就是 Fragment 自己
}
这意味着:Fragment 的整个 View hierarchy 都能通过 ViewTree 找到 Fragment。
2. ComposeView 如何找到 ViewModelStoreOwner
AndroidX Compose UI 的实现:
kotlin
// AbstractComposeView.kt
public abstract class AbstractComposeView : ViewGroup {
internal val viewModelStoreOwner: ViewModelStoreOwner? by lazy {
findViewTreeViewModelStoreOwner() // 核心:遍历 View Tree 查找
}
}
findViewTreeViewModelStoreOwner() 方法的实现原理:
java
// ViewTreeViewModelStoreOwner.java
public static ViewModelStoreOwner get(View view) {
// 向上遍历 View Tree
ViewParent parent = view.getParent();
while (parent != null) {
if (parent instanceof ViewModelStoreOwner) {
return (ViewModelStoreOwner) parent;
}
// 检查 View 的 tag
if (parent instanceof View) {
Object tag = ((View) parent).getTag(R.id.view_tree_view_model_store_owner);
if (tag instanceof ViewModelStoreOwner) {
return (ViewModelStoreOwner) tag;
}
}
parent = parent.getParent();
}
return null;
}
3. ComposeView.setContent() 设置 LocalViewModelStoreOwner
当调用 ComposeView.setContent() 时,内部会:
kotlin
public fun setContent(content: @Composable () -> Unit) {
val owner = findViewTreeViewModelStoreOwner() // 找到 Fragment
CompositionContext {
providers(LocalViewModelStoreOwner provides owner) {
content() // 你的 Compose 代码在这里执行
}
}
}
4. viewModel() 如何使用 LocalViewModelStoreOwner
kotlin
// Compose ViewModel.kt
@Composable
public inline fun <reified VM : ViewModel> viewModel(
viewModelStoreOwner: ViewModelStoreOwner = checkNotNull(LocalViewModelStoreOwner.current) {
"No ViewModelStoreOwner was provided via LocalViewModelStoreOwner"
},
key: String? = null,
factory: ViewModelProvider.Factory? = null
): VM = viewModel(VM::class, viewModelStoreOwner, key, factory)
默认参数 LocalViewModelStoreOwner.current 就是之前 setContent() 设置的 Fragment。
完整调用链
markdown
1. Fragment.performCreateView()
↓
2. Fragment 设置 ViewTreeViewModelStoreOwner = Fragment
↓
3. inflater.inflate() 创建 ComposeView
↓
4. composeView.setContent { YourScreen() }
↓
5. setContent 内部调用 findViewTreeViewModelStoreOwner()
↓
6. 找到 Fragment(因为 Fragment 设置了 ViewTree tag)
↓
7. 设置 CompositionLocal: LocalViewModelStoreOwner = Fragment
↓
8. YourScreen() 执行
↓
9. viewModel() 读取 LocalViewModelStoreOwner.current
↓
10. 得到 Fragment
↓
11. 从 Fragment 的 ViewModelStore 获取 ViewModel
实际应用
在 Fragment 中
kotlin
class MyFragment : Fragment() {
private val myViewModel by viewModels<MyViewModel>()
override fun onCreateView(...): View {
return ComposeView(requireContext()).apply {
setContent {
MyScreen()
}
}
}
}
在 Compose 中
kotlin
@Composable
fun MyScreen() {
val myViewModel: MyViewModel = viewModel()
// 这里的 myViewModel 和 Fragment 中的 myViewModel 是同一个实例
}
关键要点
-
Fragment 和 Compose 共享同一个 ViewModel ****实例
- Fragment 中的
viewModels() - Compose 中的
viewModel() - 使用相同的 ViewModelStoreOwner(Fragment)
- Fragment 中的
-
数据状态保持一致
- 在 Fragment 生命周期内,ViewModel 数据不会丢失
- 配置更改(如屏幕旋转)时数据自动恢复
-
CompositionLocal 的自动设置
- 无需手动设置
LocalViewModelStoreOwner - AndroidX 框架自动完成所有设置
- 无需手动设置
扩展:如果是 Activity 中的 ComposeView?
同样的原理:
- Activity 实现了
ViewModelStoreOwner接口 findViewTreeViewModelStoreOwner()会找到 ActivityLocalViewModelStoreOwner.current= Activity