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

Kotlin Android+Capacitor跨类更新Presentation视图的最佳设计模式

解决Capacitor插件更新Android副屏Presentation视图的合理方案

1. 让Activity接管Presentation的生命周期

Presentation实例得交给主Activity来创建和管理,不能让Capacitor插件直接持有——毕竟Presentation依赖显示设备和宿主Context,插件本身是无状态的,硬拿Context属于反模式,还容易搞出内存泄漏。

先在你的主Activity里加副屏监听和Presentation管理逻辑:

class MainActivity : BridgeActivity() {
    private var secondaryScreenPresentation: SecondaryScreenPresentation? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setupSecondaryScreenListener()
    }

    private fun setupSecondaryScreenListener() {
        val displayManager = getSystemService(DISPLAY_SERVICE) as DisplayManager
        displayManager.registerDisplayListener(object : DisplayManager.DisplayListener {
            override fun onDisplayAdded(displayId: Int) {
                val display = displayManager.getDisplay(displayId)
                // 排除主屏幕,只处理副屏
                if (display != null && display.displayId != Display.DEFAULT_DISPLAY) {
                    secondaryScreenPresentation = SecondaryScreenPresentation(this@MainActivity, display)
                    secondaryScreenPresentation?.show()
                }
            }

            override fun onDisplayRemoved(displayId: Int) {
                secondaryScreenPresentation?.dismiss()
                secondaryScreenPresentation = null
            }

            override fun onDisplayChanged(displayId: Int) {}
        }, Handler(Looper.getMainLooper()))
    }

    // 对外提供更新副屏数据的方法
    fun updateSecondaryScreenCart(transaction: Transaction) {
        secondaryScreenPresentation?.updateCartItems(transaction.items)
    }
}

然后写Presentation类,内部处理RecyclerView和Adapter:

class SecondaryScreenPresentation(context: Context, display: Display) : Presentation(context, display) {
    private lateinit var cartItemAdapter: CartItemAdapter
    private val cartItems = mutableListOf<CartItem>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.secondary_screen_layout) // 这个布局里要包含RecyclerView

        val recyclerView = findViewById<RecyclerView>(R.id.cart_recycler_view)
        recyclerView.layoutManager = LinearLayoutManager(context)
        cartItemAdapter = CartItemAdapter(context, cartItems)
        recyclerView.adapter = cartItemAdapter
    }

    // 更新购物车数据并刷新Adapter
    fun updateCartItems(newItems: List<CartItem>) {
        cartItems.clear()
        cartItems.addAll(newItems)
        cartItemAdapter.notifyDataSetChanged()
    }
}

2. 在Capacitor插件中安全调用Activity的更新方法

插件里可以通过getBridge().getActivity()拿到当前的主Activity,强转后直接调用更新方法就行,注意必须在主线程操作UI:

@PluginMethod
fun transactionChange(call: PluginCall) {
    val itemsJson = call.getString("items") ?: run {
        call.reject("缺少items参数")
        return
    }

    try {
        val transaction = Json.decodeFromString<Transaction>(itemsJson)
        val mainActivity = getBridge().getActivity() as? MainActivity ?: run {
            call.reject("无法获取MainActivity实例")
            return
        }

        // 切到主线程更新UI
        Handler(Looper.getMainLooper()).post {
            mainActivity.updateSecondaryScreenCart(transaction)
            val ret = JSObject()
            ret.put("success", true)
            call.resolve(ret)
        }
    } catch (e: Exception) {
        call.reject("解析交易数据失败: ${e.message}")
    }
}

3. 进阶优化:用LiveData实现解耦更新

如果想让组件之间更解耦,不用直接调用方法,可以用LiveData做数据驱动:

在MainActivity里加一个MutableLiveData:

class MainActivity : BridgeActivity() {
    private val transactionLiveData = MutableLiveData<Transaction?>()
    private var secondaryScreenPresentation: SecondaryScreenPresentation? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setupSecondaryScreenListener()
        observeTransactionUpdates()
    }

    private fun observeTransactionUpdates() {
        transactionLiveData.observe(this) { transaction ->
            transaction?.let {
                secondaryScreenPresentation?.updateCartItems(it.items)
            }
        }
    }

    fun updateSecondaryScreenCart(transaction: Transaction) {
        transactionLiveData.postValue(transaction)
    }

    // 副屏监听代码不变...
}

然后Presentation里直接观察这个LiveData,不用依赖Activity的方法调用:

class SecondaryScreenPresentation(context: Context, display: Display) : Presentation(context, display) {
    private lateinit var cartItemAdapter: CartItemAdapter
    private val cartItems = mutableListOf<CartItem>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.secondary_screen_layout)

        val recyclerView = findViewById<RecyclerView>(R.id.cart_recycler_view)
        recyclerView.layoutManager = LinearLayoutManager(context)
        cartItemAdapter = CartItemAdapter(context, cartItems)
        recyclerView.adapter = cartItemAdapter

        // 观察MainActivity的LiveData,自动更新数据
        (context as? MainActivity)?.transactionLiveData?.observe(this) { transaction ->
            transaction?.let {
                updateCartItems(it.items)
            }
        }
    }

    // updateCartItems方法不变...
}

为什么不能用伴生方法?

伴生方法(静态方法)持有Context会导致内存泄漏——静态引用会一直攥着Context实例,哪怕Activity已经销毁了。让Activity持有Presentation实例,跟着Activity的生命周期创建/销毁Presentation,既符合Android的组件规范,又能避免内存泄漏问题。

内容的提问来源于stack exchange,提问作者Zach Wright

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 20:05:36