fix: synchronize webapp container state access
This commit is contained in:
@@ -9,12 +9,21 @@ data class Tab(val id: String, val url: String, val active: Boolean = false, val
|
|||||||
// 真实 WebView 导航(goBack/goForward/reload)留待 Task 13 集成时接入
|
// 真实 WebView 导航(goBack/goForward/reload)留待 Task 13 集成时接入
|
||||||
// Multi-tab state management (pure Kotlin data layer, WebView-free, JVM-testable)
|
// Multi-tab state management (pure Kotlin data layer, WebView-free, JVM-testable)
|
||||||
// Real WebView navigation (goBack/goForward/reload) is wired up in Task 13
|
// Real WebView navigation (goBack/goForward/reload) is wired up in Task 13
|
||||||
|
//
|
||||||
|
// 状态方法全部 @Synchronized:open/close/switchTo 写操作在主线程,listTabs/tabs 读操作
|
||||||
|
// 在 JavaBridge 线程,跨线程读写同一 mutableListOf,需用同一把锁串行,避免
|
||||||
|
// ConcurrentModificationException(尤其 tabs() 的 toList() 迭代与写操作互斥)
|
||||||
|
// All state methods are @Synchronized: open/close/switchTo write on the main thread
|
||||||
|
// while listTabs/tabs read on the JavaBridge thread; reading and writing the same
|
||||||
|
// mutableListOf across threads must serialize on a single lock to avoid
|
||||||
|
// ConcurrentModificationException (especially toList() iteration vs writes)
|
||||||
class WebAppContainer {
|
class WebAppContainer {
|
||||||
private val tabs = mutableListOf<Tab>()
|
private val tabs = mutableListOf<Tab>()
|
||||||
|
|
||||||
// 打开标签:已存在则激活它(不重复添加),否则新增并激活;name 记录展示名
|
// 打开标签:已存在则激活它(不重复添加),否则新增并激活;name 记录展示名
|
||||||
// Open a tab: if it already exists just activate it, otherwise add and activate;
|
// Open a tab: if it already exists just activate it, otherwise add and activate;
|
||||||
// name records the display title from the manifest
|
// name records the display title from the manifest
|
||||||
|
@Synchronized
|
||||||
fun open(id: String, url: String, name: String = "") {
|
fun open(id: String, url: String, name: String = "") {
|
||||||
val existing = tabs.find { it.id == id }
|
val existing = tabs.find { it.id == id }
|
||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
@@ -27,6 +36,7 @@ class WebAppContainer {
|
|||||||
|
|
||||||
// 关闭标签:若关闭后没有激活标签则激活第一个
|
// 关闭标签:若关闭后没有激活标签则激活第一个
|
||||||
// Close a tab: if none remain active, activate the first one
|
// Close a tab: if none remain active, activate the first one
|
||||||
|
@Synchronized
|
||||||
fun close(id: String) {
|
fun close(id: String) {
|
||||||
tabs.removeAll { it.id == id }
|
tabs.removeAll { it.id == id }
|
||||||
if (tabs.isNotEmpty() && tabs.none { it.active }) {
|
if (tabs.isNotEmpty() && tabs.none { it.active }) {
|
||||||
@@ -36,6 +46,7 @@ class WebAppContainer {
|
|||||||
|
|
||||||
// 切换激活标签:id 不存在时返回 false 且不改动状态(保持原激活标签)
|
// 切换激活标签:id 不存在时返回 false 且不改动状态(保持原激活标签)
|
||||||
// Switch the active tab: return false and keep current state when id is unknown
|
// Switch the active tab: return false and keep current state when id is unknown
|
||||||
|
@Synchronized
|
||||||
fun switchTo(id: String): Boolean {
|
fun switchTo(id: String): Boolean {
|
||||||
if (tabs.none { it.id == id }) return false
|
if (tabs.none { it.id == id }) return false
|
||||||
setActive(id)
|
setActive(id)
|
||||||
@@ -44,10 +55,12 @@ class WebAppContainer {
|
|||||||
|
|
||||||
// 返回标签列表副本(外部无法直接改动内部状态)
|
// 返回标签列表副本(外部无法直接改动内部状态)
|
||||||
// Return a defensive copy of the tab list
|
// Return a defensive copy of the tab list
|
||||||
|
@Synchronized
|
||||||
fun tabs(): List<Tab> = tabs.toList()
|
fun tabs(): List<Tab> = tabs.toList()
|
||||||
|
|
||||||
// 返回当前激活标签 id,无标签或未激活返回 null
|
// 返回当前激活标签 id,无标签或未激活返回 null
|
||||||
// Return the active tab id, or null when empty/none active
|
// Return the active tab id, or null when empty/none active
|
||||||
|
@Synchronized
|
||||||
fun activeTabId(): String? = tabs.firstOrNull { it.active }?.id
|
fun activeTabId(): String? = tabs.firstOrNull { it.active }?.id
|
||||||
|
|
||||||
private fun setActive(id: String) {
|
private fun setActive(id: String) {
|
||||||
|
|||||||
Reference in New Issue
Block a user