Skip to content

Commit d14a65d

Browse files
committed
💪 [kmp/ios] 一些稳定性的提升
1 parent e9b87ea commit d14a65d

17 files changed

Lines changed: 108 additions & 56 deletions

File tree

deno.jsonc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"workspace": [
2828
"./toolkit/dweb-core",
2929
"./toolkit/dweb-helper",
30+
"./toolkit/dweb-polyfill",
3031
"./toolkit/plaoc/server",
3132
"./toolkit/plaoc/plugins",
3233
"./toolkit/plaoc/cli",

next/kmp/app/iosApp/DwebBrowser/DwebBrowser/Desktop/DwebDeskVCStore.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class DwebVCData {
3131

3232
class func startUpNMMs(_ app: UIApplication) {
3333
#if DEBUG
34-
let debugMode = true
34+
let debugMode = true//,":verbose:/.+/"
3535
#else
3636
let debugMode = false
3737
#endif

next/kmp/browser/src/commonMain/kotlin/org/dweb_browser/browser/jsProcess/ext/createJsProcess.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package org.dweb_browser.browser.jsProcess.ext
33
import io.ktor.http.URLBuilder
44
import kotlinx.coroutines.CoroutineStart
55
import kotlinx.coroutines.launch
6-
import kotlinx.serialization.encodeToString
76
import kotlinx.serialization.json.Json
87
import org.dweb_browser.browser.jmm.debugJsMM
98
import org.dweb_browser.browser.jsProcess.CreateProcessReturn
@@ -38,6 +37,7 @@ suspend fun MicroModule.Runtime.createJsProcess(
3837
locale = microModule.manifest,
3938
remote = microModule.manifest,
4039
autoStart = true,
40+
startReason = "create-fetch-ipc"
4141
)
4242
codeIpc.onClosed {
4343
codeIpc.launchJobs += codeIpc.scope.launch(start = CoroutineStart.UNDISPATCHED) { fetchIpc.close() }
@@ -94,6 +94,7 @@ class JsProcess(
9494
locale = remoteMM,
9595
// 不自动开始,等到web-worker中它自己去握手
9696
autoStart = false,
97+
startReason = "create-ipc-endpoint",
9798
)
9899
}
99100

next/kmp/core/src/commonMain/kotlin/org/dweb_browser/core/ipc/Ipc.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,12 +185,14 @@ class Ipc internal constructor(
185185
suspend fun start(await: Boolean = true, reason: String = "") {
186186
if (await) {
187187
withScope(scope) {
188+
debugIpc.verbose("start-begin", reason)
188189
endpoint.start(true)
189190
startOnce()
190191
awaitOpen("from-start $reason")
191192
}
192193
} else {
193194
scope.launch {
195+
debugIpc.verbose("start-begin", reason)
194196
endpoint.start(true)
195197
startOnce()
196198
}

next/kmp/core/src/commonMain/kotlin/org/dweb_browser/core/ipc/IpcPool.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import kotlinx.coroutines.CancellationException
44
import kotlinx.coroutines.CoroutineName
55
import kotlinx.coroutines.Job
66
import kotlinx.coroutines.cancel
7-
import kotlinx.coroutines.launch
87
import kotlinx.coroutines.plus
98
import org.dweb_browser.core.help.types.MicroModuleManifest
109
import org.dweb_browser.helper.Debugger
@@ -62,13 +61,13 @@ open class IpcPool {
6261
startReason: String?,
6362
) {
6463
/// 保存ipc,并且根据它的生命周期做自动删除
65-
debugIpcPool("createIpc", ipc)
64+
debugIpcPool("createIpc") {
65+
"ipc=$ipc start=$autoStart reason=$startReason"
66+
}
6667
ipcSet.add(ipc)
6768
/// 自动启动
6869
if (autoStart) {
69-
scope.launch {
70-
ipc.start(reason = startReason ?: "autoStart")
71-
}
70+
ipc.start(await = false, reason = startReason ?: "autoStart")
7271
}
7372
ipc.onClosed {
7473
ipcSet.remove(ipc)

next/kmp/core/src/commonMain/kotlin/org/dweb_browser/core/ipc/NativeEndpoint.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,15 +105,15 @@ class NativeEndpoint(
105105
override suspend fun postIpcMessage(msg: EndpointIpcMessage) {
106106
awaitOpen("then-postIpcMessage")
107107
withScope(scope) {
108-
debugEndpoint.verbose("message-out", msg)
108+
debugEndpoint.verbose("message-out") { "pid=${msg.pid} ipcMessage=${msg.ipcMessage}" }
109109
messageOut.send(msg)
110110
}
111111
}
112112

113113
override suspend fun doStart() {
114114
scope.launch {
115115
for ((pid, ipcMessage) in messageIn) {
116-
debugEndpoint.verbose("message-in", "pid=$pid ipcMessage=$ipcMessage")
116+
debugEndpoint.verbose("message-in") { "pid=$pid ipcMessage=$ipcMessage" }
117117
/**
118118
* UNDISPATCHED 能确保 orderBy 的顺序
119119
*/

next/kmp/core/src/commonMain/kotlin/org/dweb_browser/core/ipc/helper/IpcFork.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import org.dweb_browser.core.help.types.CommonAppManifest
1010
*/
1111
@Serializable
1212
@SerialName(IPC_MESSAGE_TYPE_FORK)
13-
class IpcFork(
13+
data class IpcFork(
1414
val pid: Int,
1515
val autoStart: Boolean,
1616
val startReason: String? = null,

next/kmp/core/src/commonMain/kotlin/org/dweb_browser/core/module/NativeMicroModule.kt

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import kotlinx.coroutines.launch
88
import kotlinx.serialization.ExperimentalSerializationApi
99
import kotlinx.serialization.cbor.Cbor
1010
import kotlinx.serialization.encodeToByteArray
11-
import kotlinx.serialization.encodeToString
1211
import kotlinx.serialization.json.Json
1312
import kotlinx.serialization.json.JsonElement
1413
import org.dweb_browser.core.help.types.DWEB_PROTOCOL
@@ -59,10 +58,20 @@ abstract class NativeMicroModule(manifest: MicroModuleManifest) : MicroModule(ma
5958
if (toMM is NativeMicroModule.NativeRuntime) {
6059
fromMM.debugMM("NMM/connectAdapter", "fromMM: ${fromMM.mmid} => toMM: ${toMM.mmid}")
6160
val channel = NativeMessageChannel(kotlinIpcPool.scope, fromMM.id, toMM.id)
62-
val fromNativeIpc =
63-
kotlinIpcPool.createIpc(channel.port1, 0, fromMM.manifest, toMM.microModule.manifest)
64-
val toNativeIpc =
65-
kotlinIpcPool.createIpc(channel.port2, 0, toMM.microModule.manifest, fromMM.manifest)
61+
val fromNativeIpc = kotlinIpcPool.createIpc(
62+
endpoint = channel.port1,
63+
pid = 0,
64+
locale = fromMM.manifest,
65+
remote = toMM.microModule.manifest,
66+
startReason = "NMM-connectAdapter"
67+
)
68+
val toNativeIpc = kotlinIpcPool.createIpc(
69+
endpoint = channel.port2,
70+
pid = 0,
71+
locale = toMM.microModule.manifest,
72+
remote = fromMM.manifest,
73+
startReason = "NMM-connectAdapter"
74+
)
6675
// fromMM.beConnect(fromNativeIpc, reason) // 通知发起连接者作为Client
6776
toMM.beConnect(toNativeIpc, reason) // 通知接收者作为Server
6877
fromNativeIpc

next/kmp/dwebview/src/iosMain/kotlin/org/dweb_browser/dwebview/engine/DWebViewEngine.ios.kt

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import org.dweb_browser.dwebview.polyfill.FaviconPolyfill
3838
import org.dweb_browser.dwebview.wkWebsiteDataStore
3939
import org.dweb_browser.helper.JsonLoose
4040
import org.dweb_browser.helper.PureBounds
41+
import org.dweb_browser.helper.SafeHashSet
4142
import org.dweb_browser.helper.Signal
4243
import org.dweb_browser.helper.SimpleSignal
4344
import org.dweb_browser.helper.collectIn
@@ -76,6 +77,8 @@ import platform.WebKit.javaScriptEnabled
7677
@OptIn(ExperimentalForeignApi::class)
7778
internal val dwebHelper = DwebHelper()
7879

80+
typealias CompletionHandler = (Any?, platform.Foundation.NSError?) -> Unit
81+
7982
@Suppress("CONFLICTING_OVERLOADS")
8083
@OptIn(ExperimentalForeignApi::class, ExperimentalResourceApi::class)
8184
class DWebViewEngine(
@@ -224,18 +227,19 @@ class DWebViewEngine(
224227
httpLocalhostGatewaySuffix
225228
)
226229
) {
227-
"dweb+" + inputUrl.replace(inputHostWithPort, inputHostWithPort.substring(
228-
0, inputHostWithPort.length - httpLocalhostGatewaySuffix.length
229-
).let { dwebHost ->
230-
val hostInfo = dwebHost.split('-')
231-
val port = hostInfo.last().toUShortOrNull()
232-
if (port != null) {
233-
hostInfo.toMutableList().run {
234-
removeLast()
235-
joinToString("-")
236-
} + ":$port"
237-
} else dwebHost
238-
})
230+
"dweb+" + inputUrl.replace(
231+
inputHostWithPort, inputHostWithPort.substring(
232+
0, inputHostWithPort.length - httpLocalhostGatewaySuffix.length
233+
).let { dwebHost ->
234+
val hostInfo = dwebHost.split('-')
235+
val port = hostInfo.last().toUShortOrNull()
236+
if (port != null) {
237+
hostInfo.toMutableList().run {
238+
removeLast()
239+
joinToString("-")
240+
} + ":$port"
241+
} else dwebHost
242+
})
239243
} else inputUrl
240244
}
241245
}
@@ -258,8 +262,7 @@ class DWebViewEngine(
258262
internal val urlObserver = DWebUrlObserver(this)
259263
val loadStateFlow =
260264
setupLoadStateFlow(this, dwebNavigationDelegate, urlObserver, configuration, options.url)
261-
val beforeUnloadSignal =
262-
setupBeforeUnloadSignal(this, dwebNavigationDelegate, loadStateFlow)
265+
val beforeUnloadSignal = setupBeforeUnloadSignal(this, dwebNavigationDelegate, loadStateFlow)
263266
val overrideUrlLoadingHooks by lazy { setupOverrideUrlLoadingHooks(this, dwebNavigationDelegate) }
264267

265268
init {
@@ -372,8 +375,8 @@ class DWebViewEngine(
372375
override fun setFrame(frame: CValue<CGRect>) {
373376
super.setFrame(frame)
374377
scrollView.contentInset = cValue { UIEdgeInsetsZero };
375-
if (!UIEdgeInsetsEqualToEdgeInsets(scrollView.adjustedContentInset,
376-
cValue { UIEdgeInsetsZero })
378+
if (!UIEdgeInsetsEqualToEdgeInsets(
379+
scrollView.adjustedContentInset, cValue { UIEdgeInsetsZero })
377380
) {
378381
val insetToAdjust = scrollView.adjustedContentInset;
379382
scrollView.contentInset =
@@ -411,6 +414,11 @@ class DWebViewEngine(
411414
return deferred
412415
}
413416

417+
/**
418+
* 因为函数倒挂给 WKWebView,所以存在内存问题,这里强制存储回调,避免被 kotlin 回收。
419+
*/
420+
private val completionHandlers = SafeHashSet<CompletionHandler>()
421+
414422
suspend fun <T> awaitAsyncJavaScript(
415423
functionBody: String,
416424
arguments: Map<Any?, *>? = null,
@@ -419,7 +427,7 @@ class DWebViewEngine(
419427
afterEval: (suspend () -> Unit)? = null,
420428
): T {
421429
val deferred = CompletableDeferred<T>()
422-
callAsyncJavaScript(functionBody, arguments, inFrame, inContentWorld) { result, error ->
430+
val ch: CompletionHandler = { result, error ->
423431
if (error == null) {
424432
deferred.complete(result as T)
425433
} else {
@@ -437,9 +445,19 @@ class DWebViewEngine(
437445
)
438446
}
439447
}
448+
callAsyncJavaScript(
449+
functionBody = functionBody,
450+
arguments = arguments,
451+
inFrame = inFrame,
452+
inContentWorld = inContentWorld,
453+
completionHandler = ch,
454+
)
440455
afterEval?.invoke()
441456

442-
return deferred.await()
457+
completionHandlers.add(ch)
458+
return deferred.await().also {
459+
completionHandlers.remove(ch)
460+
}
443461
}
444462

445463
/**

next/kmp/dwebview/src/iosMain/kotlin/org/dweb_browser/dwebview/messagePort/DWebMessagePort.ios.kt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import kotlinx.serialization.json.Json
88
import org.dweb_browser.core.ipc.helper.DWebMessage
99
import org.dweb_browser.core.ipc.helper.IWebMessagePort
1010
import org.dweb_browser.dwebview.DWebView
11+
import org.dweb_browser.helper.hexString
1112
import org.dweb_browser.helper.launchWithMain
1213
import org.dweb_browser.helper.withMainContext
1314
import platform.Foundation.NSNumber
@@ -24,9 +25,7 @@ class DWebMessagePort(val portId: Int, private val webview: DWebView, parentScop
2425
val channel = Channel<DWebMessage>(capacity = Channel.UNLIMITED)
2526
webview.lifecycleScope.launchWithMain {
2627
webview.engine.evalAsyncJavascript<Unit>(
27-
"nativeStart($portId)",
28-
null,
29-
DWebViewWebMessage.webMessagePortContentWorld
28+
"nativeStart($portId)", null, DWebViewWebMessage.webMessagePortContentWorld
3029
).await()
3130
}
3231
channel
@@ -63,9 +62,9 @@ class DWebMessagePort(val portId: Int, private val webview: DWebView, parentScop
6362
}.joinToString(",")
6463
if (event is DWebMessage.DWebMessageBytes) {
6564
webview.engine.evalAsyncJavascript<Unit>(
66-
"nativePortPostMessage($portId, ${
67-
Json.encodeToString(event.text)
68-
}, [$ports])", null, DWebViewWebMessage.webMessagePortContentWorld
65+
"nativePortPostMessage($portId, str_to_hex_binary(\"${
66+
event.binary.hexString
67+
}\"), [$ports])", null, DWebViewWebMessage.webMessagePortContentWorld
6968
).await()
7069
} else if (event is DWebMessage.DWebMessageString) {
7170
webview.engine.evalAsyncJavascript<Unit>(

0 commit comments

Comments
 (0)