现在很多App为了降低开发成本和维护成本,都会内置Web网页,在安卓端是通过WebView来实现的,这就要求我们开发人员具备Android和JS交互的开发能力。
我将通过这篇文章来记录我的学习历程,在学习过程中我还是通过一个demo来加深理解,下面先贴上效果图:
效果图分别展示了js调用安卓方法的三种效果以及安卓调用js方法的两种效果,下面我把这五个方法拆开来单个学习理解。
一、先介绍一下安卓调用js方法的两种实现方式 :
1、通过webview对象调用方法loadUrl(),这样可以实现安卓调用js的方法;
2、通过webview对象调用方法evaluateJavascript(),实现安卓调用js方法。
下面从代码来具体看这两种方法的实现过程:
先贴上html文件:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>安卓与js交互</title>
<script>
function callJS1(){
alert("安卓调用JS中的callJs方法");
}
</script>
</head>
<body>
<p>这是一个webview </p>
</body>
</html>
这里为了代码看起来清晰明了,只放了需要的部分。代码中只放了一个功能供安卓调用。下面是安卓端的代码:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private WebView mWebView;
private Button mBtn1,mBtn2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
configWebview();
}
@SuppressLint("SetJavaScriptEnabled")
private void configWebview() {
WebSettings settings = mWebView.getSettings();
//允许与js交互
settings.setJavaScriptEnabled(true);
//允许js弹窗
settings.setJavaScriptCanOpenWindowsAutomatically(true);
//加载html文件
mWebView.loadUrl("file:///android_asset/javascript.html");
mWebView.setWebChromeClient(new WebChromeClient(){
//这里的参数message就是html文件中callJS1()方法下alert中的文本
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
//创建一个弹窗
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle("安卓与JS交互");
builder.setMessage(message);
builder.setPositiveButton("确认", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
});
builder.setCancelable(false);
builder.create().show();
return true;
}
}
private void initView() {
mWebView = findViewById(R.id.webview);
mBtn1 = findViewById(R.id.btn1);
mBtn2 = findViewById(R.id.btn2);
mBtn1.setOnClickListener(this);
mBtn2.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch(v.getId()){
case R.id.btn1:
//通过loadUrl()方法调用JS的方法
getMethodByLoadUrl();
break;
}
}
private void getMethodByLoadUrl() {
mWebView.post(new Runnable() {
@Override
public void run() {
//这种方法调用js的方法没有返回值
mWebView.loadUrl("javascript:callJS1()");
}
});
}
}
安卓和js交互的中间桥梁就是安卓中的控件WebView,其对象一边绑定html文件,一边和安卓系统交互,这样就连接起了安卓和js。
通过WebView的对象调用方法getSettings()获取WebSettings对象,从对象名称就能看出这个类是来设置WebView一些属性的:
WebSettings settings = mWebView.getSettings();
拿到WebSettings对象后,通过以下代码设置安卓系统和js交互的权限:
settings.setJavaScriptEnabled(true);
webview中经常有一些弹窗,我们可以通过以下代码来设置js弹窗的权限:
settings.setJavaScriptCanOpenWindowsAutomatically(true);
WebView是一个View,我们就可以加载之前写好的html文件,WebView可以自动完成对页面的渲染:
mWebView.loadUrl("file:///android_asset/javascript.html");
我这里是加载的工程下assets文件下的一个本地html文件,在实际开发中将url更换成远程的就可以。
在这个方法中因为没有去获取返回值,所以设置了一个弹窗来检验安卓调用js方法的结果,所以在设置WebView属性的时候设置了同意js弹窗的权限,即settings.setJavaScriptCanOpenWindowsAutomatically(true);
,这里WebView只是一个View,一扇窗户(载体),里面的窗花(弹窗)需要我们自己去定义,WebView给我们提供了方法去处理js中弹窗,就是setWebChromeClient()
方法,创建弹窗的方法就是原生安卓的方法,下面贴上代码:
mWebView.setWebChromeClient(new WebChromeClient(){
//这里的参数message就是html文件中callJS1()方法下alert中的文本
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
//创建一个弹窗
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle("安卓与JS交互");
builder.setMessage(message);
builder.setPositiveButton("确认", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
});
builder.setCancelable(false);
builder.create().show();
return true;
}
}
好了,以上就是安卓调用js方法的第一种方式,用WebView提供的方法loadUrl()实现。
由于第一种方法存在一个缺点就是,安卓无法高效的获取到返回值,所以在Android4.4以后安卓系统提供了第二种方法,就是用WebView提供的方法evaluateJavaScript()来实现安卓调用js的方法,这种方法能直接获取到返回值,相较于第一种比较高效,下面来看看第二种方法的实现:
html部分代码:
function callJS2(){
return "js返回的数据";
}
这部分js功能中,我只返回了一个值,目的就是校验第二种方法获取返回值。
安卓端部分代码:
mWebView.evaluateJavascript("javascript:callJS2()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//js返回值处理
tipString(value);
}
});
从代码中看出,第二种方法传入了两个参数,第一个参数对应js中的方法,第二个参数是一个回调对象,并且重写了一个方法,方法中包含一个String类型参数,这个参数就是js方法中返回的值。
上面学习了安卓调用js方法的两种方式,下面做个总结:
第一种方法比较方便简洁,但是获取返回值比较繁琐,需要js再去调用安卓的方法将返回值传过来,这样效率很低;
第二种方法能够获取到js的返回值,但是只支持Android4.4以上的设备,向下兼容性比较差。
综于以上总结,在实际开发中 ,为了兼容各个版本,我们可以在Android4.4以前的系统使用方法一,在4.4以上的用方法二。
二、学习完了安卓调用js的方法,下面来学习js调用安卓的方法,先简单介绍实现js调用安卓方法的三种方式:
`1、通过WebView对象调用方法addJavascriptInterface()将安卓对象映射成一个字符串,这样就可以在js中通过这个字符串调用安卓中的方法;
2、安卓端通过WebViewClient的shouldOverrideUrlLoading ()方法回调拦截url,然后解析url的协议,判断是否和预先约定好的一致,做出相应的操作;
3、安卓端通过WebChromeClient的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt()消息.
下面来逐个学习,先贴上html文件 :
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>安卓与js交互</title>
<script>
function callAndroid1(){
android.test("js调用了安卓的test方法");
}
function callAndroid2(){
document.location="js://webview?name=张三&age=24";
}
function callAndroid3(){
var result=prompt("js://android?name=李四&age=24");
alert(result);
}
</script>
</head>
<body>
<p>这是一个webview </p>
<button type="button" id="btn1" onclick="callAndroid1()">点击调用安卓的方法1</button>
<button type="button" id="btn2" onclick="callAndroid2()">点击调用安卓的方法2</button>
<button type="button" id="btn3" onclick="callAndroid3()">点击调用安卓的方法3</button>
</body>
</html>
代码中定义了三个js方法,分别是callAndroid1()、callAndroid2()和callAndroid3(),还有三个按钮,点击的时候分别去调用三个方法,下面看看安卓端的代码是怎么实现js和安卓的交互的:
首先,第一个方法是映射一个安卓对象,所以需要先创建一个类,里面包含供js调用的方法:
public class Android {
private Context context;
Android(Context context) {
this.context = context;
}
@JavascriptInterface
public void test(String msg){
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}
}
建好了类,定义好了供js调用的方法后,通过如下代码完成安卓对象的映射:
mWebView.addJavascriptInterface(new Android(this),"android");
以上代码将安卓对象映射成android
,所以在html文件中就有android.test("js调用了安卓的test方法");
,到了这就完成了js对安卓方法的调用。
不过这里需要注意一点:这种方法在Android4.2以前存在漏洞,就是映射之后这个类中所有的方法都会暴漏在js中,当有些方法我们是不希望js能够调用到的时候,这个时候就存在安全威胁,所以在4.2以后 ,安卓系统让能够被js调用的方法加上注释@JavascriptInterface
。
学习完了第一种方式,下面学习第二种方式,html文件和第一种差不多,上面已经给出,这里直接贴上安卓端的处理部分代码:
mWebView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Uri uri = Uri.parse(url);
if (uri.getScheme() != null && uri.getScheme().equals("js")) {
if (uri.getAuthority() != null && uri.getAuthority().equals("webview")) {
Set<String> names = uri.getQueryParameterNames();
// tip(names);
//调用Android中的方法
tipString(uri.getQueryParameter("name"));
}
//处理完了记得return 不然会出错
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
});
代码中WebViewClient通过shouldOverrideUrlLoading拦截url,并转换成uri,然后获取url的协议,即uri.getScheme()
和uri.getAuthority()
,然后判断是否和事先约定好的一致,当一致的时候再来做出相应的操作,在这里我是将拼接在url的参数取出来并显示在屏幕上。
上面就是第二种方式的实现过程,他不存在第一种方法那样的漏洞,但是也存在一个缺点 ,因为这种方法是通过拦截url,然后判断协议的方式来调用安卓方法的,在js将url传递过来后,安卓就失去了和js的通信,所以它也存在和安卓通过loadUrl()调用js方法一样的缺点,就是无法高效获取返回值,要想取得返回值,只能让安卓再去调用js的方法将返回值传递给js,这样效率明显不理想,所以在一些不需要返回值的交互场景中推荐这种方法 。
接下来学习第三种方式,这里只是学习了输入框,另外的警告框和确认框差不多的。
安卓端的处理部分代码:
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
Uri uri = Uri.parse(message);
if (uri.getScheme() != null && uri.getScheme().equals("js")) {
if (uri.getAuthority() != null && uri.getAuthority().equals("android")) {
//通过这个方法将安卓中的数据返回到js中去
result.confirm("JS调用安卓的方法");
}
//处理完了记得return 不然会出错
return true;
}
return super.onJsPrompt(view, url, message, defaultValue, result);
}
这种方法和第二种差不多,都是判断url的协议,然后根据判断的结果做出相应的操作,它和第二种方法的区别就是第三种方法js能够获取到返回值,因为js中警告框、确认框和输入框这三种弹框都有返回值,并且都能监听到选择或是输入的结果。这也使得第三种方法能满足大多数场合。
好了 ,以上就是对js调用安卓方法三种实现方式的学习,这里总结以下:
第一种方法方便简洁,但在Android4.2以前存在漏洞,4.2以后的场合可以 考虑这种方法;
第二种方法不存在漏洞了,但是url的协议判断过程相对来说复杂,而且获取返回值也比较复杂,不需要返回值的场合可以考虑用这种方法;
第三种方法不存在漏洞,判断url协议和第二种方法一样,但这种方法能获取到返回值,所以第三种方法能满足大多数场合。
以上就是本人对Android和JS交互的学习,希望发现文中有不当之处的朋友们能够指正。