0

Basically the JavascriptInterface receive a Click event from WebView, then I need to change an HTML element multiple times, the problem is the WebView show only the last change, that's mean the rendering is not immediate.

Question: How to make webview.loadUrl("javascript:updateProgress(20);"); take effect and change WebView content immediately?

- - - - - - - - - - - - - - - - -

Old question:

I have a HTML progress-bar in my WebView, I can simply update the progress-bar value by running webview.loadUrl("javascript:updateProgress(20);"); this work fine from onCreate().

// JavaScript in WebView
function updateProgress(percentage){

    document.getElementById('progressBar').style.width = percentage + '%';
}

Now, I have a class that send binary data to an connected BLE device, I toke the example from Google BluetoothLeGatt, and I added a method to write to an characteristic (send data) in BluetoothLeService.java.

public void WriteCharacteristic(BluetoothGattCharacteristic characteristic, byte[] data, MainActivity mainactivity){

    byte[] data_twenty_byte = new byte [20];
    int progress_count = 0;

    while(get_next_twenty_byte(data, data_twenty_byte)){

        characteristic.setValue(data_twenty_byte);
        mBluetoothGatt.writeCharacteristic(characteristic);

        progress_count++;
        mainactivity.webview.loadUrl("javascript:updateProgress(" + progress_count + ");");
    }
}

The problem is the WebView won't be updated (Redraw/Re-Render) while WriteCharacteristic() is running, the WebView Redraw/Re-Render only after WriteCharacteristic() finish, mean at progress-bar 100%.

Note: I already tried runOnUiThread(new Runnable() {});

My Question is, How to force mainactivity.webview to Redraw/Re-Render immediately ?

Thank you,

ShellX3
  • 51
  • 8
  • My guess is you'd want to run this `WriteCharacteristic` method on a background thread, then post the `webvew.loadUrl(...)` back to the main thread. – nEx.Software May 28 '21 at 18:51
  • Right, `WriteCharacteristic` is a member of `BluetoothLeService` Class, and this Class is used as a service, `mainactivity.bindService(intent, this.mServiceConnection, BIND_AUTO_CREATE);` – ShellX3 May 28 '21 at 19:05
  • however, I will try now you suggestion, running `WriteCharacteristic` on a background thread. – ShellX3 May 28 '21 at 19:06
  • Doesn't work, I even avoided all BLE stuff, just JavaScript click -> JS Interface -> Call MainActivity Method -> Do progress-bar update in a loop... still updated only at the end. – ShellX3 May 28 '21 at 23:32
  • I tested all solution from this topic https://stackoverflow.com/questions/22607657/webview-methods-on-same-thread-error – ShellX3 May 28 '21 at 23:32

1 Answers1

0

As noted in my comment, pushing the long-running process to a background thread, then running the webview update on the ui thread should do what you are looking to achieve. Here's a quick example that I threw together:

package com.example.myapplication

import android.annotation.SuppressLint
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.webkit.JavascriptInterface
import android.webkit.WebView
import kotlin.concurrent.thread

class MainActivity : AppCompatActivity() {

    lateinit var webView: WebView

    @SuppressLint("SetJavaScriptEnabled")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        webView = WebView(this)
        webView.settings.javaScriptEnabled = true
        webView.addJavascriptInterface(this, "app")
        setContentView(webView)

        val html =
            """
            <html>
                <head>
                    <script type='text/javascript'>
                        function updateProgress(progress) {
                            let el = document.getElementById('progress');
                            el.innerText = progress;
                        }
                    </script>
                </head>
                <body>                    
                    <p>Value: <span id='progress'>0</span></p>
                    <button onclick='app.startUpdates();'>Start Update</button>
                </body>
            </html>            
            """.trimIndent()

        webView.loadData(html, "text/html", "UTF-8")
    }

    @JavascriptInterface
    fun startUpdates() {
        doSomething()
    }

    private fun doSomething() {
        thread {
            for (i in 1..100) {
                runOnUiThread { webView.evaluateJavascript("updateProgress($i);", null) }
                Thread.sleep(10)
            }
        }
    }

}
nEx.Software
  • 6,782
  • 1
  • 26
  • 34
  • Thank you for your comment, but my project is on Java, and `startUpdates()` must be on `public class MyJSInterface { ... }` needed for `webview.addJavascriptInterface()` – ShellX3 Jun 02 '21 at 11:53
  • I'm confident that you will be able to translate this to Java and move the function to your interface. – nEx.Software Jun 02 '21 at 16:07
  • Sorry, but if I do that I will get the same Java code I already have. I will try create a small but full example to easily identify the problem. – ShellX3 Jun 02 '21 at 18:43