Создание очереди для функции AssessmentJavascript в WebView

У меня гибридное приложение, некоторые из моих действий используют WebView для отображения веб-контента. Веб-приложение, которое я показываю в WebView, имеет интерфейс JS, который позволяет мне отправлять команды веб-приложению для перехода в разные места или выполнения разных действий.

Например, если мне нужно, чтобы мое веб-приложение переходило на страницу «профиля пользователя», я выполняю такую ​​команду, как:

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

Затем через интерфейс JS я получаю ответ, и приложение реагирует соответствующим образом.

Для повышения производительности я ввел очередь JS, поэтому команды JS выполняются последовательно. Вместо того, чтобы позвонить evaluateJavascript() функция непосредственно в WebView. Я создал пользовательский компонент 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)
        }
    }
}
  • Как я могу предварительно обработать команды js в Channel<String> так я избавляюсь от дублированных команд js?
  • Кроме того, иногда мой WebView становится невидимым, и я хочу приостановить очередь, когда это произойдет. Мне интересно, есть ли способ программно приостановить канал ?

Редактировать # 1

Кроме того, иногда мой WebView становится невидимым, и я хочу приостановить очередь, когда это произойдет. Мне интересно, есть ли способ программно приостановить канал ?

Я пробовал использовать это PausableDispatcher реализация, и кажется, что это помогает.

1 ответ

Все приведенные вами примеры команд следуют определенному шаблону: все они являются функциями. Мы можем использовать это в своих интересах!

Во-первых, давайте создадим некоторую терминологию.

является функцией (конечно!). И позвоним часть функции а.

Примером некоторых s являются:

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

Я просто придумал эту терминологию. Но это поможет вам понять логику.

Теперь, что нам нужно сделать, это посмотреть на массив команд, понять его и проверьте, имеют ли какие-либо другие команды тот же тип. Если они это делают, их необходимо исключить из очереди до выполнения очереди.

Легкий!

Я написал пример кода, который вы можете интегрировать со своим скриптом:

      // 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())

Дайте мне знать, если я пропустил ответ на этот вопрос. В противном случае, ура отличной системе очередей!

Другие вопросы по тегам