此篇讲解的是Android通过WebView与H5进行基础交互,主要分为无参方法的调用 或 传参方法的调用 ~
我那些关于WebView的回忆 ~ 包含入门使用、优化加载样式、监听加载状态、各场景后退键处理、俩端交互流程、header、user-agent传值、交互常见问题、较全API整合
提醒:被Android调用的H5前端方法(PS:setVersion 为JS方法名)
<script type="text/javascript">
function setVersion(version) {
document.getElementById("version").innerHTML = version
}
</script>
使用之前科普一波Android调用js方法有参方法与无参方法的不同
mWebView.loadUrl("javascript: H5Method()");
mWebView.loadUrl("javascript:H5Method('" + everyDay Up+ " ')");
正式实现
1.首先我们在onCreat生命周期内调用
//加载H5地址
mWebView.loadUrl("这里输入我们要调用的H5地址");
2.监听WebView的加载状态,一般和H5前端的方法交互都会在初始的url完全加载之后 !!!
// 注意我们要调用的H5前端方法: mWebView.loadUrl("javascript:setVersion('" + versionName + " ');");
/**
* 监听WebView的加载状态 分别为 : 加载的 前 中 后期
* */
mWebView.setWebViewClient(new WebViewClient() {
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
//获取当前版本号
String versionName = DeviceUtils.getVersionName(AboutUsActivity.this);
//在加载完成之后,我们通过android的方法,去调用js的方法,设置对应的版本号
mWebView.loadUrl("javascript:setVersion('" + versionName + " ');");
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//本应该加载的H5静态界面
mWebView.loadUrl(url);
return true;
}
});
package com.bakheet.garage.mine.activity;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.view.View;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ProgressBar;
import com.bakheet.garage.R;
import com.bakheet.garage.base.BaseActivity;
import com.bakheet.garage.http.HttpUrl;
import com.bakheet.garage.utils.DeviceUtils;
/**
* @author Created by YongLiu on 2017/11/14.
*/
public class AboutUsActivity extends BaseActivity {
private WebView mWebView;
@Override
protected int getLayoutId() {
return R.layout.activity_about;
}
@Override
protected void init(Bundle savedInstanceState) {
setToolBarTitle(getString(R.string.title_about_us));
mWebView = (WebView) findViewById(R.id.web_agreement);
final ProgressBar mBar = (ProgressBar) findViewById(R.id.progress_Bar);
//允许Js的语言执行
mWebView.getSettings().setJavaScriptEnabled(true);
//加载本地H5
// mWebView.loadUrl("file:///android_asset/about.html");
//加载H5地址
mWebView.loadUrl(HttpUrl.ABOUT_H5 + "?timestamp=" + System.currentTimeMillis());
/**
* 监听WebView的加载状态 分别为 : 加载的 前 中 后期
* */
mWebView.setWebViewClient(new WebViewClient() {
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
String versionName = DeviceUtils.getVersionName(AboutUsActivity.this);
//在加载完成之后,我们通过android的方法,去调用js的方法,设置对应的版本号
mWebView.loadUrl("javascript:setVersion('" + versionName + " ');");
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//本应该加载的H5静态界面
mWebView.loadUrl(url);
return true;
}
});
mWebView.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int newProgress) {
if (newProgress == 100) {
mBar.setVisibility(View.GONE);
} else {
mBar.setVisibility(View.VISIBLE);
mBar.setProgress(newProgress);
}
}
});
}
}
最低版本貌似要求19,但可尝试此方式下Android端代码部分的第二份代码(注解部分)
<script type="text/javascript">
function sum(a,b){
return a+b;
}
function do(){
document.getElementById("p").innerHTML="hello world";
}
</script>
<html>
<head>
<title>title>
<script type="text/javascript">
function sum(a,b){
return a+b;
}
function s(){
var result =window.android.back();
document.getElementById("p").innerHTML=result;
}
script>
head>
<body>
<button onclick="s()">调用本地方法button>
<a href="file:///android_asset/test2.html">点击a>
<p id="p">p>
body>
html>
mWebView.evaluateJavascript("sum(1,2)", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
Log.e(TAG, "onReceiveValue value=" + value);
}
});
或
//Android调用有返回值js方法
@TargetApi(Build.VERSION_CODES.KITKAT)
public void onClick(View v) {
mWebView.evaluateJavascript("sum(1,2)", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
Log.e(TAG, "onReceiveValue value=" + value);
}
});
}
触发android功能,如拍照 亦或 跳转Androi的一些界面
Effect(已禁止LoginData.onBack()方法,所以不会触发)
<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">
<title>Documenttitle>
head>
<body>
<button onclick="LoginData.onBack()">注册方法button>
<script type="text/javascript">
let button = document.querySelector('button')
let register = button.addEventListener('click', function () {
//LoginData.onBack();
LoginData.setData('18888888888','111111');
})
script>
body>
html>
注:如果是要让H5前端调用的方法,请在方法上加 @JavascriptInterface 注解!!!如下:
//想要让JS识别的话,必须在方法名上加 @JavascriptInterface 如:
//这里我是用于监听用户在WebView中的点击事件,捕获此事件,如捕获成功关掉当前界面
@JavascriptInterface
public void onBack(){
mContext.finish();
}
因为我交互的场景是在登录注册的界面,故我创建了LoginData类 ; 这里需要注意一下类名、方法、内部实现都是根据自己的使用场景去创建的,并不是人人相同
LoginData 交互类
package com.bakheet.garage.mine.model;
import android.app.Activity;
import android.content.Intent;
import android.webkit.JavascriptInterface;
import com.alibaba.fastjson.JSON;
import com.bakheet.garage.base.MainActivity;
import com.bakheet.garage.http.HttpManager;
import com.bakheet.garage.http.ObjectResult;
import com.bakheet.garage.utils.SpUtil;
import com.bakheet.garage.utils.ToolUtil;
import java.io.IOException;
import java.util.HashMap;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.Response;
/**
* author yongliu
* date 2018/4/19.
* desc:
*/
public class LoginData {
Activity mContext;
public LoginData(Activity mContext) {
this.mContext = mContext;
}
/**
* 暴露给JS的方法
*/
@JavascriptInterface
public void setData(String account, String password) {
//interLogin(account, password);
ToastUtils.shortShow("account ="+account+"password ="+password);
}
@JavascriptInterface
public void onBack(){
mContext.finish();
}
/**
* 登录请求 - 此条请忽略,这是我的项目需求
* */
private void interLogin(String account, String password) {
String mdPassword = ToolUtil.md5(password);
HashMap<String, String> map = new HashMap<>();
map.put("acc", account);
map.put("pwd", mdPassword);
String jsonParams = JSON.toJSONString(map);
RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), jsonParams);
Call<ObjectResult> login = HttpManager.getHttpService().getLogin(body);
HttpManager.enqueue(login, new HttpManager.OnResultListener<ObjectResult>() {
@Override
public void onSuccess(Call<ObjectResult> call, Response<ObjectResult> response) throws IOException {
String data = (String) response.body().getData();
SpUtil.putString("token", data);
Intent intent = new Intent(mContext, MainActivity.class);
mContext.startActivity(intent);
mContext.finish();
}
@Override
public void onError(Throwable t) {
}
});
}
}
注:核心配置
//基本配置:允许俩端交互 - 可识别JS代码
mWebView.getSettings().setJavaScriptEnabled(true);
//核心配置:传入的参数包含实例化的交互类、我们给前端自定的API ~ 这里的“LoginData”并不需要和类名一致,见名知意点就好
//格外注意:前端只能识别我们给传的API,只有类似"LoginData"这样的API才能让他调到我们交互类内的方法
mWebView.addJavascriptInterface(new LoginData(this),"LoginData");
//随机加载:因为给大家的是测试效果,所以我加载的本地的html!真是使用场景的话,我们加载的url一般前端的同事都会给我们
mWebView.loadUrl("file:///android_asset/register.html");
这是我的使用场景 RegisterActivity 代码,只是一些WebView的简单使用,然后需要注意的还是上方提交的核心配置 ~
package com.bakheet.garage.home.activity;
import android.os.Bundle;
import android.view.View;
import android.webkit.WebChromeClient;
import android.webkit.WebView;
import android.widget.ProgressBar;
import com.bakheet.garage.R;
import com.bakheet.garage.base.BaseActivity;
import com.bakheet.garage.mine.model.LoginData;
/**
* @author yongliu
* date 2018/4/16.
* desc:
*/
public class RegisterActivity extends BaseActivity {
private WebView mWebView;
@Override
protected int getLayoutId() {
return R.layout.activity_register;
}
@Override
protected void init(Bundle savedInstanceState) {
setToolBarTitle("注册");
mWebView = (WebView) findViewById(R.id.web_register);
final ProgressBar mBar = (ProgressBar) findViewById(R.id.progress_Bar);
mWebView.getSettings().setJavaScriptEnabled(true);
//加载H5地址
mWebView.loadUrl("file:///android_asset/register.html");
mWebView.addJavascriptInterface(new LoginData(this), "LoginData");
mWebView.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int newProgress) {
if (newProgress == 100) {
mBar.setVisibility(View.GONE);
} else {
mBar.setVisibility(View.VISIBLE);
mBar.setProgress(newProgress);
}
}
});
}
}
其实我们的loadUrl就是一个get网络请求,所以当我们需要在请求头中添加数据的时候,我们可以用到loadUrl的重载方法,如下图
常规使用
//关键API
webView.loadUrl(url,header);
请求头添加数据,是以Map的形式提交的,我们只需要写好对应的键值对,后台就可以通过Map的键名获取值了
Map<String,String> header = new HashMap<String, String>();
header.put("tick",postDate);
webView.loadUrl(url,header);
mWebView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//判断url拦截事件
if (url.equals("file:///android_asset/test2.html")) {
Log.e(TAG, "shouldOverrideUrlLoading: " + url);
startActivity(new Intent(MainActivity.this,Main2Activity.class));
return true;
} else {
mWebView.loadUrl(url);
return false;
}
}
});
此功能,本人未亲测 ~
WebSettings webSettings = webview.getSettings();
//允许js弹出窗口
setJavaScriptCanOpenWindowsAutomatically(true);
/**
* Webview加载html中有alert()执行的时候,会回调这个方法
* url:当前Webview显示的url
* message:alert的参数值
* JsResult:java将结果回传到js中
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder builder = new AlertDialog.Builder(JSActivity.this);
builder.setTitle("提示:看到这个,说明Java成功重写了Js的Alert方法");
builder.setMessage(message);//这个message就是alert传递过来的值
builder.setPositiveButton("确定", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//处理确定按钮,且通过jsresult传递,告诉js点击的是确定按钮
result.confirm();
}
});
builder.show();
//自己处理
result.cancel();
return true;
}
/**
* Webview加载html中有confirm执行的时候,会回调这个方法
* url:当前Webview显示的url
* message:alert的参数值
* JsResult:java将结果回传到js中
*/
@Override
public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
AlertDialog.Builder builder = new AlertDialog.Builder(JSActivity.this);
builder.setTitle("提示:看到这个,说明Java成功重写了Js的Confirm方法");
builder.setMessage(message);//这个message就是alert传递过来的值
builder.setPositiveButton("确定", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//处理确定按钮,且通过jsresult传递,告诉js点击的是确定按钮
result.confirm();
}
});
builder.setNegativeButton("取消", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//处理取消按钮,且通过jsresult传递,告诉js点击的是取消按钮
result.cancel();
}
});
builder.show();
//自己处理
result.cancel();
return true;
}
/**
* Webview加载html中有prompt()执行的时候,会回调这个方法
* url:当前Webview显示的url
* message:alert的参数值
*defaultValue就是prompt的第二个参数值,输入框的默认值
* JsPromptResult:java将结果重新回传到js中
*/
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue,
final JsPromptResult result) {
AlertDialog.Builder builder = new AlertDialog.Builder(JSActivity.this);
builder.setTitle("提示:看到这个,说明Java成功重写了Js的Prompt方法");
builder.setMessage(message);//这个message就是alert传递过来的值
//添加一个EditText
final EditText editText = new EditText(JSActivity.this);
editText.setText(defaultValue);//这个就是prompt 输入框的默认值
//添加到对话框
builder.setView(editText);
builder.setPositiveButton("确定", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//获取edittext的新输入的值
String newValue = editText.getText().toString().trim();
//处理确定按钮了,且过jsresult传递,告诉js点击的是确定按钮(参数就是输入框新输入的值,我们需要回传到js中)
result.confirm(newValue);
}
});
builder.setNegativeButton("取消", new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//处理取消按钮,且过jsresult传递,告诉js点击的是取消按钮
result.cancel();
}
});
builder.show();
//自己处理
result.cancel();
return true;
}
});
java.lang.RuntimeException: java.lang.Throwable: A WebView method was called on thread ‘JavaBridge’. 兼容性问题,有网友说开始在4.2的机器上没有这个问题,到4.4的机器上就出了这个问题
解决方式 :webview的loadUrl方法写在runOnUiThread中即可解决问题
runOnUiThread(new Runnable() {
@Override
public void run() {
//我的场景是在调用交互方法时发生的错误,如果在正常加载也遇到这样的异常,那么尝试把loadUrl内部字符串换成需要加载的Url
webView.loadUrl("javascript: H5Method()");
}
});
end:借鉴文章