WebView想說(shuō)愛(ài)你不容易啊

為什么要使用WebView

隨著app業(yè)務(wù)的不斷深入發(fā)展,只靠著原生代碼來(lái)堆砌功能是不現(xiàn)實(shí),畢竟開(kāi)發(fā)的時(shí)長(zhǎng)會(huì)增加,而且同時(shí)需要開(kāi)發(fā)iOS和Android兩套,并且,如果在UI上改變了一丁點(diǎn),都需要提包(雖然Android現(xiàn)在可以進(jìn)行熱更新,但是熱更新不是100%能生效的,其中的原理只要了解過(guò)的人都會(huì)知道的),最終我們會(huì)選擇使用原生嵌套H5的方式進(jìn)行開(kāi)發(fā),這樣,既可以隨時(shí)更改UI,也可以無(wú)限制的進(jìn)行功能擴(kuò)展,然后,我們就要使用到Android的WebView了,這個(gè)讓我們痛并快樂(lè)著的控件。

現(xiàn)在的手機(jī)高度定制,多多少少都會(huì)對(duì)系統(tǒng)原生的代碼進(jìn)行了修改和添加的,不同的手機(jī)的WebView呈現(xiàn)出來(lái)的效果也是不同的,可以說(shuō)是五彩繽紛了,所以我懂Android開(kāi)發(fā)者的痛苦。接下來(lái)我就具體去講解我在項(xiàng)目中使用WebView的經(jīng)驗(yàn),不喜勿噴哈。

使用

一開(kāi)始相信大家都是直接在布局文件中添加WebView控件,當(dāng)然我一開(kāi)始也是這樣做的,就是為了簡(jiǎn)單,而且也不知道這樣會(huì)出現(xiàn)什么問(wèn)題。

 <WebView    
     android:id="@+id/web_view"    
     android:layout_width="match_parent"    
     android:layout_height="match_parent"/>

直接就這樣添加了一個(gè)WebView,發(fā)現(xiàn)也沒(méi)什么問(wèn)題啊,一樣可以顯示,什么都是正常的啊。在重復(fù)打開(kāi)有WebView的頁(yè)面時(shí),你會(huì)發(fā)現(xiàn),應(yīng)用的內(nèi)存會(huì)不斷升高,銷(xiāo)毀了之后也不會(huì)降下來(lái),點(diǎn)擊GC也降不下來(lái),這樣就出現(xiàn)了內(nèi)存泄漏了,這時(shí)你就會(huì)發(fā)現(xiàn),這樣使用WebView是不正確的,那么最好方式是如何使用呢?
那就是在代碼中動(dòng)態(tài)添加。
首先在布局文件中聲明一個(gè)parent布局

<LinearLayout    
    android:id="@+id/web_view"   
    android:layout_width="match_parent"    
    android:layout_height="wrap_content"
    android:orientation="horizontal"   
    android:scrollbars="none" />

然后在代碼中,把WebView當(dāng)做其子View添加進(jìn)去

WebView webView = new WebView(context);
webViewLayout.addView(webView);

網(wǎng)上很多人說(shuō)這個(gè)context應(yīng)該用application的,我覺(jué)得是不對(duì)的,如果你的WebView需要彈出一個(gè)dialog呢?還有其他的不可預(yù)估的問(wèn)題的,最好還是用當(dāng)前的activity的Context是最合適的。
上面說(shuō)的是如何把WebView添加進(jìn)來(lái)進(jìn)行使用,然后到底它有哪些屬性是我們?cè)陂_(kāi)發(fā)中需要使用到的呢?

webView.loadUrl("www.baidu.com");//WebView加載的網(wǎng)頁(yè)使用loadUrl
WebSettings webSettings = webView.getSettings();//獲得WebView的設(shè)置
webSettings.setUseWideViewPort(true);// 設(shè)置此屬性,可任意比例縮放
webSettings.setLoadWithOverviewMode(true);//適配
webSettings.setJavaScriptEnabled(true);  //支持js
webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);  //設(shè)置 緩存模式
webSettings.setDomStorageEnabled(true);// 開(kāi)啟 DOM storage API 功能
webSettings.setDatabaseEnabled(true);//開(kāi)啟 database storage API 功能
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);//HTTPS,注意這個(gè)是在LOLLIPOP以上才調(diào)用的
webSettings.setAppCacheEnabled(true);//開(kāi)啟 Application Caches 功能
webSettings.setBlockNetworkImage(true);//關(guān)閉加載網(wǎng)絡(luò)圖片,在一開(kāi)始加載的時(shí)候可以設(shè)置為true,當(dāng)加載完網(wǎng)頁(yè)的時(shí)候再設(shè)置為false

上面是使用WebView中最基礎(chǔ)的設(shè)置,相信在開(kāi)發(fā)過(guò)程中都會(huì)進(jìn)行如上的設(shè)置的。

webView.setWebChromeClient(new WebChromeClient() {    
     @Override   
     public void onProgressChanged(WebView view, int newProgress) {
         //加載的進(jìn)度
     }
     @Override
     public void onReceivedTitle(WebView view, String title) {   
         //獲取WebView的標(biāo)題
     }
    @Override
    public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {    
        return super.onJsAlert(view, url, message, result);
        //Js 彈框
    }
    @Override
    public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {   
        AlertDialog.Builder b = new AlertDialog.Builder(IllegalQueryActivity.this);    
        b.setTitle("刪除");    
        b.setMessage(message);    
        b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {        
            @Override        
            public void onClick(DialogInterface dialog, int which) {            
                result.confirm();        
            }    
        });    
        b.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {        
            @Override        
            public void onClick(DialogInterface dialog, int which) {            
                result.cancel();        
            }    
        });    
        b.create().show();    
        return true;
    }
});
webView.setWebViewClient(new WebViewClient() {    
    @Override    
    public boolean shouldOverrideUrlLoading(WebView view, String url) {        
       //需要設(shè)置在當(dāng)前WebView中顯示網(wǎng)頁(yè),才不會(huì)跳到默認(rèn)的瀏覽器進(jìn)行顯示
       return true;   
    }    
    @Override    
    public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
        super.onReceivedError(view, request, error);
        //加載出錯(cuò)了
    }   
    @Override    
    public void onPageFinished(WebView view, String url) {        
        super.onPageFinished(view, url);
        //加載完成
    }
});
webView.setDownloadListener(new DownLoadListener());//下載監(jiān)聽(tīng)
private class DownLoadListener implements DownloadListener {   
    @Override   
    public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {      

    }
}

然后就是WebView跟JS的交互了

webView.addJavascriptInterface(new WebAppInterface(this), "WebJs");
public class WebAppInterface { 
    Context mContext;    
    public WebAppInterface(Context c) {        
        mContext = c;    
    }    
    @JavascriptInterface    
    public void method() {
    }
}
webView.loadUrl("javascript:jsMethod()");//這是WebView最簡(jiǎn)單的調(diào)用JS的方法

當(dāng)activity執(zhí)行生命周期的時(shí)候,這里需要注意的是在onDestroy的時(shí)候,需要銷(xiāo)毀WebView,不然也會(huì)出現(xiàn)內(nèi)存泄漏的。

@Overrideprotected void onPause() {    
    super.onPause();    
    if (webView != null) {        
        webView.onPause();    
    }
}
@Override
protected void onResume() {    
    super.onResume();    
    if (webView != null) {        
        webView.onResume();    
    }
}
@Override
protected void onDestroy() {        
    if (webView != null) {        
        webView.clearCache(true); //清空緩存   
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {            
            if (webViewLayout != null) {                
                webViewLayout.removeView(webView);            
            }            
        webView.removeAllViews();            
        webView.destroy();        
    }else {            
        webView.removeAllViews();            
        webView.destroy();            
        if (webViewLayout != null) {                
            webViewLayout.removeView(webView);           
        }        
     }  
     webView = null;    
  }   
}
可以看到上面的onDestroy方法中對(duì)系統(tǒng)的版本進(jìn)行了判斷,那是因?yàn)槲以诓煌陌姹局羞M(jìn)行了測(cè)試,如果低于5.0版本的WebView中,如果先在parent中remove了WebView,那WebView將無(wú)法進(jìn)行destroy了,這樣就會(huì)造成內(nèi)存的泄漏,下來(lái)你們可以自己去嘗試一下這個(gè)說(shuō)法是不是正確的。

現(xiàn)在還遇到的一個(gè)問(wèn)題就是,當(dāng)WebView嵌套在ScrollView中時(shí),某些機(jī)型會(huì)出現(xiàn)閃屏的問(wèn)題,單獨(dú)WebView的時(shí)候是不會(huì)出現(xiàn)的,把硬件加速關(guān)閉了之后,對(duì)用戶的體驗(yàn)又不好,所以暫時(shí)還未想到比較好的解決方案,所以還是建議不要在ScrollView中嵌套WebView這樣的控件。
暫時(shí)就總結(jié)了這么多,相信以后在項(xiàng)目中還會(huì)遇到更多的問(wèn)題的,遇到其他的問(wèn)題再更新上來(lái),以備忘吧。


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容