The article directories
- Android Default method
-
- Android calls JS code
- JS to call Android code
- Using jsbridge
- WebView loads web pages select files to upload
Android Default method
The most comprehensive summary of Android WebView and JS interaction
Android calls JS code
There are two ways to call JS code on Android:
- LoadUrl () via WebView
- EvaluateJavascript via WebView ()
Example: Click the Android button to call callJS () in WebView JS (text name: javascript)
Example: for the convenience of display, this article is to use Andorid call local JS code description; In practice, Android calls more remote JS code, changing the loaded JS code path to a URL
1. Create javascript. HTML and put it in the SRC /main/assets folder (select main and right-click new-folder-assets Folder).
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="Generator" content="EditPlus ®">
<meta name="Author" content="">
<meta name="Keywords" content="">
<meta name="Description" content="">
<title>Document</title>
</head>
<body>
<script>
// The method Android needs to call
function callJS(){
alert("Android calls the JS callJS method.");
}
</script>
</body>
</html>
Copy the code
2. Call JS code in Android via 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();
// Set the permission to interact with Js
webSettings.setJavaScriptEnabled(true);
// Set to allow JS pop-ups
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
// Load the JS code first
// The format is file:/// / android_asset/filene.html
mWebView.loadUrl("file:///android_asset/javascript.html");
button = (Button) findViewById(R.id.button);
button.setOnClickListener(v -> {
// Send messages through Handler
mWebView.post(new Runnable() {
@Override
public void run(a) {
// Note that the name of the called JS method must correspond to
// Call the javascript callJS() method
mWebView.loadUrl("javascript:callJS()"); }}); });// The js dialog box needs to be supported because the popover check result is set
// The webView is just the carrier, the content rendering needs to use the webviewChromClient class to achieve
// Handle JavaScript dialogs by setting WebChromeClient objects
// Set the Alert() function that responds to js
mWebView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder b = new AlertDialog.Builder(MainActivity.this);
b.setTitle("Alert");
b.setMessage(message);
b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) { result.confirm(); }}); b.setCancelable(false);
b.create().show();
return true; }}); }}Copy the code
Note: the JS code call must be called after the onPageFinished() callback, otherwise the method onPageFinished() belongs to the WebViewClient class will not be called, mainly at the end of page loading
Method 2: Through evaluateJavascript () of the WebView this method does not refresh the page, whereas the first method (loadUrl) does. It is more efficient and simpler to use than the first method. Available only after Android 4.4
button.setOnClickListener(v -> {
// Send messages through Handler
mWebView.post(new Runnable() {
@Override
public void run(a) {
mWebView.evaluateJavascript("javascript:callJS()".new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
// This is the result returned by js}}); }}); });Copy the code
JS to call Android code
There are three ways to call Android code with JS:
- Through the WebView
addJavascriptInterface()
Object mapping - Through the WebViewClient
shouldOverrideUrlLoading()
Method callback intercepts url - Through WebChromeClient
onJsAlert()
,onJsConfirm()
,onJsPrompt()
Method callback intercepts the JS dialog boxalert()
,confirm()
,prompt()
The message
Demo effect:
Method 1: Use WebView addJavascriptInterface() to map objects. 1. Define an Android class that maps with JS objects: AndroidtoJs
public class AndroidtoJs extends Object {
// Define the method that JS needs to call
// Methods called by javascript must be annotated with @javascriptInterface
@JavascriptInterface
public void hello(String msg) {
System.out.println("JS calls Android's Hello method"); }}Copy the code
2. Place the.html JS code in the SRC /main/assets folder
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="Generator" content="EditPlus ®">
<meta name="Author" content="">
<meta name="Keywords" content="">
<meta name="Description" content="">
<title>Document</title>
<script>
function callAndroid(){
// Because of the object mapping, calling the test object is equal to calling the Android mapping object
test.hello("Js calls the Hello method in Android");
}
</script>
</head>
<body>// Click the button to call the callAndroid function<button type="button" id="button1" onclick="callAndroid()"></button>
</body>
</html>
Copy the code
3. Set the mapping between Android class and JS code through WebView in Android
// Map Java objects to JS objects with addJavascriptInterface()
// Argument 1: Javascript object name
// Parameter 2: Java object name
mWebView.addJavascriptInterface(new AndroidtoJs(), "test");//AndroidtoJS class objects map toJS test objects
Copy the code
This method has serious vulnerability issues, as described in the article: Android WebView Usage Vulnerabilities you didn’t know about
Method 2: Android shouldOverrideUrlLoading() with WebViewClient shouldOverrideUrlLoading() Parse the protocol of the URL. If a predefined protocol is detected, call the corresponding method. 1
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="Generator" content="EditPlus ®">
<meta name="Author" content="">
<meta name="Keywords" content="">
<meta name="Description" content="">
<title>Document</title>
<script>
function callAndroid2(){
/* The url protocol is: js://webview? arg1=111&arg2=222*/
document.location = "js://webview? arg1=111&arg2=222";
}
</script>
</head>
<body>
<button type="button" id="button" value="Js calling Android method 2" onclick="callAndroid2()"></button>
</body>
</html>
Copy the code
ShouldOverrideUrlLoading ()
mWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// Step 2: Check whether it is the required URL according to the protocol parameters
// Use scheme (protocol format) and authority (protocol name) to judge (first two parameters)
// Suppose the incoming URL = "js://webview? Arg1 =111&arg2=222"
Uri uri = Uri.parse(url);
// If the url protocol = the pre-agreed JS protocol
// Parse the parameters down
if (uri.getScheme().equals("js")) {
// If authority = pre-specify webViews in the protocol, that is, represent the protocol that conforms to the convention
// So intercepting the URL, now JS starts calling the method Android needs
if (uri.getAuthority().equals("webview")) {
// Step 3:
// The logic needed to execute JS
System.out.println("Js calls Android methods");
// Can take parameters on the protocol and pass them to Android
HashMap<String, String> params = new HashMap<>();
Set<String> collection = uri.getQueryParameterNames();
}
return true;
}
return super.shouldOverrideUrlLoading(view, url); }});Copy the code
This method does not have the vulnerability of method 1, but JS obtaining the return value of Android method is complicated
If JS wants to get the return value of the Android method, it can only execute the JS method via WebView loadUrl () to pass the return value back.
/ / Android: MainActivity. Java
mWebView.loadUrl("javascript:returnResult("+3+")");
/ / JS: javascript. HTML
function returnResult(result){
alert("result is" + result);
}
Copy the code
Method 3: Via WebChromeClientonJsAlert()
,onJsConfirm()
,onJsPrompt()
Method callback intercepts the JS dialog boxalert()
,confirm()
,prompt()
The message
Android via WebChromeClientonJsAlert()
,onJsConfirm()
,onJsPrompt()
Method callback intercepts the JS dialog box, respectively
(the above three methods), get their message content, and then parse it
The following example will intercept the JS input box (i.eprompt()
Methods). Common intercepts are: intercepts JS input fields (i.eprompt()
Method), because onlyprompt()
Can return any type of value, the operation of the most comprehensive convenient, more flexible; whilealert()
Dialog box has no return value;confirm()
The dialog box can only return two values in two states (OK/cancel)
1. Place javascript in assets folder
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="Generator" content="EditPlus ®">
<meta name="Author" content="">
<meta name="Keywords" content="">
<meta name="Description" content="">
<title>Document</title>
<script>
function callAndroid3(){
Call prompt ()
var result=prompt("js://webview? arg1=111&arg2=222");
alert("demo " + result);
}
</script>
</head>
<body>
<button type="button" id="button3" onclick="callAndroid3()">Js calls Android mode 3</button>
</body>
</html>
Copy the code
The onJsPrompt() callback is triggered when the above JS code is loaded using mwebview.loadurl (“file:///android_asset/javascript.html”) : If it intercepts a warning box (alert()), the onJsAlert() callback is triggered; If it intercepts a confirmation box (that is, confirm()), the onJsConfirm() callback is triggered;
2. Clone onJsPrompt() on Android via WebChromeClient
mWebView.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
// Check whether it is the required URL according to the protocol parameters (same principle as method 2)
// Use scheme (protocol format) and authority (protocol name) to judge (first two parameters)
// Suppose the incoming URL = "js://webview? Arg1 =111&arg2=222"
Uri uri = Uri.parse(message);
// If the url protocol = the pre-agreed JS protocol
// Parse the parameters down
if ( uri.getScheme().equals("js")) {
// If authority = pre-specify webViews in the protocol, that is, represent the protocol that conforms to the convention
// So intercepting the URL, now JS starts calling the method Android needs
if (uri.getAuthority().equals("webview")) {
//
// The logic needed to execute JS
System.out.println("Js calls Android methods");
// Can take parameters on the protocol and pass them to Android
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("Js called the Android method successfully");
}
return true;
}
return super.onJsPrompt(view, url, message, defaultValue, result);
}
/*// Alert () and confirm() intercept the same principle, @override public Boolean onJsAlert(WebView view, String URL, String message, JsResult result) { return super.onJsAlert(view, url, message, result); } * /
// Intercepts the JS validation box
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
return super.onJsConfirm(view, url, message, result); }});Copy the code
Using jsbridge
Android and Js interaction JSBridge using Android BridgeWebView simple use, as well as climb the pit
AddJavascriptInterface below Android4.2 has security vulnerabilities. Although addJavascriptInterface is replaced by @javascriptInterface after Android4.2, due to compatibility and security problems, Basically, we don’t use the addJavascriptInterface method or the @javascriptInterface annotation provided by Android to implement it, so we have to find a safe and compatible solution for all versions of Android
1. Add maven {url “https://jitpack.io”} to the gradle file
allprojects {
repositories {
google()
jcenter()
maven { url "https://jitpack.io"}}}Copy the code
Add dependencies to Module gradle
dependencies {
...
implementation 'com. Making. Lzyzsd: jsbridge: 1.0.4'
}
Copy the code
3. Replace WebView with BridgeWebView in the layout file
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="10dp">
<EditText
android:id="@+id/et"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/bt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Android calls JS methods" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
android:text="Here's the WebView." />
<com.github.lzyzsd.jsbridge.BridgeWebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Copy the code
4. Code in the Activity
public class JsBridgeActivity extends AppCompatActivity {
private EditText et;
private Button bt;
private BridgeWebView webview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_jsbridge);
et = (EditText) findViewById(R.id.et);
bt = (Button) findViewById(R.id.bt);
webview = (BridgeWebView) findViewById(R.id.webview);
webview.setDefaultHandler(new DefaultHandler());
webview.setWebChromeClient(new WebChromeClient());
webview.loadUrl("file:///android_asset/test.html");
This method is called when a callHandler method is called in JS (handlerName must be the same as in JS).
webview.registerHandler("submitFromWeb".new BridgeHandler() {
@Override
public void handler(String data, CallBackFunction function) {
Log.e("TAG"."Js returns:" + data);
// Displays messages passed from JS to Android
Toast.makeText(JsBridgeActivity.this."Js return." + data, Toast.LENGTH_LONG).show();
//Android returns a message to JS
function.onCallBack("I am js calling Android to return data:"+ et.getText().toString()); }}); bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Call the method in js (must be the same as the handlerName in JS)
webview.callHandler("functionInJs"."Android calls js66".new CallBackFunction() {
@Override
public void onCallBack(String data) {
Log.e("TAG"."onCallBack:" + data);
Toast.makeText(JsBridgeActivity.this, data, Toast.LENGTH_LONG).show(); }}); }}); }}Copy the code
5. Put the HTML in the Assets folder
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="Width = device - width, initial - scale = 1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<! -- Compiled and minified CSS -->
<link rel="stylesheet" href="./materialize.min.css">
<! -- Compiled and minified JavaScript -->
<script src="./materialize.min.js"></script>
<title>Test</title>
</head>
<body>
<div class="input-field col s6">
<input placeholder="Please enter data" id="text1" type="text" class="validate">
</div>
<button class="waves-effect waves-light btn" onclick="testClick();">Js calls the Android method</button>
</body>
<script>
// call the Android method: receive the data from Android, and do some processing
function testClick() {
SubmitFromWeb is the name of the method. It must be the same as the name of the method when it was registered in Android
// Parameter 2: The data returned to the Android terminal can be a string, JSON, etc
// Parameter 3: the corresponding processing logic after js receives the data from Android
window.WebViewJavascriptBridge.callHandler(
'submitFromWeb'
, {'param': "JS received data successfully --"},function(responseData) {
alert(responseData)
}
);
}
//JS registers the event listener
function connectWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) {
callback(WebViewJavascriptBridge)
} else {
document.addEventListener(
'WebViewJavascriptBridgeReady'
, function() {
callback(WebViewJavascriptBridge)
},
false); }}// Register the callback function and call the initialization function on the first connection
connectWebViewJavascriptBridge(function(bridge) {
/ / initialization
bridge.init(function(message, responseCallback) {
var data = {
'Javascript Responds': 'Wee! '
};
alert("jasdashjd");
responseCallback(data);
});
//Android calls js methods: functionInJs method names need to be consistent and return notifications to Android
bridge.registerHandler("functionInJs".function(data, responseCallback) {
alert(data);
var data2 = document.getElementById("text1").value;
var responseData = "I am the data returned by Android calling the JS method --"+ data2;
responseCallback(responseData);
});
})
</script>
</html>
Copy the code
Operation effect:
WebView loads web pages select files to upload
Android uses WebView to load web pages and select file uploads
Create a new upload. HTML and place it under assets
<! DOCTYPEhtml>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,maximun-scale=1,mininum-scale=1,user-scale=1">
<tile>File selection</tile>
<style>
.filechooser {
width: 95%;
height: 50px;
-webkit-background-clip: border-box;
}
</style>
<script>
var display = function(){
var path = document.getElementById("file_chooser").textContent;
document.getElementById("filechooser_display").innerHTML("<b>"+path+"</b>");
}
</script>
</head>
<body>
<fieldset>
<input class="filechooser" id="file_chooser" type="file" placeholder="Select file" onchange="display()"
oninput="display()">
<p id="filechooser_display"></p>
</fieldset>
</body>
</html>
Copy the code
Activity
public class UploadActivity extends AppCompatActivity {
private WebView webView;
private ValueCallback<Uri> uploadFile;
private ValueCallback<Uri[]> uploadFiles;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_upload);
webView = findViewById(R.id.webview);
webView.setWebChromeClient(new WebChromeClient() {
/ / For Android 3.0 +
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
Log.i("test"."openFileChooser 1");
uploadFile = uploadFile;
openFileChooseProcess();
}
// For Android < 3.0
public void openFileChooser(ValueCallback<Uri> uploadMsgs) {
Log.i("test"."openFileChooser 2");
uploadFile = uploadFile;
openFileChooseProcess();
}
// For Android > 4.1.1
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
Log.i("test"."openFileChooser 3");
uploadFile = uploadFile;
openFileChooseProcess();
}
// For Android >= 5.0
public boolean onShowFileChooser(WebView webView, ValueCallback
filePathCallback, WebChromeClient.FileChooserParams fileChooserParams)
[]> {
Log.i("test"."openFileChooser 4:" + filePathCallback.toString());
uploadFiles = filePathCallback;
openFileChooseProcess();
return true; }}); webView.loadUrl("file:///android_asset/upload.html");
}
private void openFileChooseProcess(a) {
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("* / *");
startActivityForResult(Intent.createChooser(i, "Select file"), 0);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case 0:
if(uploadFile ! =null) {
Uri result = data == null|| resultCode ! = RESULT_OK ?null
: data.getData();
uploadFile.onReceiveValue(result);
uploadFile = null;
}
if(uploadFiles ! =null) {
Uri result = data == null|| resultCode ! = RESULT_OK ?null
: data.getData();
uploadFiles.onReceiveValue(new Uri[]{result});
uploadFiles = null;
}
break;
default:
break; }}else if (resultCode == RESULT_CANCELED) {
if(uploadFile ! =null) {
uploadFile.onReceiveValue(null);
uploadFile = null;
}
if(uploadFiles ! =null) {
uploadFiles.onReceiveValue(null);
uploadFiles = null; }}}@Override
protected void onResume(a) {
super.onResume();
webView.onResume();
}
@Override
protected void onPause(a) {
super.onPause();
webView.onPause();
}
/** * Ensure that the logout configuration can be released */
@Override
protected void onDestroy(a) {
if(webView ! =null) {
webView.destroy();
}
super.onDestroy(); }}Copy the code
A few notes on the code: Clicking on the button to upload the file simulates the form submission, which calls openFileChooser(…). Methods. The method then receives and processes the parameter ValueCallback < URI > uploadMsg. The URI is the file path selected locally. Then the Intent’s startActivityForResult(…) Method Go to the page for selecting a file. OnActivityResult (int requestCode, int resultCode, Intent Data)
Webkit no longer supports openFileChooser(…). Instead, use onShowFileChooser(…)
Download resources