Skip to content

Commit

Permalink
Merge pull request #5 from ding1dingx/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
syxc authored Aug 25, 2024
2 parents 58c9b31 + 9797263 commit ec9c78d
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 44 deletions.
10 changes: 7 additions & 3 deletions app/src/main/java/com/ding1ding/jsbridge/app/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,15 @@ class MainActivity :
}

private fun setupWebViewBridge(webView: WebView) {
bridge = WebViewJavascriptBridge.create(this, webView, lifecycle).apply {
bridge = WebViewJavascriptBridge.create(
context = this,
webView = webView,
lifecycle = lifecycle,
webViewClient = createWebViewClient(),
).apply {
consolePipe = object : ConsolePipe {
override fun post(message: String) {
Logger.d("[console.log]") { message }
Logger.i("[console.log]") { message }
}
}

Expand All @@ -124,7 +129,6 @@ class MainActivity :

override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
bridge.injectJavascriptIfNeeded()
Logger.d(TAG) { "onPageFinished" }
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ import androidx.lifecycle.LifecycleOwner
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

class WebViewJavascriptBridge private constructor(
private val context: Context,
Expand All @@ -30,6 +36,8 @@ class WebViewJavascriptBridge private constructor(
private val isInjected = AtomicBoolean(false)
private val isWebViewReady = AtomicBoolean(false)

private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())

init {
setupBridge()
}
Expand Down Expand Up @@ -60,8 +68,7 @@ class WebViewJavascriptBridge private constructor(
),
)
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean =
client?.shouldOverrideUrlLoading(view, url)
?: super.shouldOverrideUrlLoading(view, url)
client?.shouldOverrideUrlLoading(view, url) ?: super.shouldOverrideUrlLoading(view, url)

// Add other WebViewClient methods as needed, delegating to the client if it's not null
}
Expand All @@ -76,7 +83,7 @@ class WebViewJavascriptBridge private constructor(
return
}
Logger.d { "Injecting JavaScript" }
webView.post {
scope.launch {
webView.evaluateJavascript("javascript:$bridgeScript", null)
webView.evaluateJavascript("javascript:$consoleHookScript", null)
isInjected.set(true)
Expand All @@ -100,28 +107,30 @@ class WebViewJavascriptBridge private constructor(
Logger.e { "Bridge is not injected. Cannot call handler: $handlerName" }
return
}
val callbackId = callback?.let { "native_cb_${uniqueId.incrementAndGet()}" }
callbackId?.let { responseCallbacks[it] = callback }

val callbackId = callback?.let { "native_cb_${uniqueId.incrementAndGet()}" }?.also {
responseCallbacks[it] = callback
}
val message = CallMessage(handlerName, data, callbackId)
val messageString = MessageSerializer.serializeCallMessage(message)
dispatchMessage(messageString)
Logger.d { "Handler called: $handlerName" }
}

private fun processMessage(messageString: String) {
try {
val message = MessageSerializer.deserializeResponseMessage(
messageString,
responseCallbacks,
messageHandlers,
)
when {
message.responseId != null -> handleResponse(message)
else -> handleRequest(message)
scope.launch {
try {
val message = MessageSerializer.deserializeResponseMessage(
messageString,
responseCallbacks,
messageHandlers,
)
when {
message.responseId != null -> handleResponse(message)
else -> handleRequest(message)
}
} catch (e: Exception) {
Logger.e(e) { "Error processing message" }
}
} catch (e: Exception) {
Logger.e(e) { "Error processing message" }
}
}

Expand All @@ -135,36 +144,34 @@ class WebViewJavascriptBridge private constructor(
}

private fun handleRequest(message: ResponseMessage) {
val handler = messageHandlers[message.handlerName]
if (handler is MessageHandler<*, *>) {
messageHandlers[message.handlerName]?.let { handler ->
@Suppress("UNCHECKED_CAST")
val typedMessageHandler = handler as MessageHandler<Any?, Any?>
val responseData = typedMessageHandler.handle(message.data)
val responseData = (handler as MessageHandler<Any?, Any?>).handle(message.data)
message.callbackId?.let { callbackId ->
val response = ResponseMessage(callbackId, responseData, null, null, null)
val responseString = MessageSerializer.serializeResponseMessage(response)
dispatchMessage(responseString)
Logger.d { "Request handled: ${message.handlerName}" }
}
} else {
Logger.e { "No handler found for: ${message.handlerName}" }
}
} ?: Logger.e { "No handler found for: ${message.handlerName}" }
}

private fun dispatchMessage(messageString: String) {
val script = "WebViewJavascriptBridge.handleMessageFromNative('$messageString');"
webView.post {
scope.launch {
val script = "WebViewJavascriptBridge.handleMessageFromNative('$messageString');"
webView.evaluateJavascript(script, null)
Logger.d { "Message dispatched to JavaScript" }
}
}

private fun loadAsset(fileName: String): String = try {
context.assets.open(fileName).bufferedReader().use { it.readText() }
} catch (e: Exception) {
Logger.e(e) { "Error loading asset $fileName" }
""
}.trimIndent()
private fun loadAsset(fileName: String): String = runBlocking(Dispatchers.IO) {
try {
context.assets.open(fileName).bufferedReader().use { it.readText() }.trimIndent()
} catch (e: Exception) {
Logger.e(e) { "Error loading asset $fileName" }
""
}
}

private fun clearState() {
responseCallbacks.clear()
Expand All @@ -174,18 +181,14 @@ class WebViewJavascriptBridge private constructor(
Logger.d { "Bridge state cleared" }
}

private fun removeJavascriptInterface() {
private fun release() {
webView.removeJavascriptInterface("normalPipe")
webView.removeJavascriptInterface("consolePipe")
Logger.d { "JavaScript interfaces removed" }
}

private fun release() {
removeJavascriptInterface()
consolePipe = null
responseCallbacks.clear()
messageHandlers.clear()
clearState()
scope.cancel()
Logger.d { "Bridge released" }
}

Expand Down Expand Up @@ -222,17 +225,19 @@ class WebViewJavascriptBridge private constructor(
}

companion object {
@JvmStatic
fun create(
context: Context,
webView: WebView,
lifecycle: Lifecycle? = null,
webViewClient: WebViewClient? = null,
): WebViewJavascriptBridge = WebViewJavascriptBridge(context, webView).also { bridge ->
lifecycle?.addObserver(bridge)
bridge.setWebViewClient(webViewClient)
): WebViewJavascriptBridge = WebViewJavascriptBridge(context, webView).apply {
lifecycle?.addObserver(this)
setWebViewClient(webViewClient)
Logger.d { "Bridge created and lifecycle observer added" }
}

@JvmStatic
fun setLogLevel(level: Logger.LogLevel) {
Logger.logLevel = level
}
Expand Down

0 comments on commit ec9c78d

Please sign in to comment.