【问题标题】:Creating a queue for the evaluateJavascript function on a WebView在 WebView 上为 evaluateJavascript 函数创建队列
【发布时间】:2022-01-01 23:04:20
【问题描述】:

我有一个混合应用程序;我的一些活动使用 WebView 来显示 Web 内容。我在 WebView 中显示的 Web 应用程序有一个 JS 界面,可让我向 Web 应用程序发送命令以导航不同的位置或执行其他操作。

例如,如果我需要我的网络应用导航到“用户配置文件”页面,我执行如下命令:

class SomeActivity: AppCompatActivity {
   ...
   webView.evaluateJavascript("navigateTo(\"userprofile\")")
   ...
}

然后,我通过 JS 接口得到响应,应用程序做出相应的反应。

我引入了一个JS队列来提高性能,所以JS命令是顺序执行的。我没有直接在 WebView 上调用 evaluateJavascript() 函数,而是创建了一个自定义 WebView 组件,将此 JS 队列设置为属性。

class SomeActivity: AppCompatActivity {
   ...
   webView.jsQueue.queueEvaluateJavascript("navigateTo(\"userprofile\")")
   ...
}

现在我想在此基础上添加一个新行为,即能够预处理队列中的命令。我所说的预处理的意思是,如果我曾经对相同“类型”的命令进行排队,例如:

class SomeActivity: AppCompatActivity {
   ...
   webView.jsQueue.queueEvaluateJavascript("navigateTo(\"userprofile\")")
   webView.jsQueue.queueEvaluateJavascript("navigateTo(\"about-me\")")
   webView.jsQueue.queueEvaluateJavascript("navigateTo(\"user-list\")")
   ...
}

我想要发生的是队列足够聪明,可以放弃这两个第一个“导航”命令 - "navigateTo(\"userprofile\")""navigateTo(\"about-me\")" - 因为我不希望我的 WebView 导航到这两个地方只是为了最后导航到"navigateTo(\"user-list\")"

这个JS队列的实现是这样的:

class JsQueue(
    private val webView: WebView,
    private val scope: CoroutineScope
) {
    
    init {
        scope.launch { 
            for (jsScript in jsChannel) {
                runJs(jsScript)
            }
        }
    }

    private val jsChannel = Channel<String>(BUFFERED)

    fun queueEvaluateJavascript(script: String) {
        runBlocking {
            jsChannel.send(script)
        }
    }

    suspend fun runJs(script: String) = suspendCoroutine<String> { cont ->
        webView.evaluateJavascript(script) { result ->
            cont.resume(result)
        }
    }
}
  • 如何预处理Channel&lt;String&gt;中的js命令,所以我 抛弃重复的 js 命令?
  • 另外,有时我的 WebView 会变得不可见,我想在这种情况发生时暂停队列。我想知道是否有任何方法可以以编程方式暂停频道

编辑#1

另外,有时我的 WebView 会变得不可见,我想 发生这种情况时暂停队列。我想知道是否有任何方法可以以编程方式暂停 Channel

我尝试过使用 this PausableDispatcher 实现,它似乎可以解决问题。

【问题讨论】:

  • 在 JavaScript 中实现这种行为会比尝试捕获 evaluateJavascript 调用更容易吗?如果您必须在 Kotlin 中执行此操作,您可以轻松创建 queueJavascriptexecuteQueue 方法,它们有选择地将给定的命令保存在数组中,并在运行 executeQueue 命令时通过 evaluateJavascript 调用所有命令。

标签: android kotlin webview kotlin-coroutines kotlinx.coroutines.channels


【解决方案1】:

您提供的所有命令示例都遵循特定模式:它们都是函数。我们可以利用这一点!

首先,让我们创建一些术语。

navigateTo() 是一个函数(当然!)。 让我们将函数的navigateTo 部分称为type

一些types 的示例是:

console.log() => `console.log`,
gotoUrl(url) => `gotoUrl`.

我刚刚编造了这个术语。但它会帮助你理解逻辑。

现在,我们需要做的是查看命令数组,了解它是type,并检查是否有其他命令具有相同的类型。如果是,则需要在队列执行之前将它们从队列中排除。

简单!

我编写了一个示例代码,您可以将它与您的脚本集成:

// Example array of commands for demonstration.
let commands = [
    'navigateTo("a")',
    'navigateTo("b")',
    'navigateTo("c")',
];


/** A list of non-duplicate types*/
let types = [];
/** A list of non-duplicate commands */
let newCommands = [];

// Reverse the array because the most important commands start from the end of array.
for(let command of commands.reverse()){
    let type = command.slice(0, command.indexOf('('));
    // Determine if type already exists
    let alreadyExists = false;
    for(let commandType of types){
      if(type == commandType){
        alreadyExists = true;
        break;
      }
    }
  
    if(alreadyExists)
      // Type already exists. Do not add to command list.
      continue;
    
     // This type & command does not exist.
     // Update command & type arrays
     types.push(type);
     newCommands.push(command)
}

// New Commands
console.log("Commands: ", newCommands);
// If you want to keep same queue order without duplicates:
console.log("Commands: ", newCommands.reverse())

如果我错过了回答这个问题的分数,请告诉我。否则,为一个很棒的队列系统干杯!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-01-27
    • 2018-12-02
    • 2011-06-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多