First, Android and JS interaction – bridge WebView

(1) JS calls Android methods

There are three methods of JS calling Android, and the principles, use, advantages and disadvantages are introduced in the following sequence.

1. Object mapping via addJavascriptInterface() of WebView

Specific principles

Android and JS through webview. AddJavascriptInterface (new JSKit (), “MJS) method to form the object map, JS object of MJS can invoke in the Android JSKit methods on the object.

The specific use

Step 1: In Android, set the mapping between Android classes and JS code through WebView

public class MainActivity extends AppCompatActivity {
    WebView mWebView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mWebView = (WebView) findViewById(R.id.webview);
        WebSettings webSettings = mWebView.getSettings();
        webSettings.setJavaScriptEnabled(true); // Set the permission to interact with Js // Javascript object name / / through the addJavascriptInterface () AJavaScriptInterface class object is mapped to the JS object of MJS mWebView. AddJavascriptInterface (new JSKit(),"mjs"); // Load the JS code mwebview.loadURL ("file:///android_asset/javascript.html");
Copy the code

Step 2: Define an Android class: JSKit that maps to JS objects

Public class JSKit {// define the method that JS needs to call, @javascriptInterface public void hello(String MSG) {system.out.println (String MSG);"JS successfully calls Android's hello method"); }}Copy the code

Step 3: Place the javascript. HTML format of the javascript code to be called in the SRC /main/assets folder

<! DOCTYPE html> <html> <head> <meta charset="utf-8">
      <title>Carson</title>  
      <script>
         function callAndroid(){// called because of object mappingtestObject is equal to calling the Android mapped object mjs.hello("Js calls the Hello method in Android"); } </script> </head> <body> // Click the button to call callAndroid function <buttontype="button" id="button1" onclick="callAndroid()"></button>
   </body>
</html>
Copy the code

The advantages and disadvantages

Advantages: simple to use, only Android objects and JS objects can be mapped;

Disadvantages:

For Android below 4.2, there are security vulnerabilities, which need to be fixed by interception Prompt ();

For Android above 4.2, you only need to annotate the function called by JS with @javascriptInterface

Intercepting urls via the WebViewClient shouldOverrideUrlLoading() method callback;

Specific principles

Android intercepts the URL through the WebViewClient callback shouldOverrideUrlLoading() and resolves the protocol for the URL. If a pre-agreed protocol is detected, the corresponding method is called, that is, JS needs to call the method in Android.

The specific use

Step 1: Place the required Url protocol JS code: javasjavascript. HTML in the SRC /main/assets folder

<! DOCTYPE html> <html> <head> <meta charset="utf-8">
      <title>SoarYuan</title>
     <script>
         function callAndroid(){/* The convention url protocol is: js:// webView? arg1=111&arg2=222*/ document.location ="js://webview? arg1=111&arg2=222"; } </script> </head> <! -- Clicking the button calls the callAndroid() method --> <body> <buttontype="button" id="button2" onclick="callAndroid()"</button> </body> </ HTML >Copy the code

When the JS is loaded via Android’s mwebview.loadurl (“file:///android_asset/javascript.html”), it calls shouldOverrideUrlLoading().

Step 2: Override shouldOverrideUrlLoading() on Android via WebViewClient

public class MainActivity extends AppCompatActivity {
    WebView mWebView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mWebView = (WebView) findViewById(R.id.webview);
        WebSettings webSettings = mWebView.getSettings();
        webSettings.setJavaScriptEnabled(true); / / set with Js interaction permissions webSettings. SetJavaScriptCanOpenWindowsAutomatically (true); Step 1: load the JS code in the following format :file:///android_asset/ filenames. HTML mwebview.loadurl ("file:///android_asset/javascript.html"); / / copy the WebViewClient class mWebView shouldOverrideUrlLoading method. The setWebViewClient (newWebViewClient() {/ / the overloading method is not recommended, above 7.0 system has rejected the / / shouldOverrideUrlLoading (WebView view, String url) this method, / / if you want to intercept the url, you need to do to compatibility, Rewrite / / shouldOverrideUrlLoading (WebView view, WebResourceRequest request) method, @override public Boolean shouldOverrideUrlLoading(WebView View, String URL) {returnsuper.shouldOverrideUrlLoading(view, url); } @override public Boolean shouldOverrideUrlLoading(WebView View, WebResourceRequest Request){ // It is generally determined by scheme (protocol format) & authority (protocol name) (first two parameters) // It is assumed that the url passed in ="js://webview? arg1=111&arg2=222"Uris (which are also agreed to intercept) URIs;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                        uri = request.getUrl();
                    } else{ uri = Uri.parse(request.toString()); } // If the url's protocol = predefined JS protocol, parse the following parsing parametersif ( uri.getScheme().equals("js")) {// if authority = pre-agreed webview in the protocol, that is, represents the convention of the protocol // so intercept the URL, the following JS starts to call the method Android needsif (uri.getAuthority().equals("webview") {// Step 3: Execute the logic system.out.println ("Js calls Android methods"); HashMap<String, String> params = new HashMap<>(); Set<String> collection = uri.getQueryParameterNames(); String result =(useid=123456); 
                            view.loadUrl("javascript:returnResult(\"" + result + "\")");
                            
                        }
                         return true;
                    }
                    returnsuper.shouldOverrideUrlLoading(view, request); }}); }}Copy the code

The advantages and disadvantages

Advantages: No addJavascriptInterface() vulnerability;

Disadvantages: Complex return value of JS getting Android methods. If JS wants to get the return value of the Android method, it must execute the JS method to pass the return value back via the WebView loadUrl().

// Android:MainActivity.java
mWebView.loadUrl("javascript:returnResult(" + result + ")"); / / JS: javascript. HTMLfunction returnResult(result){
    alert("result is" + result);
}
Copy the code

3, through the WebChromeClient onJsAlert(), onJsConfirm(), onJsPrompt() method callback interception JS dialog box alert(), confirm(), prompt() method, message interception

Specific principles

Android intercepts JS dialogs via onJsAlert(), onJsConfirm(), and onJsPrompt (), and then parses the content of their messages.

The specific use

The common interception is JS input box prompt() method, because only prompt() can return any type of value, the operation is the most comprehensive, convenient, more flexible; The alert() dialog does not return a value; The Confirm () dialog box can only return two values in two states (confirm/cancel).

Step 1: Load the JS code, as follows: javascript. HTML into the SRC /main/assets folder

<! DOCTYPE html> <html> <head> <meta charset="utf-8">
      <title>SoarYuan</title>
     <script>
        function clickprompt(){var result=prompt();"js://demo? arg1=111&arg2=222");
            alert("demo "+ result); } </script> </head> <! Clickprompt () --> <body> <buttontype="button" id="button3" onclick="clickprompt()"</button> </body> </ HTML >Copy the code

When the above JS code is loaded using mwebview.loadurl (“file:///android_asset/javascript.html”), the callback onJsPrompt() is triggered, as follows:

If it is an interception warning box, alert(), the callback onJsAlert() is triggered; The callback onJsConfirm() is triggered if it is intercepting the confirmation box, known as Confirm();

Step 2: Copy onJsPrompt() on Android via WebChromeClient

public class MainActivity extends AppCompatActivity {
    WebView mWebView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mWebView = (WebView) findViewById(R.id.webview);
        WebSettings webSettings = mWebView.getSettings();
        webSettings.setJavaScriptEnabled(true); / / set with Js interaction permissions webSettings. SetJavaScriptCanOpenWindowsAutomatically (true); Step 1: load the JS code in the following format :file:///android_asset/ filenames. HTML mwebview.loadurl ("file:///android_asset/javascript.html");

        mWebView.setWebChromeClient(new WebChromeClient() {// block the input field (shouldOverrideUrlLoading) // Message: represents the content of promt() (not the URL) // result: represents the return value of the input field @override public Boolean onJsPrompt(WebView view, String URL, String Message, String defaultValue, JsPromptResult result) {// Step 2: Determine whether the url is required according to the protocol parameters (same principle as method 2) // Generally judge according to scheme (protocol format) & authority (protocol name) (first two parameters) // The url passed in ="js://webview? arg1=111&arg2=222"Uri = uri.parse (message); // If url protocol = pre-agreed js protocol, parse the following parsing parametersif ( uri.getScheme().equals("js") {// if authority = the webview in the protocol in advance, that is, the protocol that represents the convention // so intercept the URL, the JS below starts to call the method that Android needsif (uri.getAuthority().equals("webview") {// Step 3: Execute the logic system.out.println ("Js calls Android methods"); HashMap<String, String> params = new HashMap<>(); Set<String> collection = uri.getQueryParameterNames(); // Parameter result: represents the return value of the message box (input value) result.confirm((useid=123456));
                }
                    return true;
            }
                returnsuper.onJsPrompt(view, url, message, defaultValue, result); } @override public Boolean onJsAlert(WebView view, String URL, String message, JsResult result) {returnsuper.onJsAlert(view, url, message, result); } @override public Boolean onJsConfirm(WebView view, String URL, String message, JsResult result) {returnsuper.onJsConfirm(view, url, message, result); }}); }}Copy the code

The advantages and disadvantages

Advantages:

There is no addJavascriptInterface() vulnerability;

Can meet most situations of interaction scenarios;

Result.confirm () is easy to call back to JS to retrieve data;

Disadvantages: Complex adaptation, need to carry on the constraints of the protocol

(2) Android calls JS methods

There are two methods for Android to call JS, which are described in turn below.

Webbview.loadurl ()

Step 1: Load the JS code, as follows: javascript. HTML into the SRC /main/assets folder

<! DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Carson_Ho</title> <scriptfunction callJS(){
             alert("Android calls the callJS method");
         }
    </script>
   </head>
</html>

Copy the code

Step 2: In Android, call JS code with WebView Settings

 public class MainActivity extends AppCompatActivity {
    WebView mWebView;
    Button button;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mWebView =(WebView) findViewById(R.id.webview);
        WebSettings webSettings = mWebView.getSettings();
        webSettings.setJavaScriptEnabled(true); / / set with Js interaction permissions webSettings. SetJavaScriptCanOpenWindowsAutomatically (true); // Set to allow JS popover mwebView.loadURL ("file:///android_asset/javascript.html");
        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {@override public void onClick(View v) {// send a message via Handler mwebView.post (new)Runnable() {
                    @Override
                    public void run() {// call the javascript callJS() method mwebview.loadURL ()"javascript:callJS()"); }}); }}); // Set the alert popover to display the resultssetWebChromeClient mWebView.setWebChromeClient(new WebChromeClient()); }}Copy the code

2, through the WebView. EvaluateJavascript () – Android4.4 later

EvaluateJavascript () is more efficient and concise to use. But the execution of this method does not cause the page to refresh, whereas the execution of loadUrl() does.

mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {@override public void onReceiveValue(String value) {// This is the result returned by js}});Copy the code

3. Suggestions for use

Use a mix of these two methods, i.e. loadUrl() for Android 4.4 below and evaluateJavascript() for Android 4.4 above.

if (Build.VERSION.SDK_INT < 18) {
    mWebView.loadUrl("javascript:callJS()");
} else{mWebView. EvaluateJavascript ("javascript:callJS()", new ValueCallback<String>() {@override public void onReceiveValue(String value) {// This is the result returned by js}}); }Copy the code

Ii. Problems encountered during development

1. The Alert cannot be displayed

If WebChromeClient is not set, use the following code:

myWebView.setWebChromeClient(new WebChromeClient());
Copy the code

2. Uncaught ReferenceError: functionName is not defined

The cause of the problem is that the JS method is called before the page’s JS code is loaded. The solution is to call the JS method after the page has loaded. For example: just open the HTML page, to call the JS returnResult method

webview.loadUrl("file:///android_asset/javascript.html");
   
String result = (useid=123456);
webview.loadUrl("javascript:returnResult(\"" + result + "\")");
Copy the code

Set it up as follows:

webview.setWebViewClient(new WebViewClient(){
      @Override
            public void onPageFinished(WebView view, String url) {
                super.onPageFinished(view, url);

                String result = (useid=123456);
                webview.loadUrl("javascript:returnResult(\"" + result + "\")"); }});Copy the code

3, Uncaught TypeError: Object [Object Object] has no method

Security restriction issue, appear on Android4.2 and above machines.

Solution: Set targetSdkVersion to 17 or higher and introduce the @javascriptInterface annotation.

Code obfuscation problem. If the unobfuscated version works fine, but the obfuscated version runs incorrectly and says Uncaught TypeError: Object [Object Object] has no method, you did not do the obfuscated exception. Add a code solution like this to the obfuscation file:

Add confusion to proguard-rules.pro. -keepattributes *Annotation* -keepattributes *JavascriptInterface* -keep public class xx.xxx.JSKit{ public <methods>; } with xx, XXX.. JSKit is a class that does not need to be confusedCopy the code

4, All WebView methods must be called on the same thread

The Java callback thread after the JS call is not the main thread. To resolve the above exception, simply place the WebView operations in the main thread.

webView.post(new Runnable() {
    @Override
    public void run() {
        webView.loadUrl(YOUR_URL).
    }
});
Copy the code

Three, reference thanks

www.cnblogs.com/android-blo…

www.jianshu.com/p/345f4d8a5…