Android开罐头——WebView高可扩展性封装(一)

阅读之前推荐阅读博客大佬的这2篇
Android开发:最全面、最易懂的Webview使用详解
最全面总结 Android WebView与 JS 的交互方式

本文作者: @youyuge
个人博客站点: https://youyuge.cn
参考自imooc实战课程,感谢猿猿老师,另外猿猿老师让我发一下课程链接~~(笑,非广告)http://coding.imooc.com/learn/list/116.html

后续篇目:
Android开罐头——WebView高可扩展性封装(二)
Android开罐头——WebView高可扩展性封装(三)

一、架构搭建

首先,我们确定第一步应该完成什么:

  • 作为封装,一个抽象的父类是必须要的,比如初始化webViewwebView的生命周期管理,因为大家知道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,而用一个布尔变量,能让webViewfragment pause或者resume的时候自动也跟着pauseresume(原理很简单的)
    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 更新:

Android开罐头——WebView高可扩展性封装(二)

17.8.3更新:

Android开罐头——WebView高可扩展性封装(三)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容