抽取一个简单的WebView基类
在安卓开发过程中经常需要接入H5页面,但每次接入都重新写一个WebView是很不方便的,所以为了减少工作量,特抽取一个WebView基类,将所有的公共操作均放入基类中执行。
1 抽取WebView基类
1.1 设置WebView属性
WebSettings用来管理WebView的状态配置,下面列出了常用的几种设置
WebSettings webSettings = mWebView.getSettings();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); // 允许加载混合网路协议内容
}
webSettings.setLoadWithOverviewMode(true); // 适应屏幕
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
webSettings.setJavaScriptEnabled(true); // 允许使用javascript
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE); // 设置缓存模式
webSettings.setDomStorageEnabled(true); // 开启DOM缓存
webSettings.setJavaScriptCanOpenWindowsAutomatically(true); // 允许加载新窗口
webSettings.setAllowFileAccess(true); // 允许访问文件数据
1.2 设置UserAgent
UserAgent即用户代理,简称UA,是http协议的一部分,声明了浏览器用于http请求的用户代理头的值,例如:Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1 。通过这个标识,前端可以统计访问H5的具体是哪种设备。
在部分情况下,多个APP共同使用一个前端页面,前端需要区分打开此页面的具体是哪个APP,这个时候就需要客户端同学对UA进行修改,在UA末尾添加和前端约定好的字符串来区分。
webSettings.setUserAgentString(webSettings.getUserAgentString() + getCustomUserAgentString()); // 设置user-agent
定义一个抽象方法,在继承WebView基类的activity中实现此方法,从而达到修改UA的目的。
/**
* 额外添加的userAgent
*/
abstract String getCustomUserAgentString();
1.3 设置native与js交互相关的类及别名
对接的H5页面不同,js的别名和方法也可能不同,所以交互部分的代码不在基类中展示,只提供抽象方法,具体实现由继承基类的activity自己实现。
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
if (null != getInterfaceObject() && null != getInterfaceName())
mWebView.addJavascriptInterface(getInterfaceObject(), getInterfaceName());
}
抽象方法如下:
/**
* 管理native与js交互代码的类
*/
abstract Object getInterfaceObject();
/**
* js调用native时的别名
*/
abstract String getInterfaceName();
1.4 加载url
mWebView.loadUrl(getUrl()); // 加载url
抽象方法如下:
/**
* 需加载的url
*/
abstract String getUrl();
1.5 重写WebChromeClient
WebChromeClient是WebView中用来处理Javascript的对话框、图标、标题等。下边只罗列了WebChromeClient中的部分方法,需要重写的时候可结合实际情况实现。
/**
* 重写WebChromeClient--处理js的对话框、图标、title、加载进度等
*/
private WebChromeClient getWebChromeClient() {
return new WebChromeClient() {
@Override // 接收文档标题
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
}
@Override // 显示Alert对话框
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
return super.onJsAlert(view, url, message, result);
}
@Override // 显示confirm对话框
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
return super.onJsConfirm(view, url, message, result);
}
@Override // 显示prompt对话框
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
return super.onJsPrompt(view, url, message, defaultValue, result);
}
@Override // 显示文件选择器
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
return super.onShowFileChooser(webView, filePathCallback, fileChooserParams);
}
};
}
1.6 重写WebViewClient
WebViewClient处理各种通知、请求事件
/**
* 重写WebViewClient--处理各种通知、请求事件
*/
private WebViewClient getWebViewClient() {
return new WebViewClient(){
@Override // url开始加载
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
}
@Override // 废弃于API23,加载资源时出错
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
}
@TargetApi(Build.VERSION_CODES.M)
@Override // 在API23添加,加载资源时出错
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
}
@Override // 加载资源时出现http错误
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
super.onReceivedHttpError(view, request, errorResponse);
}
@Override // url加载完成
public void onPageFinished(WebView view, String url) {
mOtherActionListener.otherAction(url);
super.onPageFinished(view, url);
}
@Override // 在API24被废弃,拦截页面加载,返回true表示拦截并处理点击事件,返回false则为超链接会在当前webview中加载
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return super.shouldOverrideUrlLoading(view, url);
}
@TargetApi(Build.VERSION_CODES.N)
@Override // 在API24添加,拦截页面加载,返回true表示拦截并处理点击事件,返回false则为超链接会在当前webview中加载
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return super.shouldOverrideUrlLoading(view, request);
}
};
}
1.7 其他额外操作
在继承此基类的子类中,还可能会有其他不同的额外操作需要执行,比如:url加载完成后弹出toast提示等,我们采用listener来监听
private OtherActionListener mOtherActionListener = null;
/**
* 其他额外操作用listener来实现
*/
public void setOtherActionListener(OtherActionListener otherActionListener) {
this.mOtherActionListener = otherActionListener;
}
interface OtherActionListener {
void otherAction(String str);
}
在需要加载完url后弹出toast提示的子类中实现otherAction方法
@Override
public void otherAction(String str) {
Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
}
2 详细源码
2.1 layout文件
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/base_web_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
2.2 BaseWebViewActivity基类
/**
* WebView基类
*/
public abstract class BaseWebViewActivity extends AppCompatActivity {
private WebView mWebView = null;
private OtherActionListener mOtherActionListener = null;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base_webview);
mWebView = (WebView) findViewById(R.id.base_web_view);
initWebSettings();
mWebView.loadUrl(getUrl()); // 加载url
mWebView.setWebChromeClient(getWebChromeClient());
mWebView.setWebViewClient(getWebViewClient());
}
/**
* 设置WebView属性
*/
@SuppressLint({"SetJavaScriptEnabled", "JavascriptInterface", "AddJavascriptInterface"})
private void initWebSettings() {
WebSettings webSettings = mWebView.getSettings();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); // 允许加载混合网路协议内容
}
webSettings.setLoadWithOverviewMode(true); // 适应屏幕
webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
webSettings.setJavaScriptEnabled(true); // 允许使用javascript
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE); // 设置缓存模式
webSettings.setDomStorageEnabled(true); // 开启DOM缓存
webSettings.setJavaScriptCanOpenWindowsAutomatically(true); // 允许加载新窗口
webSettings.setAllowFileAccess(true); // 允许访问文件数据
webSettings.setUserAgentString(webSettings.getUserAgentString() + getCustomUserAgentString()); // 设置user-agent
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
if (null != getInterfaceObject() && null != getInterfaceName())
mWebView.addJavascriptInterface(getInterfaceObject(), getInterfaceName());
}
}
/**
* 额外添加的userAgent
*/
abstract String getCustomUserAgentString();
/**
* 管理native与js交互代码的类
*/
abstract Object getInterfaceObject();
/**
* js调用native时的别名
*/
abstract String getInterfaceName();
/**
* 需加载的url
*/
abstract String getUrl();
/**
* 重写WebChromeClient--处理js的对话框、图标、title、加载进度等
*/
private WebChromeClient getWebChromeClient() {
return new WebChromeClient() {
@Override // 接收文档标题
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
}
@Override // 显示Alert对话框
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
return super.onJsAlert(view, url, message, result);
}
@Override // 显示confirm对话框
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
return super.onJsConfirm(view, url, message, result);
}
@Override // 显示prompt对话框
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
return super.onJsPrompt(view, url, message, defaultValue, result);
}
@Override // 显示文件选择器
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
return super.onShowFileChooser(webView, filePathCallback, fileChooserParams);
}
};
}
/**
* 重写WebViewClient--处理各种通知、请求事件
*/
private WebViewClient getWebViewClient() {
return new WebViewClient(){
@Override // url开始加载
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
}
@Override // 废弃于API23,加载资源时出错
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
}
@TargetApi(Build.VERSION_CODES.M)
@Override // 在API23添加,加载资源时出错
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
}
@Override // 加载资源时出现http错误
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
super.onReceivedHttpError(view, request, errorResponse);
}
@Override // url加载完成
public void onPageFinished(WebView view, String url) {
mOtherActionListener.otherAction(url);
super.onPageFinished(view, url);
}
@Override // 在API24被废弃,拦截页面加载,返回true表示拦截并处理点击事件,返回false则为超链接会在当前webview中加载
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return super.shouldOverrideUrlLoading(view, url);
}
@TargetApi(Build.VERSION_CODES.N)
@Override // 在API24添加,拦截页面加载,返回true表示拦截并处理点击事件,返回false则为超链接会在当前webview中加载
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return super.shouldOverrideUrlLoading(view, request);
}
};
}
/**
* 其他额外操作用listener来实现
*/
public void setOtherActionListener(OtherActionListener otherActionListener) {
this.mOtherActionListener = otherActionListener;
}
interface OtherActionListener {
void otherAction(String str);
}
}
2.3 继承此基类的activity
public class MainActivity extends BaseWebViewActivity implements BaseWebViewActivity.OtherActionListener {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setOtherActionListener(this);
}
@Override
String getCustomUserAgentString() {
return "";
}
@Override
Object getInterfaceObject() {
return new JsInterface();
}
@Override
String getInterfaceName() {
return "bridge";
}
@Override
String getUrl() {
return "file:///android_asset/test1.html";
}
@Override
public void otherAction(String str) {
Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
}
/**
* js调用java
*/
public class JsInterface {
@JavascriptInterface
public void helloNative(String str) { // 定义js需要调用的方法
Toast.makeText(MainActivity.this, str, Toast.LENGTH_LONG).show();
}
}
}
2.4 html文件
<html>
<script type="text/javascript">
function callNative() {
window.bridge.helloNative("js调用Java成功!");
}
</script>
<button onclick="javascript:callNative();">js调用java</button>
</html>