Kotlin中跨Activity的不同Fragment间数据共享问题
跨Activity的Fragment间数据共享解决方案
针对你描述的场景——MainActivity下的三个Fragment(Home/Cart/User),点击HomeFragment的产品跳转到独立Activity的ProductDetailFragment,点击「加入购物车」后同步更新CartFragment的列表——以下是几种更可靠的解决方案,替代当前的SharedPreferences临时方案:
一、推荐方案:Application级ViewModel + LiveData
之前你用LiveData失败,大概率是因为使用了Activity级ViewModel,不同Activity的ViewModel实例相互独立,导致跨Activity无法共享数据。改用Application作为ViewModel的宿主,就能实现全局共享:
1. 创建全局CartViewModel
class CartViewModel(application: Application) : AndroidViewModel(application) { // 维护购物车中的产品slug列表(避免重复添加) private val _cartSlugs = MutableLiveData<MutableList<String>>(mutableListOf()) val cartSlugs: LiveData<MutableList<String>> = _cartSlugs // 添加产品到购物车 fun addToCart(slug: String) { val currentList = _cartSlugs.value ?: mutableListOf() if (!currentList.contains(slug)) { currentList.add(slug) _cartSlugs.postValue(currentList) // 线程安全更新数据 } } // 可选:移除产品方法 fun removeFromCart(slug: String) { val currentList = _cartSlugs.value ?: mutableListOf() currentList.remove(slug) _cartSlugs.postValue(currentList) } }
2. 在ProductDetailFragment中调用添加逻辑
class ProductDetailFragment : Fragment() { // 获取Application级别的ViewModel实例 private val cartViewModel: CartViewModel by viewModels({ requireActivity().application }) // 「加入购物车」按钮点击事件 private fun handleAddToCart(slug: String) { cartViewModel.addToCart(slug) Toast.makeText(context, "已加入购物车", Toast.LENGTH_SHORT).show() } }
3. 在CartFragment中监听数据变化并更新UI
class CartFragment : Fragment() { private val cartViewModel: CartViewModel by viewModels({ requireActivity().application }) private lateinit var productAdapter: ProductAdapter private val productList = mutableListOf<Product>() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) initGridLayout() // 监听购物车slug列表变化,同步拉取产品详情 cartViewModel.cartSlugs.observe(viewLifecycleOwner) { slugs -> productList.clear() slugs.forEach { slug -> lifecycleScope.launch { try { // 调用你的Retrofit接口获取产品详情 val product = yourProductApi.getProductBySlug(slug) productList.add(product) productAdapter.notifyItemInserted(productList.size - 1) } catch (e: Exception) { Log.e("CartFragment", "拉取产品失败: ${e.message}") } } } // 显示空购物车状态(如果需要) emptyCartView.visibility = if (slugs.isEmpty()) View.VISIBLE else View.GONE } } private fun initGridLayout() { productAdapter = ProductAdapter(productList) recyclerView.apply { layoutManager = GridLayoutManager(context, 2) adapter = productAdapter } } }
二、进阶方案:DataStore替换SharedPreferences
DataStore是Google官方推荐的SharedPreferences替代方案,解决了SP的主线程阻塞、数据一致性差等问题,且支持Flow异步观察数据变化:
1. 创建CartDataStore封装类
class CartDataStore(private val context: Context) { // 初始化DataStore private val Context.dataStore by preferencesDataStore(name = "cart_store") private val CART_SLUGS = stringSetPreferencesKey("cart_slugs") // 添加slug到购物车 suspend fun addSlug(slug: String) { context.dataStore.edit { prefs -> val currentSlugs = prefs[CART_SLUGS] ?: emptySet() prefs[CART_SLUGS] = currentSlugs + slug } } // 获取购物车slug列表(返回Flow,支持实时监听) fun getCartSlugs(): Flow<Set<String>> { return context.dataStore.data .map { prefs -> prefs[CART_SLUGS] ?: emptySet() } } // 可选:移除slug方法 suspend fun removeSlug(slug: String) { context.dataStore.edit { prefs -> val currentSlugs = prefs[CART_SLUGS] ?: emptySet() prefs[CART_SLUGS] = currentSlugs - slug } } }
2. 在Application中初始化单例
class MyApp : Application() { lateinit var cartDataStore: CartDataStore private set override fun onCreate() { super.onCreate() cartDataStore = CartDataStore(this) } }
3. 在ProductDetailFragment中使用
class ProductDetailFragment : Fragment() { private val cartDataStore by lazy { (requireContext().applicationContext as MyApp).cartDataStore } private fun handleAddToCart(slug: String) { lifecycleScope.launch { cartDataStore.addSlug(slug) Toast.makeText(context, "已加入购物车", Toast.LENGTH_SHORT).show() } } }
4. 在CartFragment中监听数据
class CartFragment : Fragment() { private val cartDataStore by lazy { (requireContext().applicationContext as MyApp).cartDataStore } private lateinit var productAdapter: ProductAdapter private val productList = mutableListOf<Product>() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) initGridLayout() // 收集Flow数据变化 lifecycleScope.launch { cartDataStore.getCartSlugs().collect { slugs -> productList.clear() slugs.forEach { slug -> launch { try { val product = yourProductApi.getProductBySlug(slug) productList.add(product) productAdapter.notifyItemInserted(productList.size - 1) } catch (e: Exception) { Log.e("CartFragment", "拉取产品失败: ${e.message}") } } } emptyCartView.visibility = if (slugs.isEmpty()) View.VISIBLE else View.GONE } } } }
方案对比
| 方案 | 优点 | 缺点 |
|---|---|---|
| Application级ViewModel | 实现简单、实时响应、生命周期感知 | 进程被杀后数据丢失(需结合持久化) |
| DataStore | 异步安全、持久化、类型安全 | 代码量略多,需熟悉Flow协程 |
| 当前SharedPreferences | 上手快 | 主线程阻塞、数据一致性差、易出现ANR |
如果需要持久化ViewModel的数据,可以结合DataStore,在ViewModel初始化时从DataStore读取历史数据,更新LiveData,这样进程重启后购物车数据不会丢失。
内容的提问来源于stack exchange,提问作者Kaan Düzbastılar
相关产品推荐
相关产品推荐

