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高可扩展性封装(三)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349

推荐阅读更多精彩内容