Android WebView常見問題及解決方案匯總

參考

WebView在現在的項目中使用的頻率應該還是非常高的。
  • WebSettings
    WebSettings webSettings = mWebView.getSettings();
    webview.requestFocusFromTouch();//支持獲取手勢焦點,輸入用戶名、密碼或其他
    setJavaScriptEnabled(true);//支持js
    setPluginsEnabled(true);//支持插件 webSettings.setRenderPriority(RenderPriority.HIGH);//提高渲染的優先級設置自適應屏幕,兩者合用
    setUseWideViewPort(true);//將圖片調整到適合webview的大小
    setLoadWithOverviewMode(true);// 縮放至屏幕的大小setSupportZoom(true);//支持縮放,默認為true。是下面那個的前提。setBuiltInZoomControls(true);//設置內置的縮放控件。//若上面是false,則該WebView不可縮放,這個不管設置什么都不能縮放。setDisplayZoomControls(false);//隱藏原生的縮放控件setLayoutAlgorithm(LayoutAlgorithm.SINGLE_COLUMN);//支持內容重新布局 supportMultipleWindows();//多窗口 setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);//關閉webview中緩存 setAllowFileAccess(true);//設置可以訪問文件 setNeedInitialFocus(true);//當webview調用requestFocus時為webview設置節點setJavaScriptCanOpenWindowsAutomatically(true);//支持通過JS打開新窗口 setLoadsImagesAutomatically(true);//支持自動加載圖片setDefaultTextEncodingName("utf-8");//設置編碼格式
    關于緩存
    緩存模式
    LOAD_CACHE_ONLY: 不使用網絡,只讀取本地緩存數據
    LOAD_DEFAULT: (默認)根據cache-control決定是否從網絡上取數據。
    LOAD_NO_CACHE: 不使用緩存,只從網絡獲取數據.
    LOAD_CACHE_ELSE_NETWORK,只要本地有,無論是否過期,或者no-cache,都使用緩存中的數據。
    結合使用(離線加載):
if (NetStatusUtil.isConnected(getApplicationContext())) {
        webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
//根據cache-control決定是否從網絡上取數據。
}else {
       webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
//沒網,則從本地獲取,即離線加載
}
webSettings.setDomStorageEnabled(true);// 開啟 DOM storage API 功能webSettings.setDatabaseEnabled(true);//開啟 database storage API 功能
webSettings.setAppCacheEnabled(true);//開啟 Application Caches 功能
String cacheDirPath = getFilesDir().getAbsolutePath() + APP_CACAHE_DIRNAME;
webSettings.setAppCachePath(cacheDirPath);//設置  Application Caches 緩存目錄
**注意:**每個 Application 只調用一次 WebSettings.setAppCachePath(),WebSettings.setAppCacheMaxSize()
加載方式
**加載一個網頁:**
webView.loadUrl("[http://www.google.com/](http://www.google.com/)");
**加載apk包中的一個html頁面**
webView.loadUrl("file:///android_asset/test.html");
**加載手機本地的一個html頁面的方法:**
webView.loadUrl("content://com.android.htmlfileprovider/sdcard/test.html");
WebViewClient
**WebViewClient就是幫助WebView處理各種通知、請求事件的。**
打開網頁時不調用系統瀏覽器, 而是在本WebView中顯示:
mWebView.setWebViewClient(new WebViewClient(){
@Override public boolean shouldOverrideUrlLoading(WebView view, String url) {
          view.loadUrl(url);returntrue;
        }
});

\

WebViewClient方法

WebViewClient mWebViewClient =new WebViewClient(){
    shouldOverrideUrlLoading(WebView view,String url)  最常用的,比如上面的。
//在網頁上的所有加載都經過這個方法,這個函數我們可以做很多操作。
//比如獲取url,查看url.contains(“add”),進行添加操作    
shouldOverrideKeyEvent(WebView view, KeyEvent event)
//重寫此方法才能夠處理在瀏覽器中的按鍵事件。    
onPageStarted(WebView view,String url, Bitmap favicon)
//這個事件就是開始載入頁面調用的,我們可以設定一個loading的頁面,告訴用戶程序在等待網絡響應。    
onPageFinished(WebView view,String url)
//在頁面加載結束時調用。同樣道理,我們可以關閉loading 條,切換程序動作。    onLoadResource(WebView view,String url)// 在加載頁面資源時會調用,每一個資源(比如圖片)的加載都會調用一次。    
onReceivedError(WebView view, int errorCode,String description,String failingUrl)
// (報告錯誤信息)    
doUpdateVisitedHistory(WebView view,String url, boolean isReload)
//(更新歷史記錄)    
onFormResubmission(WebView view, Message dontResend, Message resend)
//(應用程序重新請求網頁數據)    
onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler,String host,String realm)
//(獲取返回信息授權請求)    
onReceivedSslError(WebView view, SslErrorHandler handler, SslError error)
//重寫此方法可以讓webview處理https請求。    
onScaleChanged(WebView view, float oldScale, float newScale)
// (WebView發生改變時調用)    
onUnhandledKeyEvent(WebView view, KeyEvent event)
//(Key事件未被加載時調用)
}
**將上面定義的WebViewClient設置給WebView:**
webView.setWebViewClient(mWebViewClient);

WebChromeClient

**WebChromeClient是輔助WebView處理Javascript的對話框,網站圖標,網站title,加載進度等 :**
方法中的代碼都是由Android端自己處理。
WebChromeClient mWebChromeClient =new WebChromeClient() {
//獲得網頁的加載進度,顯示在右上角的TextView控件中
@Override public void onProgressChanged(WebView view,int newProgress) 
{
if (newProgress <100) {
            String progress = newProgress +"%";        
}else {        
}    
}
//獲取Web頁中的title用來設置自己界面中的title
//當加載出錯的時候,比如無網絡,這時onReceiveTitle中獲取的標題為 找不到該網頁,
//因此建議當觸發onReceiveError時,不要使用獲取到的title
@Override public void onReceivedTitle(WebView view, String title) {
        MainActivity.this.setTitle(title);
}
@Override public void onReceivedIcon(WebView view, Bitmap icon) {
//    
}
@Override public boolean onCreateWindow(WebView view,boolean isDialog,boolean isUserGesture, Message resultMsg) {
//return true;    
}
@Override public void onCloseWindow(WebView window) {    
}
//處理alert彈出框,html 彈框的一種方式
@OverridepublicbooleanonJsAlert(WebView view, String url, String message, JsResult result) {
//return true;    
}
//處理confirm彈出框
@Override public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult            result) {//returntrue;    }//處理prompt彈出框@OverridepublicbooleanonJsConfirm(WebView view, String url, String message, JsResult result) {//returntrue;
}
};
**同樣,將上面定義的WebChromeClient設置給WebView:**
webView.setWebChromeClient(mWebChromeClient);
調用JS代碼
WebSettings webSettings = mWebView .getSettings();  
webSettings.setJavaScriptEnabled(true);  mWebView.addJavascriptInterface(newInsertObj(),"jsObj");
**上面這是前提!!!**
然后實現上面的類,這個類提供了四個方法,注釋的非常清楚。
class Insert Object ends Object {
//給html提供的方法,js中可以通過:var str = window.jsObj.HtmlcallJava(); 獲取到
@Javascript Interface public String HtmlcallJava()
 {
return"Html call Java";   
 }
//給html提供的有參函數 : window.jsObj.HtmlcallJava2("IT-homer blog"
);
@Javascript Interface public String HtmlcallJava2(final String param) {
return"Html call Java : " + param;    
}
//Html給我們提供的函數
@Javascript Interface public void JavacallHtml() {        
runOnUiThread(new Runnable() {
@Override public void run() {
//這里是調用方法                
mWebView.loadUrl("javascript: showFromHtml()");                
Toast.makeText(Html5Activity.this,"clickBtn", Toast.LENGTH_SHORT).show();
            }
        });
    }
//Html給我們提供的有參函數
@JavascriptInterfacepublicvoidJavacallHtml2(final String param) 
{
        runOnUiThread(new Runnable() {
                @Override public void run() {
                mWebView.loadUrl("javascript: showFromHtml2('IT-homer blog')"); 
               Toast.makeText(Html5Activity.this,"clickBtn2", Toast.LENGTH_SHORT).show();
                }
        });
    }
}

Android 調用js有個漏洞:
http://blog.csdn.net/leehong2005/article/details/11808557
WebView的方法
前進、后退
goBack()//后退goForward()//前進goBackOrForward(intsteps)//以當前的index為起始點前進或者后退到歷史記錄中指定的steps, 如果steps為負數則為后退,正數則為前進canGoForward()//是否可以前進canGoBack()//是否可以后退
清除緩存數據:
clearCache(true);//清除網頁訪問留下的緩存,由于內核緩存是全局的因此這個方法不僅僅針對webview而是針對整個應用程序.clearHistory()//清除當前webview訪問的歷史記錄,只會webview訪問歷史記錄里的所有記錄除了當前訪問記錄.clearFormData()//這個api僅僅清除自動完成填充的表單數據,并不會清除WebView存儲到本地的數據。
WebView的狀態:
onResume()//激活WebView為活躍狀態,能正常執行網頁的響應onPause()//當頁面被失去焦點被切換到后臺不可見狀態,需要執行onPause動過, onPause動作通知內核暫停所有的動作,比如DOM的解析、plugin的執行、JavaScript執行。pauseTimers()//當應用程序被切換到后臺我們使用了webview, 這個方法不僅僅針對當前的webview而是全局的全應用程序的webview,它會暫停所有webview的layout,parsing,javascripttimer。降低CPU功耗。resumeTimers()//恢復pauseTimers時的動作。destroy()//銷毀,關閉了Activity時,音樂或視頻,還在播放。就必須銷毀。
但是注意:
webview調用destory時,webview仍綁定在Activity上.這是由于自定義webview構建時傳入了該Activity的context對象,因此需要先從父容器中移除webview,然后再銷毀webview:
rootLayout.removeView(webView);webView.destroy();
判斷WebView是否已經滾動到頁面底端 或者 頂端:
getScrollY() //方法返回的是當前可見區域的頂端距整個頁面頂端的距離,也就是當前內容滾動的距離.
getHeight()或者getBottom() //方法都返回當前WebView這個容器的高度
getContentHeight()返回的是整個html的高度,但并不等同于當前整個頁面的高度,因為WebView有縮放功能,所以當前整個頁面的高度實際上應該是原始html的高度再乘上縮放比例.因此,更正后的結果,準確的判斷方法應該是:
if (webView.getContentHeight() * webView.getScale() == (webView.getHeight() + webView.getScrollY())) {//已經處于底端 }if(webView.getScrollY() ==0){//處于頂端
}
避免WebView內存泄露的一些方式
1.可以將 Webview 的 Activity 新起一個進程,結束的時候直接System.exit(0);退出當前進程;
啟動新進程,主要代碼: AndroidManifest.xml 配置文件代碼如下
在新進程中啟動 Activity ,里面傳了 一個 Url:
Intent intent =newIntent("com.lyl.boon.ui.activity.htmlactivity");Bundle bundle =newBundle(); bundle.putString("url", gankDataEntity.getUrl()); intent.putExtra("bundle",bundle);
startActivity(intent);
然后在 Html5Activity 的onDestory() 最后加上 System.exit(0); 殺死當前進程。
2.不能在xml中定義 Webview ,而是在需要的時候創建,并且Context使用 getApplicationgContext(),如下代碼:
LinearLayout.LayoutParamsparams =new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); mWebView =new WebView(getApplicationContext()); mWebView.setLayoutParams(params);
mLayout.addView(mWebView);
3.在 Activity 銷毀的時候,可以先讓 WebView 加載null內容,然后移除 WebView,再銷毀 WebView,最后置空。
代碼如下:
@OverrideprotectedvoidonDestroy() {if (mWebView !=null) { mWebView.loadDataWithBaseURL(null,"","text/html","utf-8",null); mWebView.clearHistory(); ((ViewGroup) mWebView.getParent()).removeView(mWebView); mWebView.destroy(); mWebView =null; }super.onDestroy();
}
返回鍵
返回上一次瀏覽的頁面
public booleanonKeyDown(int keyCode, KeyEventevent) {if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) { mWebView.goBack();returntrue; }return super.onKeyDown(keyCode,event);
}
有一個非常不錯的 Html5Activity 加載類帖出來:

import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;import android.view.KeyEvent;
import android.webkit.GeolocationPermissions;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.lyl.test.R;
public class Html5Activity extends AppCompatActivity { 
private String mUrl;
private LinearLayout mLayout;
private WebView mWebView;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);        
setContentView(R.layout.activity_web);      
 Bundle bundle = getIntent().getBundleExtra("bundle");        
mUrl = bundle.getString("url");        
Log.d("Url:", mUrl);        
mLayout = (LinearLayout) findViewById(R.id.web_layout);        
LinearLayout.LayoutParams params =new 
LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);        
mWebView =new WebView(getApplicationContext());        
mWebView.setLayoutParams(params);        
mLayout.addView(mWebView);       
WebSettings mWebSettings = mWebView.getSettings();        
mWebSettings.setSupportZoom(true);        
mWebSettings.setLoadWithOverviewMode(true);        
mWebSettings.setUseWideViewPort(true);        
mWebSettings.setDefaultTextEncodingName("utf-8");        
mWebSettings.setLoadsImagesAutomatically(true);
//調用JS方法.安卓版本大于17,加上注解 @JavascriptInterface        
mWebSettings.setJavaScriptEnabled(true);        
saveData(mWebSettings);        
newWin(mWebSettings);        
mWebView.setWebChromeClient(webChromeClient);        
mWebView.setWebViewClient(webViewClient);        
mWebView.loadUrl(mUrl);    
}
/**    * 多窗口的問題    */
private void newWin(WebSettings mWebSettings) {
    //html中的_bank標簽就是新建窗口打開,有時會打不開,需要加以下
    //然后 復寫 WebChromeClient的onCreateWindow方法        
    mWebSettings.setSupportMultipleWindows(true);        
    mWebSettings.setJavaScriptCanOpenWindowsAutomatically(true);
}
/**    * HTML5數據存儲    */
private void saveData(WebSettings mWebSettings) {
    //有時候網頁需要自己保存一些關鍵數據,Android WebView 需要自己設置        
    mWebSettings.setDomStorageEnabled(true);        
    mWebSettings.setDatabaseEnabled(true);        
    mWebSettings.setAppCacheEnabled(true);        
    String appCachePath =getApplicationContext().getCacheDir().getAbsolutePath();        
    mWebSettings.setAppCachePath(appCachePath);    
}    
WebViewClient webViewClient =new WebViewClient(){
/**
 * 多頁面在同一個WebView中打開,就是不新建activity或者調用系統瀏覽器打開       
*/
  @Override public boolean shouldOverrideUrlLoading(WebView view, String url) 
     {            
    view.loadUrl(url);
    return true;      
    }   
 };    
WebChromeClient webChromeClient =new WebChromeClient() {
//=========HTML5定位=========
//需要先加入權限//////
@Override public void onReceivedIcon(WebView view, Bitmap icon) {
      super.onReceivedIcon(view, icon);
}
@Override public void onGeolocationPermissionsHidePrompt() {
      super.onGeolocationPermissionsHidePrompt();        
}
@Override public void onGeolocationPermissionsShowPrompt(final String origin,final GeolocationPermissions.Callback callback) {          
 callback.invoke(origin,true,false);
//注意個函數,第二個參數就是是否同意定位權限,第三個是是否希望內核記住
    super.onGeolocationPermissionsShowPrompt(origin, callback);       
 }
//=========HTML5定位===================
//=========多窗口的問題===========================
@OverridepublicbooleanonCreateWindow(WebView view,boolean isDialog,boolean isUserGesture, Message resultMsg) {            
WebView.WebViewTransport transport = (WebView.WebViewTransport)
 resultMsg.obj;            
transport.setWebView(mWebView);            
resultMsg.sendToTarget();
return true;        
}
//=========多窗口的問題=========================
};
@OverridepublicbooleanonKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && mWebView.canGoBack()) 
{           
   mWebView.goBack();returntrue;     
   }
return super.onKeyDown(keyCode, event);
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,310評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,566評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,055評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,303評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,799評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,683評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,899評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,135評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,528評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,844評論 2 372

推薦閱讀更多精彩內容