Intermodulation between Android and JS through WebView
I. Overview:
Android and JS interact through WebView, in fact:
- Android calls JS code;
- JS calls Android code;
The link between the two is WebView.
Android calls JS code in the following ways:
- loadUrl() through WebView;
- Evaluate Javascript () via WebView;
There are three ways for JS to call Android code:
- Object mapping through WebView's addJavascriptInterface();
- Intercept url by calling back the shouldOverrideUrlLoading () method of WebViewClient;
- Intercept JS dialog alert(), confirm(), prompt() messages via WebChromeClient onJsAlert(), onJsConfirm(), onJsPrompt() method callbacks.
-
2. Android calls JS method through WebView
1. Use details of loadUrl() via WebView:
In Android, the click event is triggered, that is, the method in WebView JS (text name webjs) is invoked.
—— For example convenience and response speed, Andorid calls local JS code to explain. In actual development, Andorid may call JS code in local Html file or JS code in HTML page loaded by network.
Steps:
A. HTML files that need to be invoked for JS code are placed in the src/main/assets folder:
The code in the webjs.html file is as follows:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Title</title> <h4>JS and Android(Java)Interaction</h4> <input type="button" value="js transfer Android" onclick="ok()"> <script type="text/javascript"> function ok() { android.webMessage("JS and Android(Java)Interaction"); } function javaCallJavascriptNoParam() { alert("Android Medium Java adopt WebView Called javaScript Parametric structure"); document.getElementById("javacalljs").innerHTML = "Android Medium Java adopt WebView Called javaScript Parametric structure<br\>"; } function javaCallJavascript(param) { alert(param); document.getElementById("javacalljs").innerHTML = param; } </script> </head> <body> <div id="javacalljs"></div> <br/><br/> </body> </html>
B. Setting common properties of WebView in Activity:
1) Let Web pages be displayed in WebView instead of being opened in a browser
mWeb.setWebViewClient(new WebViewClient());
2) Setting up the browsing interface of startup overview mode and using a wide-screen view window.
WebSettings setting = mWeb.getSettings(); setting.setUseWideViewPort(true); setting.setLoadWithOverviewMode(true);
3) Permission to call Js:
setting.setJavaScriptEnabled(true); setting.setJavaScriptCanOpenWindowsAutomatically(true);
B. Load App's built-in html file in Activity:
String URL = "file:///android_asset/webjs.html"; mWeb.loadUrl(URL);
C. Call the javascript code in the html file in Activity's Java code:
@OnClick(R.id.tvLoadWeb) public void onViewClicked() { mWeb.loadUrl("javascript:javaCallJavascript('Android Medium Java adopt WebView Called javaScript A PARAMETRIC CONSTRUCTION METHOD')"); }
Click on the button and the pop-up effect is as follows:
Attention should be paid here! Attention!! Attention!!! Important things are to be repeated for 3 times:
1. There is no concept of polymorphism and overload in Java in javascript, that is to say, no method with the same name can appear, even if the following parameters are different (the most striking is parametric and non-parametric). Like the method function javaCallJavascriptNoParam() {} and function javaCallJavascript ({}) in the html file above, if we all use the same method name, sometimes the call will be wrong: Undefined;
2. In the loadUrl method of WebView calling javascript method:
mWeb.loadUrl("javascript:javaCallJavascript('Android Medium Java adopt WebView Called javaScript A PARAMETRIC CONSTRUCTION METHOD')");
Some blogs start with html file names, such as "webjs" in this case.
mWeb.loadUrl("webjs:javaCallJavascript(Android Medium Java adopt WebView Called javaScript A PARAMETRIC CONSTRUCTION METHOD')");
The blogger said that it was successful in writing and calling, but I never succeeded in calling when I was learning. I don't know whether I didn't learn from home or what. In short, the call was not smooth. But one thing is certain: the former method is sure to be successfully invoked.
3. It's the same whether there are parameters or not. It's just not to pass parameters. That's not much to say.
3. JS calls Java through WebView
Invoke step:
1. Create an html file and place it in the src/main/assets folder:
The webjs.html file code is as follows:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Title</title> <h4>JS and Android(Java)Interaction</h4> <input type="button" value="js transfer Android" onclick="ok()"> <script type="text/javascript"> function javaCallJsNoArgs() { document.getElementById("content1").innerHTML += "js call java,Java Callback js Parametric method<br\>" } function javaCallJsExistArgs(args) { document.getElementById("content2").innerHTML += "js call java,Java Callback js Parametric method,The parameters are:" + args + "<br\>"; } </script> </head> <body> <div id="javacalljs"></div> <br/><br/> JS and Android(Java)Interaction<br/><br/><br/> <input type="button" value="JS transfer Java Parametric-free method and callback" onclick="js_call_java.javaCallJsMethod1()"/><br/> <div id="content1"></div> <br/><br/> <input type="button" value="JS transfer Java Reference method and callback" onclick="js_call_java.javaCallJsMethod2()"/><br/> <div id="content2"></div> </body> </html>
2. Layout the WebView control in Activity's xml file and set the corresponding properties of Java and Js intermodulation:
WebSettings setting = mWvJsCallJava.getSettings(); setting.setJavaScriptEnabled(true); /The role of the following individual attributes has been described before and will not be repeated. setting.setJavaScriptCanOpenWindowsAutomatically(true); setting.setUseWideViewPort(true); setting.setLoadWithOverviewMode(true); //Setting the encoding format setting.setDefaultTextEncodingName("utf-8"); WebViewUtils.webViewSetting(setting, true, false, getApplicationContext()); WebViewUtils.setViewClient(mWvJsCallJava); WebViewUtils.setChromeClien(mWvJsCallJava, getApplicationContext()); //Adding an object so that the js object can access the method of the object mWvJsCallJava.addJavascriptInterface(new Javascript(), "js_call_java");
The Javascript.class file code above is as follows:
final class Javascript{ public Javascript(){ } @JavascriptInterface public void javaCallJsMethod1() { runOnUiThread(new Runnable() { @Override public void run() { mWvJsCallJava.loadUrl("javascript:javaCallJsNoArgs()"); } }); } @JavascriptInterface public void javaCallJsMethod2() { runOnUiThread(new Runnable() { @Override public void run() { mWvJsCallJava.loadUrl("javascript:javaCallJsExistArgs('I am bright moon.')"); } }); } }
The function of the parameter "js_call_java" in the addJavascriptInterface() method:
My understanding is:
He is an identifier when WebView interacts with JS. Clicking on the button in WebView, the button calls the method behind the identifier in Java code through the "js_call_java" identifier. For example:
<input type="button" value="JS transfer Java Reference method and callback" onclick="js_call_java.javaCallJsMethod2()"/>
After clicking on this button, the system will call the corresponding method after him through WebView according to the identifier "js_call_java". Here, the method after the identifier "js_call_java" is: javaCallJsMethod2(), then he calls the javaCallJsMethod2() method in the Javascript object.
The code for the WebViewUtils.class file above is posted at the end.
3. Loading html files
String URL = "file:///android_asset/webjs.html"; mWvJsCallJava.loadUrl(URL);
This completes the JS call to Java code. We can also continue to call JS methods in Java code, as above:
mWvJsCallJava.loadUrl("javascript:javaCallJsExistArgs('I am bright moon.')");
The effect of JS calling Java code is as follows:
Here the Java code calls the javaCallJsExistArgs() method in the js code. The other is the same.
So far, the interaction between JS and Java has come to an end.
The code for the WebViewUtils.class file is as follows:
public class WebViewUtils { public static void webViewSetting(WebSettings setting, boolean setTure, boolean setFalse, Context context) { ///////////////////////////////////////////////////////////////////////////////////////// /**If database storage is allowed, read and write permissions are required, default false**/ /**See how the setDatabasePath API correctly sets up database storage. * This setting has global characteristics, and all WebView instances of the same process share the same configuration. Note: Ensure any WebView in the same process * Modify this property before loading the page, because setting WebView after that might ignore the configuration**/ setting.setDatabaseEnabled(setTure); ///////////////////////////////////////////////////////////////////////////////////////// /**If the cache is set, a valid cache path must be set to take effect. The default value is false.**/ setting.setAppCacheEnabled(true); setting.setAppCachePath(context.getCacheDir().getAbsolutePath()); ///////////////////////////////////////////////////////////////////////////////////////// /**Whether positioning is allowed, default true. * Note: In order to ensure that positioning can be used, the following points should be guaranteed: * 1,Application android.Manifest.permission#ACCESS_COARSE_LOCATION Location permission is required * 2,Application We need to implement the monitoring callback of WebChromeClient#onGeolocation Permission ShowPrompt. * Receiving notifications of Js location requests accessing geographical locations**/ setting.setGeolocationEnabled(setTure); ///////////////////////////////////////////////////////////////////////////////////////// /**Whether to save form data, default false**/ setting.setSaveFormData(setTure); ///////////////////////////////////////////////////////////////////////////////////////// /**Whether to start the overview mode browsing interface, when the page width exceeds the display width of WebView, narrow the page to adapt to WebView. Default false * It needs to be used in conjunction with setUseWideViewPort() to take effect.**/ setting.setLoadWithOverviewMode(setTure); /**Whether the meta tag attribute of ViewPort is supported, if the page has the width specified by the ViewPort metatag, * The value specified by meta tag is used, otherwise the default is to use a wide-screen view window (width when horizontal and vertical screens)**/ setting.setUseWideViewPort(setTure); ///////////////////////////////////////////////////////////////////////////////////////// /**Notify WebView whether it needs to set a node to get focus when WebView#requestFocus(int,android.graphics.Rect) * Default true when called**/ setting.setNeedInitialFocus(setTure); ///////////////////////////////////////////////////////////////////////////////////////// /**Layout algorithm**/ setting.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL); ///////////////////////////////////////////////////////////////////////////////////////// /**Set whether (supports) JS methods are allowed to execute**/ setting.setJavaScriptEnabled(setTure); ///////////////////////////////////////////////////////////////////////////////////////// /**Whether to support multiple windows, if set to true, the onCreateWindow method of WebChromeClient must be implemented by the main program, default false**/ setting.setSupportMultipleWindows(setTure); ///////////////////////////////////////////////////////////////////////////////////////// /**Whether JS is allowed to open windows automatically. Default false**/ setting.setJavaScriptCanOpenWindowsAutomatically(setFalse); ///////////////////////////////////////////////////////////////////////////////////////// /**Whether to allow access to the content URL of WebView allows WebView to access the content stored by Content Privider. Default true**/ setting.setAllowContentAccess(setTure); /**Whether to allow access to WebView internal files, default true, do not know what it means**/ setting.setAllowFileAccess(setTure); ///////////////////////////////////////////////////////////////////////////////////////// /**Whether to allow Javascript loaded through file url to read local files, default value false**/ setting.setAllowFileAccessFromFileURLs(setTure); ///////////////////////////////////////////////////////////////////////////////////////// /**Is it allowed to read all resources (including files, http,https) through Javascript loaded by file url, default value false**/ setting.setAllowUniversalAccessFromFileURLs(setTure); ///////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////// Resource Loading /**Whether or not to load images automatically, including images from the network, built-in images from app, and images from Sdcard**/ setting.setLoadsImagesAutomatically(setTure); /**Whether or not to load network pictures automatically**/ setting.setBlockNetworkImage(setTure); /**Whether or not to load network resources**/ setting.setBlockNetworkLoads(setTure); ///////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////// zoom /**Whether to support zooming, with setBuiltInZoomControls, default true**/ setting.setSupportZoom(setTure); /**Whether or not to use WebView's built-in zoom component, which consists of zoom control floating on the window and gesture zoom control, defaults to false**/ setting.setBuiltInZoomControls(setFalse); /**Is the built-in zoom control displayed (a bit like the progress bar, with "-" on the left and "+" on the right)?**/ setting.setDisplayZoomControls(setFalse); ///////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////// Default text encoding, default value "UTF-8" /**Set the encoding format of the page, default UTF-8**/ setting.setDefaultTextEncodingName("utf-8"); ///////////////////////////////////////////////////////////////////////////////////////// /**Set the default font size, default 16, value range [1-72], beyond the range, use the upper limit. * In addition, there are a series of ways to set the size of various fonts in WebView: setXxxFontSize (int size)**/ setting.setDefaultFontSize(16); /**Default equal width font size, default value 16**/ setting.setDefaultFixedFontSize(16); /**Minimum text size, default 8**/ setting.setMinimumFontSize(8); /**Minimum text logic size, default 8**/ setting.setMinimumLogicalFontSize(8); /**Percentage of text zoom, default value 100**/ setting.setTextZoom(100); ///////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////// Setting fonts /**Whether to support multiple windows if set to true, the onCreateWindow method of WebChromeClient must be implemented by the main program, default false * In addition, there are a series of ways to set various fonts of WebView: setXxxFontFamily (String font)**/ setting.setStandardFontFamily("sans-serif"); /**serif font, default value**/ setting.setSerifFontFamily("serif"); /**sans-serif by default**/ setting.setSansSerifFontFamily("sans-serif"); /**Equal width font, default value "monospace"**/ setting.setFixedFontFamily("monospace"); /**Handwritten (cursive), default value "cursive"**/ setting.setCursiveFontFamily("cursive"); /**Fantasy, default value "fantasy"**/ setting.setFantasyFontFamily("fantasy"); ///////////////////////////////////////////////////////////////////////////////////////// /**Storage, HTML5 DOM storage API enabled, default false*/ setting.setDomStorageEnabled(true); ///////////////////////////////////////////////////////////////////////////////////////// /**Whether you need user gestures to play Media, default true**/ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { setting.setMediaPlaybackRequiresUserGesture(setTure); } ///////////////////////////////////////////////////////////////////////////////////////// /**Set the WebView loading behavior for loading unsafe resources. KITKAT version and the following default is MIXED_CONTENT_ALWAYS_ALLOW * In this way, LOLLIPOP defaults to MIXED_CONTENT_NEVER_ALLOW. Strongly recommended: use MIXED_CONTENT_NEVER_ALLOW**/ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { setting.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); } ///////////////////////////////////////////////////////////////////////////////////////// /**Whether to grate when leaving the screen (increasing memory consumption), default false**/ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { setting.setOffscreenPreRaster(false); } ///////////////////////////////////////////////////////////////////////////////////////// /**Decide whether to retrieve data from the network based on cache-control * LOAD_DEFAULT Default loading mode * LOAD_CACHE_ELSE_NETWORK Use caching according to network conditions * LOAD_NO_CACHE No caching * LOAD_CACHE_ONLY Use only caching**/ if (isNetworkAvailable(context)) { //Have a network, get from the network. setting.setCacheMode(WebSettings.LOAD_DEFAULT); } else { // No network, offline loading cache (even if it has expired) setting.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); } } public static void setViewClient(WebView mWeb) { /*****Function: Let HTML pages be displayed in WebView instead of being opened with a browser**/ mWeb.setWebViewClient(new WebViewClient() { @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { //To start loading page calls, we can set up a loading page to tell the user that the program is waiting for the network response. super.onPageStarted(view, url, favicon); } @Override public void onPageFinished(WebView view, String url) { //Called at the end of page loading. We can close the loading bar and switch program actions. super.onPageFinished(view, url); } @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { //Make the web page open without calling the system browser, but in this WebView display view.loadUrl(url); return super.shouldOverrideUrlLoading(view, url); } @Override public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { return super.shouldOverrideUrlLoading(view, request); } @Override public void onLoadResource(WebView view, String url) { //Page resources are called when they are loaded, and each resource (such as a picture) is called once. super.onLoadResource(view, url); } @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { //Called when the server loading the page has an error, such as 404. switch (errorCode) { //HttpStatus.SC_NOT_FOUND case 404: break; default: break; } super.onReceivedError(view, errorCode, description, failingUrl); } @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { //Dedicated to https requests super.onReceivedSslError(view, handler, error); } }); } public static void setChromeClien(WebView mWeb, final Context context) { mWeb.setWebChromeClient(new WebChromeClient() { @Override public void onProgressChanged(WebView view, int newProgress) { //Page loading progress: newProgress is the percentage of loading progress: 0 <= newProgress <= 100; super.onProgressChanged(view, newProgress); } @Override public void onReceivedTitle(WebView view, String title) { //The title of the page to load, such as http://www.baidu.com: Baidu; http://www.ifeng.com: Phoenix.com. super.onReceivedTitle(view, title); } @Override public boolean onJsAlert(WebView view, String url, String message, JsResult result) { //Allow javascript warning boxes to pop up. message is the content of the warning boxes. return super.onJsAlert(view, url, message, result); } @Override public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) { //Allow javascript confirmation boxes to pop up, and message is the confirmation information. new AlertDialog.Builder(context) .setTitle("Information validation") .setMessage(message) .setPositiveButton("Determine", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { result.confirm(); } }) .setNegativeButton("cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { result.cancel(); } }) .setCancelable(false) .show(); // Return Boolean Value: Determine whether to confirm or cancel when clicking // true means click confirmation; false means click cancel; return super.onJsConfirm(view, url, message, result); } @Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { //Allow pop-up of javascript input box EditText et = new EditText(context); AlertDialog.Builder dialog = new AlertDialog.Builder(context); dialog.setTitle("Input information").setView(et) .setPositiveButton("Sure", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { //Returns the value in the input box. } }) .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { //Return null. } }).setCancelable(false).show(); return super.onJsPrompt(view, url, message, defaultValue, result); } }); } public static void clearCache(WebView mWeb) { //Clear the cache left by web access by targeting the entire application. mWeb.clearCache(true); //Clear all history records of current webview access mWeb.clearHistory(); //This api only cleans up the form data that is automatically filled, and it does not cleanse up the data that WebView stores locally. mWeb.clearFormData(); } public static boolean isNetworkAvailable(Context context) { // Get all connection management objects (including wi-fi,net, etc.) try { ConnectivityManager connectivity = (ConnectivityManager) context .getSystemService(Context.CONNECTIVITY_SERVICE); if (connectivity != null) { // Getting Objects for Network Connection Management NetworkInfo info = connectivity.getActiveNetworkInfo(); if (info != null && info.isConnected()) { // Determine whether the current network is connected if (info.getState() == NetworkInfo.State.CONNECTED) { return true; } } } } catch (Exception e) { e.printStackTrace(); } return false; } public static Bitmap getLocalBitmap(String URL) { try { FileInputStream input = new FileInputStream(URL); Log.i("TAG", "onViewClicked: input is Empty?:" + (input == null)); return BitmapFactory.decodeStream(input); } catch (Exception e) { Log.i("TAG", "onViewClicked: Throw exception:" + e); return null; } } }