一.简介
交互方式总结
Android与JS通过WebView互相调用方法,实际上是
Android 调用 JS 的代码。JS 调用 Android 的代码。二者沟通的桥梁是WebView。
二.Android 调用 JS
对于Android 调用 JS 代码的方法有2种
1.通过 WebView 的 loadUrl()方法。
2.通过 WebView 的 evaluateJavascript()方法。此方法专门用于异步调用JavaScript方法,并且能够得到一个回调结果。
通过WebView的loadUrl()代码详解
1.将需要调用的JS代码以.html格式放到src/main/assets文件夹里
2.abc.html代码
<script language="JavaScript" type="text/javascript">
var nowDate = new Date();
var nH=nowDate.getHours();
var nM=nowDate.getMinutes();
var nS=nowDate.getSeconds();
function MyOnclick() {
document.write("当前时间:" + nH+ ":" + nM + ":" + nS);
}
</script>
3.Android代码
package com.wjn.customwebviewjs;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.webkit.WebView;
import android.widget.TextView;
public class WebViewLoadUrlActivity extends AppCompatActivity {
private WebView webView;
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webviewloadurl);
initView();
}
/**
* 初始化各种View
*/
private void initView() {
webView = findViewById(R.id.activity_webviewloadurl_webview);
//设置WebSettings
WebViewSettingsUtils.setWebSettings(webView);
//webview加载网页
webView.loadUrl("file:///android_asset/abc.html");
//点击事件
textView = findViewById(R.id.activity_webviewloadurl_textview);
textView.setOnClickListener(new View.OnClickListener() {
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void onClick(View v) {
webView.loadUrl("javascript:MyOnclick()");
}
});
}
}
4.结果
点击按钮前
点击按钮后
5.总结
通过WebView的loadUrl方法可以实现简单的Android调用JS的方法。
通过WebView的evaluateJavascript()代码详解
优点:该方法比第一种方法效率更高、使用更简洁。
因为该方法的执行不会使页面刷新,而第一种方法(loadUrl )的执行则会。Android 4.4 后才可使用。
1.将需要调用的JS代码以.html格式放到src/main/assets文件夹里
2.def.html代码
<script language="JavaScript" type="text/javascript">
var nowDate = new Date();
var nH=nowDate.getHours();
var nM=nowDate.getMinutes();
var nS=nowDate.getSeconds();
function MyOnclick() {
return "当前时间:" + nH+ ":" + nM + ":" + nS;
}
</script>
3.Android代码
package com.wjn.customwebviewjs;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.webkit.ValueCallback;
import android.webkit.WebView;
import android.widget.TextView;
import android.widget.Toast;
public class WebViewEvaluateJavaScriptActivity extends AppCompatActivity {
private WebView webView;
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webviewevaluatejavascript);
initView();
}
/**
* 初始化各种View
*/
private void initView() {
webView = findViewById(R.id.activity_webviewevaluatejavascript_webview);
//设置WebSettings
WebViewSettingsUtils.setWebSettings(webView);
//webview加载网页
webView.loadUrl("file:///android_asset/def.html");
//点击事件
textView = findViewById(R.id.activity_webviewevaluatejavascript_textview);
textView.setOnClickListener(new View.OnClickListener() {
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
@Override
public void onClick(View v) {
evaluateJavascriptMethod();
}
});
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public void evaluateJavascriptMethod() {
webView.evaluateJavascript("javascript:MyOnclick()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//此处为 js 返回的结果
Toast.makeText(WebViewEvaluateJavaScriptActivity.this, value, Toast.LENGTH_LONG).show();
}
});
}
}
4.结果
5.两种方法对比
6.使用建议
两种方法混合使用,即Android 4.4以下使用方法1,Android 4.4以上方法2
三.JS 调用 Android
对于JS调用Android代码的方法有3种:
1.通过WebView的addJavascriptInterface()进行对象映射。
2.通过 WebViewClient 的shouldOverrideUrlLoading()方法回调拦截 url。
3.通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法拦截JS对话框alert()、confirm()、prompt() 消息
通过WebView的addJavascriptInterface()代码详解
通过 WebView的addJavascriptInterface()进行对象映射(从Android4.2开始。 只有添加 @JavascriptInterface 声明的Java方法才可以被JavaScript调用)
1.将需要调用的JS代码以.html格式放到src/main/assets文件夹里
2.hhh.html代码
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta http-equiv="Content-Language" content="zh-cn"/>
<title>Android WebView 与 Javascript 交互</title>
<head>
<style>
body {background-color:#e6e6e6}
.rect
{
color:white;
font-family:Verdana, Arial, Helvetica, sans-serif;
font-size:16px;
width:100px;
padding:6px;
background-color:#98bf21;
text-decoration:none;
text-align:center;
border:none;
cursor:pointer;
}
.inputStyle {font-size:16px;padding:6px}
</style>
</head>
<body>
<p>测试Android WebView 与 Javascript 交互</p>
<input id="name_input" class="inputStyle" type="text"/>
<a class="rect" onclick="sendInfoToJava()">JS调用Java</a>
<script>
function sendInfoToJava(){
//调用android程序中的方法,并传递参数
var name = document.getElementById("name_input").value;
window.AndroidWebView.showInfoFromJs(name);
}
</script>
</body>
</html>
3.Android代码
package com.wjn.customwebviewjs;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.webkit.WebView;
public class WebViewAddJavaScriptInterfaceActivity extends AppCompatActivity {
private WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webviewaddjavascriptinterface);
initView();
}
/**
* 初始化各种View
*/
private void initView() {
webView = findViewById(R.id.activity_webviewaddjavascriptinterface_webview);
//设置WebSettings
WebViewSettingsUtils.setWebSettings(webView);
//webview加载网页
webView.loadUrl("file:///android_asset/hhh.html");
// 添加js交互接口类,并起别名 AndroidWebView
webView.addJavascriptInterface(new JSAndroid(this), "AndroidWebView");
}
}
4.JS类
package com.wjn.customwebviewjs;
import android.content.Context;
import android.widget.Toast;
public class JSAndroid {
private Context context;
public JSAndroid(Context context) {
this.context = context;
}
@android.webkit.JavascriptInterface
public void showInfoFromJs(String name) {
Toast.makeText(context, "JS页面输入内容:" + name, Toast.LENGTH_LONG).show();
}
}
5.结果
6.特点
优点:使用简单 仅将Android对象和JS对象映射即可。
缺点:存在严重的漏洞问题。
Google 在Android 4.2 版本中规定对被调用的函数以 @JavascriptInterface进行注解从而避免漏洞攻击。
通过 WebViewClient 的方法shouldOverrideUrlLoading ()回调拦截 url 代码详解
1.具体原理
Android通过 WebViewClient 的回调方法shouldOverrideUrlLoading ()拦截 url,解析该 url 的协议,如果检测到是预先约定好的协议,就调用相应方法,即JS需要调用Android的方法。
2.将需要调用的JS代码以.html格式放到src/main/assets文件夹里
3.zzz.html代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>shouldOverrideUrlLoading ()拦截 url</title>
<script>
function callAndroid1(){
/*约定的url协议为:js://webview?arg1=111&arg2=222*/
document.location = "js://webview?arg1=111&arg2=222";
}
function callAndroid2(){
/*不拦截 直接跳转*/
document.location = "https://blog.csdn.net/weixin_37730482/article/category/6876458";
}
</script>
</head>
<!-- 点击按钮则调用callAndroid()方法 -->
<body>
<button type="button" id="button1" onclick="callAndroid1()">点击调用Android代码 拦截url</button>
<button type="button" id="button2" onclick="callAndroid2()">点击调用Android代码 不拦截url</button>
</body>
</html>
4.Android代码
package com.wjn.customwebviewjs;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import java.util.Iterator;
import java.util.Set;
public class WebViewShouldOverrideUrlLoadingActivity extends AppCompatActivity {
private WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webviewshouldoverrideurlloading);
initView();
}
/**
* 初始化各种View
*/
private void initView() {
webView = findViewById(R.id.activity_webviewshouldoverrideurlloading_webview);
//设置WebSettings
WebViewSettingsUtils.setWebSettings(webView);
//webview加载网页
webView.loadUrl("file:///android_asset/zzz.html");
//添加WebViewClient监听
webView.setWebViewClient(new MyWebViewClient());
}
/**
* WebViewClient 实现类
*/
private class MyWebViewClient extends WebViewClient {
/**
* shouldOverrideUrlLoading方法 拦截url
*/
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Uri uri = Uri.parse(url);
if (uri.getScheme().equals("js")) {// 如果url的协议 = 预先约定的 js 协议 就解析往下解析参数
if (uri.getAuthority().equals("webview")) {
// 可以在协议上带有参数并传递到Android上
Set<String> collection = uri.getQueryParameterNames();
Iterator<String> iterable = collection.iterator();
String value = "";
while (iterable.hasNext()) {
value = value + uri.getQueryParameter(iterable.next()) + ";";
}
Toast.makeText(WebViewShouldOverrideUrlLoadingActivity.this, value, Toast.LENGTH_LONG).show();
}
view.stopLoading();
return true;
} else {//不是 事先规定的url直接跳转url
view.loadUrl(url);
return false;
}
}
}
}
5.结果
点击第一个按钮(拦截url) 获取协议的参数值
点击第二个按钮(不拦截url) 直接WebView跳转页面
6.特点
优点:不存在方式1的漏洞。
缺点:JS获取Android方法的返回值复杂。
7.补充 Uri 获取url各种信息
代码
private void uriMethod(){
String url="http://api.svipmovie.com/front/columns/getVideoList.do?catalogId=402834815584e463015584e539330016&pnum=1";
Uri uri=Uri.parse(url);
Log.d("TAG","全部链接:"+url);
Log.d("TAG","-----------------------------");
String scheme=uri.getScheme();// http
Log.d("TAG","scheme:"+scheme);
Log.d("TAG","-----------------------------");
String authority=uri.getAuthority();// api.svipmovie.com
Log.d("TAG","authority:"+authority);
Log.d("TAG","-----------------------------");
String encodedpath=uri.getEncodedPath();// /front/columns/getVideoList.do
Log.d("TAG","encodedpath:"+encodedpath);
Log.d("TAG","-----------------------------");
String encodedquery=uri.getEncodedQuery();// catalogId=402834815584e463015584e539330016&pnum=1
Log.d("TAG","encodedquery:"+encodedquery);
Log.d("TAG","-----------------------------");
//全部参数Key
Set<String> collection = uri.getQueryParameterNames();
Iterator<String> iterable = collection.iterator();
String key = "";
while (iterable.hasNext()) {
key = key + iterable.next() + "###";
}
Log.d("TAG","key:"+key);
Log.d("TAG","-----------------------------");
//全部参数Value
Set<String> collections = uri.getQueryParameterNames();
Iterator<String> iterables = collections.iterator();
String value = "";
while (iterables.hasNext()) {
value = value + uri.getQueryParameter(iterables.next()) + "###";
}
Log.d("TAG","value:"+value);
Log.d("TAG","-----------------------------");
}
结果
D/TAG: 全部链接:http://api.svipmovie.com/front/columns/getVideoList.do?catalogId=402834815584e463015584e539330016&pnum=1
D/TAG: -----------------------------
D/TAG: scheme:http
D/TAG: -----------------------------
D/TAG: authority:api.svipmovie.com
D/TAG: -----------------------------
D/TAG: encodedpath:/front/columns/getVideoList.do
D/TAG: -----------------------------
D/TAG: encodedquery:catalogId=402834815584e463015584e539330016&pnum=1
D/TAG: -----------------------------
D/TAG: key:catalogId###pnum###
D/TAG: -----------------------------
D/TAG: value:402834815584e463015584e539330016###1###
D/TAG: -----------------------------
通过 WebChromeClient 的onJsAlert()+onJsConfirm()+onJsPrompt()方法分别拦截JS对话框alert()+confirm()+prompt()
在JS中,有三个常用的对话框方法
方式3的原理:Android通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调分别拦截JS对话框(即上述三个方法),得到他们的消息内容,然后解析即可。
1. 将需要调用的JS代码以.html格式放到src/main/assets文件夹里
2.kkk.html代码
<html>
<head>
<meta http-equiv = "Content-Type" content="text/html;charset=UTF-8"
<title>测试Js的三种不同对话框</title>
<script language="JavaScript">
function alertFun()
{
alert("Js的Alert警告对话框弹起!");
}
function confirmFun()
{
if(confirm("访问个人主页?"))
{location.href = "https://blog.csdn.net/weixin_37730482/article/category/6876458";}
else alert("取消访问!");
}
function promptFun()
{
var word = prompt("Prompt对话框","");
if(word)
{
alert("你输入了:"+word)
}else{alert("呵呵,你什么都没写!");}
}
function showAndroidToJsWithParameter(msg)
{
alert("Android 调用 JS 并传递参数:"+msg);
}
</script>
</head>
<body>
<p>三种对话框的使用</p>
<p>Alert对话框</p>
<p>
<input type="submit" name = "Submit1" value="点击显示JS的Alert对话框" onclick="alertFun()"/>
</p>
<p>Confirm对话框</p>
<p>
<input type="submit" name = "Submit2" value="点击显示JS的Confirm对话框" onclick="confirmFun()"/>
</p>
<p>Prompt对话框</p>
<p>
<input type="submit" name = "Submit3" value="点击显示JS的Prompt对话框" onclick="promptFun()"/>
</p>
</body>
</html>
3.Android代码
package com.wjn.customwebviewjs;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.widget.EditText;
import android.widget.TextView;
public class WebViewJsDialogActivity extends AppCompatActivity {
private TextView textView;
private WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webviewjsdialog);
initView();
}
/**
* 初始化各种View
*/
private void initView() {
webView = findViewById(R.id.activity_webviewjsdialog_webview);
//设置WebSettings
WebViewSettingsUtils.setWebSettings(webView);
//webview加载网页
webView.loadUrl("file:///android_asset/kkk.html");
//添加WebChromeClient监听
webView.setWebChromeClient(new MyWebChromeClient());
//TextView点击事件
textView = findViewById(R.id.activity_webviewjsdialog_textview);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String msg = "Android传递的参数666";
webView.loadUrl("javascript:showAndroidToJsWithParameter('" + msg + "')");
}
});
}
/**
* WebChromeClient 实现类
*/
private class MyWebChromeClient extends WebChromeClient {
/**
* onJsAlert方法 拦截JS的alert对话框
*/
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
//创建一个Builder来显示网页中的对话框
new AlertDialog.Builder(WebViewJsDialogActivity.this)
.setTitle("Alert对话框")
.setMessage(message)
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
}).setCancelable(false).show();
return true;
}
/**
* onJsConfirm方法 拦截JS的confirm对话框
*/
@Override
public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
new AlertDialog.Builder(WebViewJsDialogActivity.this)
.setTitle("Confirm对话框")
.setMessage(message)
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.cancel();
}
}).setCancelable(false).show();
return true;
}
/**
* onJsPrompt方法 拦截JS的prompt对话框
*/
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, final JsPromptResult result) {
//①获得一个LayoutInflater对象factory,加载指定布局成相应对象
final LayoutInflater inflater = LayoutInflater.from(WebViewJsDialogActivity.this);
final View myview = inflater.inflate(R.layout.prompt_view, null);
//设置TextView对应网页中的提示信息,edit设置来自于网页的默认文字
((TextView) myview.findViewById(R.id.textview)).setText(message);
((EditText) myview.findViewById(R.id.edittext)).setText(defaultValue);
//定义对话框上的确定按钮
new AlertDialog.Builder(WebViewJsDialogActivity.this).setTitle("Prompt对话框").setView(myview)
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//单击确定后取得输入的值,传给网页处理
String value = ((EditText) myview.findViewById(R.id.edittext)).getText().toString();
result.confirm(value);
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.cancel();
}
}).show();
return true;
}
}
}
4.结果
点击Android原生TextView
点击JS的Alert对话框
点击JS的Confirm对话框对话框
点击JS的Prompt对话框对话框
补充:Android调用相机相册,并将内容传递给JS
JS代码
index1.html
<!DOCTYPE html>
<html lang="zh-cmn-Hans">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript">
function pickPhoto() {
try {
App.pickPhotoFromLibrary({"needCompress": false}, function (data) {
alert(data.message);
var elementById = document.getElementById('firstPhoto');
elementById.src = "data:image/jpg;base64," + data.image64;
elementById.width=240;
elementById.height = 320;
return false;
});
} catch (e) {
alert(e);
}
}
function takePhoto() {
try {
App.makePhotoFromCamera({}, function (data) {
var elementById = document.getElementById('secondPhoto');
elementById.src = "data:image/png;base64," + data.image64;
elementById.width=240;
elementById.height = 320;
})
} catch (e) {
alert(e);
}
}
</script>
<script type="text/javascript" src="http://www.baidu.com/static/native-app.js"></script>
<style type="text/css">
photo {
width: 240px;
height: 400px;
}
</style>
</head>
<body>
<img id="firstPhoto" class="photo"/>
<br/>
<button onclick="pickPhoto()">系统相册</button>
<br/>
<img id="secondPhoto" class="photo"/>
<br/>
<button onclick="takePhoto()">去拍照</button>
</body>
</html>
local.js
//callback容器
var SDKNativeEvents = {}
/*
* 存入传来的callback回调,加入唯一键名,使调用相同的功能可以并发
* funcName native的方法名
* data 参数
* callback 回调匿名方法
*/
function sdk_launchFunc(funcName,data,callback){
if(!data){
alert("必须传入data");
return;
}
if(!callback){
alert("必须传入回调function");
return;
}
var newName = funcName += getUniqueKey();
SDKNativeEvents[newName] = callback;
App.native_launchFunc(newName,JSON.stringify(data));
}
/**
* native回调 本地做完事情后将funcName和data传过来,调用之前h5预留的匿名函数
* funcName 对应的触发方法名
* data 参数
*/
function sdk_nativeCallback(funcName,data){
var obj= JSON.parse(data);
try{
if(SDKNativeEvents[funcName]){
SDKNativeEvents[funcName](obj);
SDKNativeEvents[funcName] = null;
}
}catch(e){
alert(e);
}
}
//工具--生成唯一键
var chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'];
function randomStr() {
var res = "";
for(var i = 0; i < 5; i++) {
var id = Math.ceil(Math.random() * 35);
res += chars[id];
}
return res;
}
function getUniqueKey(){
return randomStr()+""+(new Date()).getTime();
}
window.App.pickPhotoFromLibrary = function(data, callback) {
sdk_launchFunc("pickPhotoFromLibrary", data, callback);
}
window.App.makePhotoFromCamera = function(data, callback) {
sdk_launchFunc("makePhotoFromCamera", data, callback);
}
window.App.encrypt = function(data, callback) {
sdk_launchFunc("encrypt", data, callback);
}
window.App.playSnake = function(data, callback) {
sdk_launchFunc("playSnake", data, callback);
}
Android代码
package com.wjn.customwebviewjs;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.google.gson.JsonObject;
import com.tencent.smtt.sdk.CacheManager;
import java.io.File;
import java.io.IOException;
public class WebViewAndroidJSActivity extends AppCompatActivity {
private WebView webView;
private ToastUtils toast;
private String pickPhotoFuncNameName;//手机相册 JS传递
private String takePhotofuncName;//手机拍照 JS传递
private String mPath;
private Bitmap bitmap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_webviewandroidjs);
initView();
}
/**
* 初始化各种View
*/
private void initView() {
toast=new ToastUtils(this);
webView = findViewById(R.id.activity_webviewandroidjs_webview);
//设置WebSettings
WebViewSettingsUtils.setWebSettings(webView);
//添加WebViewClient事件
webView.setWebViewClient(new myWebViewClient());
// 添加js交互接口类,并起别名 AndroidWebView
webView.addJavascriptInterface(new JavascriptInterface(this), "App");
//webview加载网页
webView.loadUrl("file:///android_asset/index1.html");
}
/**
* WebViewClient监听
*/
private class myWebViewClient extends WebViewClient {
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
WebResourceResponse webResourceResponse = super.shouldInterceptRequest(view, url);
if (url == null) {
return webResourceResponse;
}
if (url.endsWith("native-app.js")) {
try {
webResourceResponse = new WebResourceResponse("text/javascript", "UTF-8", WebViewAndroidJSActivity.this.getAssets().open("local.js"));
} catch (IOException e) {
e.printStackTrace();
}
}
return webResourceResponse;
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
WebResourceResponse webResourceResponse = super.shouldInterceptRequest(view, request);
if (request == null) {
return webResourceResponse;
}
String url = request.getUrl().toString();
if (url != null && url.endsWith("native-app.js")) {
try {
webResourceResponse = new WebResourceResponse("text/javascript", "UTF-8", WebViewAndroidJSActivity.this.getAssets().open("local.js"));
} catch (IOException e) {
e.printStackTrace();
}
}
return webResourceResponse;
}
}
/**
* Native JS 接口
*/
@SuppressLint("JavascriptInterface")
public class JavascriptInterface {
/**
* 构造方法
*/
private Context context;
public JavascriptInterface(Context context) {
this.context = context;
}
/**
* JS 调用 Android
*/
@android.webkit.JavascriptInterface
public void native_launchFunc(final String funcName, final String jsonStr) {
if (funcName.startsWith("pickPhotoFromLibrary")) {
selectImageFromLocal(funcName);//手机相册
} else if (funcName.startsWith("makePhotoFromCamera")) {
takePhoto(funcName);//手机拍照
}
}
}
/**
* 手机相册
* */
public void selectImageFromLocal(String funcName){
pickPhotoFuncNameName = funcName;
if(FileHelper.isSdCardExist()){
Intent intent;
if(Build.VERSION.SDK_INT<19){
intent=new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
}else{
intent=new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
}
startActivityForResult(intent, StringConstant.REQUEST_CODE_SELECT_PICTURE);
}else{
toast.showToast(StringConstant.Filestatus1);
}
}
/**
*手机拍照
* */
public void takePhoto(String funcName){
takePhotofuncName = funcName;
if(FileHelper.isSdCardExist()){
Uri photoURI=null;
String name=String.valueOf(System.currentTimeMillis());
mPath=FileHelper.createAvatarPathPicture(name);
Intent takePictureIntent=new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if(takePictureIntent.resolveActivity(getPackageManager())!=null){
//创建一个File
File photoFile = new File(mPath);
if(photoFile != null){
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
//如果是7.0及以上的系统使用FileProvider的方式创建一个Uri
photoURI= FileProvider.getUriForFile(WebViewAndroidJSActivity.this, "com.wjn.customwebviewjs.fileprovider", photoFile);
takePictureIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
takePictureIntent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
} else {
//7.0以下使用这种方式创建一个Uri
photoURI = Uri.fromFile(photoFile);
}
//将Uri传递给系统相机
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, StringConstant.REQUEST_CODE_TAKE_PHOTO);
}
}
}else{
toast.showToast(StringConstant.Filestatus1);
}
}
/**
* onActivityResult方法
* */
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case StringConstant.REQUEST_CODE_SELECT_PICTURE://本地相册
Uri uri = data.getData();
if (uri != null) {
String filePath;
if (!TextUtils.isEmpty(uri.toString()) && uri.toString().startsWith("file")) {
filePath = uri.getPath();
} else {
filePath = RealPathUtil.getRealPathFromURI(this, uri);
}
bitmap=BitmapFactory.decodeFile(filePath);
String base64Image = Base64Util.bitmapToBase64(bitmap);
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("image64", base64Image);
jsonObject.addProperty("message", "图片获取成功");
webView.loadUrl("javascript:sdk_nativeCallback(\'" + pickPhotoFuncNameName + "\',\'" + jsonObject + "\')");
}
break;
case StringConstant.REQUEST_CODE_TAKE_PHOTO://手机拍照
bitmap=BitmapFactory.decodeFile(mPath);
String base64Image = Base64Util.bitmapToBase64(bitmap);
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("image64", base64Image);
webView.loadUrl("javascript:sdk_nativeCallback(\'" + takePhotofuncName + "\',\'" + jsonObject + "\')");
break;
}
}
/**
* onDestroy方法
* */
@Override
protected void onDestroy() {
super.onDestroy();
Base64Util.recycleBitmap(bitmap);//释放Bitmap
//删除剪切保存的图片
File file = new File(FileHelper.getTheRootDirectory() + StringConstant.PICTURTemporary_Path);
Base64Util.deleteFile(file);
//删除WebView缓存
destory();
}
/**
* 删除WebView缓存
* */
private void destory() {
File file = CacheManager.getCacheFileBaseDir();
if (file !=null && file.exists() && file.isDirectory()){
for (File item : file.listFiles()) {
item.delete();
}
file.delete();
}
deleteDatabase("webview.db");
deleteDatabase("webviewCache.db");
webView.destroy();
}
}
三种方式的对比 & 使用场景
四.总结