阅读之前推荐阅读博客大佬的这2篇
Android开发:最全面、最易懂的Webview使用详解
最全面总结 Android WebView与 JS 的交互方式
本文作者: @youyuge
个人博客站点: https://youyuge.cn
参考自imooc实战课程,感谢猿猿老师,另外猿猿老师让我发一下课程链接~~(笑,非广告)http://coding.imooc.com/learn/list/116.html
后续篇目:
Android开罐头——WebView高可扩展性封装(二)
Android开罐头——WebView高可扩展性封装(三)
一、架构搭建
首先,我们确定第一步应该完成什么:
- 作为封装,一个抽象的父类是必须要的,比如初始化
webView
,webView
的生命周期管理,因为大家知道webView
容易内存泄漏。我们建立一个抽象类WebDelegate.java
继承自fragment
。 - 同时,子类实现一些具体的做法,比如
webSettings
的设置。 - 既然如此,父类就应当和子类通信,因为有些
webView
的设置必须在子类实现,而这些设置,应该在父类初始化webView
的时候就执行。很明显,我们需要一个接口类,进行回调通信。我们建立一个接口类IWebViewInitializer
。
子类通过继承获取父类数据,而父类通过接口回调获得数据。关系图:
二、IWebViewInitializer回调接口
我们想让子类必须实现这个接口,注意是必须,若非必须可参考onClick
点击事件的设计,而由于这里是必须实现,所以我们先在WebDelegate
基类中创建抽象方法:
public abstract IWebViewInitializer setInitializer();
因此,父类已经可以获取到我们的接口实例了。具体接口如下:
public interface IWebViewInitializer {
WebView initWebViewSettings(WebView webView);
//针对浏览器本身行为的控制,如前进后退的回调
WebViewClient initWebViewClient();
//针对页面的控制,如js交互
WebChromeClient initWebChromeClient();
}
子类需要实现3个具体的方法,具体实现以后再谈,我们先考虑父类。
三、父类WebDelegate
3.1 继承自fragment,首先定义一些变量,其中:
- 为了弱引用创建了一个引用队列
- 使用布尔变量
mIsWebViewAvailable
,这里是参考sdk里自带的WebViewFragment
类(api25及其以下才有),这个类是为了显示一个WebView
,而用一个布尔变量,能让webView
在fragment pause
或者resume
的时候自动也跟着pause
和resume
(原理很简单的)
private WebView mWebView = null;
private final ReferenceQueue<WebView> WEB_VIEW_QUEUE = new ReferenceQueue<>();
private String mUrl = null;
private boolean mIsWebViewAvailable = false;
3.2 初始化WebView
初始化工作,此方法在onCreate()
中执行:
- 注意初始化的时候使用了 弱引用防止内存泄漏
- 初始化后
mIsWebViewAvailable
变为true
表示之后webview
可用了 -
addJavascriptInterface
方法会让网页里的js能调用本地LatteWebInterface
实例里的方法,此类在章节4里写
@SuppressLint("JavascriptInterface")
private void initWebView() {
if(mWebView !=null){
mWebView.removeAllViews();
mWebView.destroy();
}else {
//获取子类回调传回来的接口实例
final IWebViewInitializer initializer = setInitializer();
if(initializer !=null){
final WeakReference<WebView> webViewWeakReference =
new WeakReference<WebView>(new WebView(getContext()),WEB_VIEW_QUEUE);
mWebView = webViewWeakReference.get();
mWebView = initializer.initWebViewSettings(mWebView);
mWebView.setWebViewClient(initializer.initWebViewClient());
mWebView.setWebChromeClient(initializer.initWebChromeClient());
//为webView添加js接口对象映射,让网页上的js能调用本地代码,注意此方法要加注解
mWebView.addJavascriptInterface(LatteWebInterface.create(this),"latte");
//webview可用了
mIsWebViewAvailable = true;
}else {
throw new NullPointerException("Initializer is null!");
}
}
}
- 主要的初始化完毕后,其次是在一些回调事件(比如
onPause
)中处理webView
的生命周期,防止内存泄漏,完整的基类暂时如下:
/**
* @function 网络服务的抽象基类
* Created by 尤晟 on 2017-07-29.
*/
//这里继承的BaseDelegate基类暂时理解为封装过的Fragment,第二篇中会有详细封装过程
public abstract class WebDelegate extends BaseDelegate{
private WebView mWebView = null;
private final ReferenceQueue<WebView> WEB_VIEW_QUEUE = new ReferenceQueue<>();
private String mUrl = null;
private boolean mIsWebViewAvailable = false;
public WebDelegate() {
}
public abstract IWebViewInitializer setInitializer();
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Bundle args = getArguments();
mUrl = args.getString(RouteKeys.URL.name());
initWebView();
}
//初始化webview
@SuppressLint("JavascriptInterface")
private void initWebView() {
if(mWebView !=null){
mWebView.removeAllViews();
mWebView.destroy();
}else {
final IWebViewInitializer initializer = setInitializer();
if(initializer !=null){
final WeakReference<WebView> webViewWeakReference =
new WeakReference<WebView>(new WebView(getContext()),WEB_VIEW_QUEUE);
mWebView = webViewWeakReference.get();
mWebView = initializer.initWebViewSettings(mWebView);
mWebView.setWebViewClient(initializer.initWebViewClient());
mWebView.setWebChromeClient(initializer.initWebChromeClient());
mWebView.addJavascriptInterface(LatteWebInterface.create(this),"latte");
//webview可用了
mIsWebViewAvailable = true;
}else {
throw new NullPointerException("Initializer is null!");
}
}
}
public WebView getWebView() {
if (mWebView == null) {
throw new NullPointerException("WebView IS NULL!");
}
return mIsWebViewAvailable ? mWebView : null;
}
public String getUrl() {
if (mUrl == null) {
throw new NullPointerException("WebView IS NULL!");
}
return mUrl;
}
@Override
public void onPause() {
super.onPause();
if(mWebView !=null){
mWebView.onPause();
}
}
@Override
public void onResume() {
super.onResume();
if (mWebView != null) {
mWebView.onResume();
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
mIsWebViewAvailable = false;
}
@Override
public void onDestroy() {
super.onDestroy();
if (mWebView != null) {
mWebView.removeAllViews();
mWebView.destroy();
mWebView = null;
}
}
}
四、创建js交互的本地对象类LatteWebInterface
此类在父基类WebDelegate
中的addJavascriptInterface
方法中就被调用,进行对象映射,为了让js代码执行此类中的方法。
/**
* @function 用来和原生进行交互,js代码中通过反射调用此类中的方法
* Created by 尤晟 on 2017-07-30.
*/
public class LatteWebInterface {
private final WebDelegate DELEGATE;
private LatteWebInterface(WebDelegate DELEGATE) {
this.DELEGATE = DELEGATE;
}
//简单工厂模式
static LatteWebInterface create(WebDelegate delegate){
return new LatteWebInterface(delegate);
}
//js中会调用此方法
public String event(String params) {
final String action = JSON.parseObject(params).getString("data");
return null;
}
}
五、总结
至此,基类,子类的回调接口,js本地类三个文件已暂时编写完成,已可以初步使用,但是还远远不够,后续篇目中将继续封装,可关注本人~~
17.7.31 更新:
17.8.3更新: