Android開罐頭——WebView高可擴展性封裝(一)

閱讀之前推薦閱讀博客大佬的這2篇
Android開發(fā):最全面匕累、最易懂的Webview使用詳解
最全面總結(jié) Android WebView與 JS 的交互方式

本文作者: @youyuge
個人博客站點: https://youyuge.cn
參考自imooc實戰(zhàn)課程缀壤,感謝猿猿老師,另外猿猿老師讓我發(fā)一下課程鏈接~~(笑,非廣告)http://coding.imooc.com/learn/list/116.html

后續(xù)篇目:
Android開罐頭——WebView高可擴展性封裝(二)
Android開罐頭——WebView高可擴展性封裝(三)

一览妖、架構(gòu)搭建

首先,我們確定第一步應(yīng)該完成什么:

  • 作為封裝,一個抽象的父類是必須要的,比如初始化webView乡括,webView的生命周期管理,因為大家知道webView容易內(nèi)存泄漏智厌。我們建立一個抽象類WebDelegate.java繼承自fragment诲泌。
  • 同時,子類實現(xiàn)一些具體的做法铣鹏,比如webSettings的設(shè)置敷扫。
  • 既然如此,父類就應(yīng)當(dāng)和子類通信诚卸,因為有些webView的設(shè)置必須在子類實現(xiàn)葵第,而這些設(shè)置,應(yīng)該在父類初始化webView的時候就執(zhí)行惨险。很明顯羹幸,我們需要一個接口類,進行回調(diào)通信辫愉。我們建立一個接口類IWebViewInitializer栅受。

子類通過繼承獲取父類數(shù)據(jù),而父類通過接口回調(diào)獲得數(shù)據(jù)恭朗。關(guān)系圖:

初步架構(gòu)通信圖

二屏镊、IWebViewInitializer回調(diào)接口

我們想讓子類必須實現(xiàn)這個接口,注意是必須痰腮,若非必須可參考onClick點擊事件的設(shè)計而芥,而由于這里是必須實現(xiàn),所以我們先在WebDelegate基類中創(chuàng)建抽象方法:

 public abstract IWebViewInitializer setInitializer();

因此膀值,父類已經(jīng)可以獲取到我們的接口實例了棍丐。具體接口如下:

public interface IWebViewInitializer {

    WebView initWebViewSettings(WebView webView);

    //針對瀏覽器本身行為的控制误辑,如前進后退的回調(diào)
    WebViewClient initWebViewClient();

    //針對頁面的控制,如js交互
    WebChromeClient initWebChromeClient();
}

子類需要實現(xiàn)3個具體的方法,具體實現(xiàn)以后再談歌逢,我們先考慮父類巾钉。

三、父類WebDelegate

3.1 繼承自fragment秘案,首先定義一些變量砰苍,其中:

  • 為了弱引用創(chuàng)建了一個引用隊列
  • 使用布爾變量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()中執(zhí)行:

  • 注意初始化的時候使用了 弱引用防止內(nèi)存泄漏
  • 初始化后mIsWebViewAvailable變?yōu)?code>true表示之后webview可用了
  • addJavascriptInterface方法會讓網(wǎng)頁里的js能調(diào)用本地LatteWebInterface實例里的方法吼旧,此類在章節(jié)4里寫
 @SuppressLint("JavascriptInterface")
    private void initWebView() {
        if(mWebView !=null){
            mWebView.removeAllViews();
            mWebView.destroy();
        }else {
            //獲取子類回調(diào)傳回來的接口實例
            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接口對象映射,讓網(wǎng)頁上的js能調(diào)用本地代碼未舟,注意此方法要加注解
                mWebView.addJavascriptInterface(LatteWebInterface.create(this),"latte");
                //webview可用了
                mIsWebViewAvailable = true;
            }else {
                throw new NullPointerException("Initializer is null!");
            }
        }
    }
  • 主要的初始化完畢后黍少,其次是在一些回調(diào)事件(比如onPause)中處理webView的生命周期,防止內(nèi)存泄漏处面,完整的基類暫時如下:
/** 
 * @function 網(wǎng)絡(luò)服務(wù)的抽象基類
 * Created by 尤晟 on 2017-07-29.
 */

//這里繼承的BaseDelegate基類暫時理解為封裝過的Fragment,第二篇中會有詳細(xì)封裝過程
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;
        }
    }
}

四菩掏、創(chuàng)建js交互的本地對象類LatteWebInterface

此類在父基類WebDelegate中的addJavascriptInterface方法中就被調(diào)用魂角,進行對象映射,為了讓js代碼執(zhí)行此類中的方法智绸。

/**
 * @function 用來和原生進行交互野揪,js代碼中通過反射調(diào)用此類中的方法
 * 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中會調(diào)用此方法
    public String event(String params) {
        final String action = JSON.parseObject(params).getString("data");
        return null;
    }

}

五、總結(jié)

至此瞧栗,基類斯稳,子類的回調(diào)接口,js本地類三個文件已暫時編寫完成迹恐,已可以初步使用挣惰,但是還遠(yuǎn)遠(yuǎn)不夠,后續(xù)篇目中將繼續(xù)封裝殴边,可關(guān)注本人~~


17.7.31 更新:

Android開罐頭——WebView高可擴展性封裝(二)

17.8.3更新:

Android開罐頭——WebView高可擴展性封裝(三)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末憎茂,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子锤岸,更是在濱河造成了極大的恐慌竖幔,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件是偷,死亡現(xiàn)場離奇詭異拳氢,居然都是意外死亡募逞,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門馋评,熙熙樓的掌柜王于貴愁眉苦臉地迎上來放接,“玉大人,你說我怎么就攤上這事栗恩⊥盖” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵磕秤,是天一觀的道長乳乌。 經(jīng)常有香客問我,道長市咆,這世上最難降的妖魔是什么汉操? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮蒙兰,結(jié)果婚禮上磷瘤,老公的妹妹穿的比我還像新娘。我一直安慰自己搜变,他們只是感情好采缚,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著挠他,像睡著了一般扳抽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上殖侵,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天贸呢,我揣著相機與錄音,去河邊找鬼拢军。 笑死楞陷,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的茉唉。 我是一名探鬼主播固蛾,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼赌渣!你這毒婦竟也來了魏铅?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤坚芜,失蹤者是張志新(化名)和其女友劉穎览芳,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鸿竖,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡沧竟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年铸敏,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片悟泵。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡杈笔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出糕非,到底是詐尸還是另有隱情蒙具,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布朽肥,位于F島的核電站禁筏,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏衡招。R本人自食惡果不足惜篱昔,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望始腾。 院中可真熱鬧州刽,春花似錦、人聲如沸浪箭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽奶栖。三九已至房待,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間驼抹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工拜鹤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留框冀,地道東北人。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓敏簿,卻偏偏與公主長得像明也,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子惯裕,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容