日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > Android >内容正文

Android

Android WebView开发问题汇总

發(fā)布時(shí)間:2025/4/16 Android 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android WebView开发问题汇总 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在native與網(wǎng)頁(yè)相結(jié)合開(kāi)發(fā)的過(guò)程中,難免會(huì)遇到關(guān)于WebView一些共通的問(wèn)題。就我目前開(kāi)發(fā)過(guò)程中遇到的問(wèn)題以及最后得到的優(yōu)化方案都將在這里列舉出來(lái)。有些是老生常談,有些則是個(gè)人摸索得出解決方法。

1.加快HTML網(wǎng)頁(yè)裝載完成的速度

默認(rèn)情況html代碼下載到WebView后,webkit開(kāi)始解析網(wǎng)頁(yè)各個(gè)節(jié)點(diǎn),發(fā)現(xiàn)有外部樣式文件或者外部腳本文件時(shí),會(huì)異步發(fā)起網(wǎng)絡(luò)請(qǐng)求下載文件,但如果在這之前也有解析到image節(jié)點(diǎn),那勢(shì)必也會(huì)發(fā)起網(wǎng)絡(luò)請(qǐng)求下載相應(yīng)的圖片。在網(wǎng)絡(luò)情況較差的情況下,過(guò)多的網(wǎng)絡(luò)請(qǐng)求就會(huì)造成帶寬緊張,影響到css或js文件加載完成的時(shí)間,造成頁(yè)面空白loading過(guò)久。解決的方法就是告訴WebView先不要自動(dòng)加載圖片,等頁(yè)面finish后再發(fā)起圖片加載。

故在WebView初始化時(shí)設(shè)置如下代碼:

[java]?view plaincopy
  • public?void?int?()?{??
  • ????if(Build.VERSION.SDK_INT?>=?19)?{??
  • ????????webView.getSettings().setLoadsImagesAutomatically(true);??
  • ????}?else?{??
  • ????????webView.getSettings().setLoadsImagesAutomatically(false);??
  • ????}??
  • }??
  • 同時(shí)在WebView的WebViewClient實(shí)例中的onPageFinished()方法添加如下代碼:

    [java]?view plaincopy
  • @Override??
  • public?void?onPageFinished(WebView?view,?String?url)?{??
  • ????if(!webView.getSettings().getLoadsImagesAutomatically())?{??
  • ????????webView.getSettings().setLoadsImagesAutomatically(true);??
  • ????}??
  • }??

  • 從上面的代碼,可以看出我們對(duì)系統(tǒng)API在19以上的版本作了兼容。因?yàn)?.4以上系統(tǒng)在onPageFinished時(shí)再恢復(fù)圖片加載時(shí),如果存在多張圖片引用的是相同的src時(shí),會(huì)只有一個(gè)image標(biāo)簽得到加載,因而對(duì)于這樣的系統(tǒng)我們就先直接加載。

    2.自定義出錯(cuò)界面

    當(dāng)WebView加載頁(yè)面出錯(cuò)時(shí)(一般為404 NOT FOUND),安卓WebView會(huì)默認(rèn)顯示一個(gè)賣(mài)萌的出錯(cuò)界面。但我們?cè)趺茨茏層脩舭l(fā)現(xiàn)原來(lái)我使用的是網(wǎng)頁(yè)應(yīng)用呢,我們期望的是用戶在網(wǎng)頁(yè)上得到是如原生般應(yīng)用的體驗(yàn),那就先要從干掉這個(gè)默認(rèn)出錯(cuò)頁(yè)面開(kāi)始。當(dāng)WebView加載出錯(cuò)時(shí),我們會(huì)在WebViewClient實(shí)例中的onReceivedError()方法接收到錯(cuò)誤,我們就在這里做些手腳:

    [java]?view plaincopy
  • @Override??
  • public?void?onReceivedError?(WebView?view,?int?errorCode,?String?description,?String?failingUrl)?{??
  • ????super.onReceivedError(view,?errorCode,?description,?failingUrl);??
  • ????loadDataWithBaseURL(null,?"",?"text/html",?"utf-8",?null);??
  • ????mErrorFrame.setVisibility(View.VISIBLE);??
  • }??
  • 從上面可以看出,我們先使用loadDataWithBaseURL清除掉默認(rèn)錯(cuò)誤頁(yè)內(nèi)容,再讓我們自定義的View得到顯示(mErrorFrame為蒙在WebView之上的一個(gè)LinearLayout布局,默認(rèn)為View.GONE)。

    3.是否存在滾動(dòng)條

    當(dāng)我們做類(lèi)似上拉加載下一頁(yè)這樣的功能的時(shí)候,頁(yè)面初始的時(shí)候需要知道當(dāng)前WebView是否存在縱向滾動(dòng)條,如果有則不加載下一頁(yè),如果沒(méi)有則加載下一頁(yè)直到其出現(xiàn)縱向滾動(dòng)條。首先繼承WebView類(lèi),在子類(lèi)添加下面的代碼:

    [java]?view plaincopy
  • public?boolean?existVerticalScrollbar?()?{??
  • ????return?computeVerticalScrollRange()?>?computeVerticalScrollExtent();??
  • }??
  • computeVerticalScrollRange得到的是可滑動(dòng)的最大高度,computeVerticalScrollExtent得到的是滾動(dòng)把手自身的高,當(dāng)不存在滾動(dòng)條時(shí),兩者的值是相等的。當(dāng)有滾動(dòng)條時(shí)前者一定是大于后者的。

    4.是否已滾動(dòng)到頁(yè)面底部

    同樣我們?cè)谧錾侠虞d下一頁(yè)這樣的功能時(shí),也需要知道當(dāng)前頁(yè)面滾動(dòng)條所處的狀態(tài),如果快到底部,則要發(fā)起網(wǎng)絡(luò)請(qǐng)求數(shù)據(jù)更新網(wǎng)頁(yè)。同樣繼承WebView類(lèi),在子類(lèi)覆蓋onScrollChanged方法,具體如下:

    [java]?view plaincopy
  • @Override??
  • protected?void?onScrollChanged(int?newX,?int?newY,?int?oldX,?int?oldY)?{??
  • ????super.onScrollChanged(newX,?newY,?oldX,?oldY);??
  • ????if?(newY?!=?oldY)?{??
  • ????????float?contentHeight?=?getContentHeight()?*?getScale();??
  • ????????//?當(dāng)前內(nèi)容高度下從未觸發(fā)過(guò),?瀏覽器存在滾動(dòng)條且滑動(dòng)到將抵底部位置??
  • ????????if?(mCurrContentHeight?!=?contentHeight?&&?newY?>?0?&&?contentHeight?<=?newY?+?getHeight()?+?mThreshold)?{??
  • ????????????//?TODO?Something...??
  • ????????????mCurrContentHeight?=?contentHeight;??
  • ????????}??
  • ????}??
  • }??
  • 上面mCurrContentHeight用于記錄上次觸發(fā)時(shí)的網(wǎng)頁(yè)高度,用來(lái)防止在網(wǎng)頁(yè)總高度未發(fā)生變化而目標(biāo)區(qū)域發(fā)生連續(xù)滾動(dòng)時(shí)會(huì)多次觸發(fā)TODO,mThreshold是一個(gè)閾值,當(dāng)頁(yè)面底部距離滾動(dòng)條底部的高度差<=這個(gè)值時(shí)會(huì)觸發(fā)TODO。

    5.遠(yuǎn)程網(wǎng)頁(yè)需訪問(wèn)本地資源

    當(dāng)我們?cè)赪ebView中加載出從web服務(wù)器上拿取的內(nèi)容時(shí),是無(wú)法訪問(wèn)本地資源的,如assets目錄下的圖片資源,因?yàn)檫@樣的行為屬于跨域行為(Cross-Domain),而WebView是禁止的。解決這個(gè)問(wèn)題的方案是把html內(nèi)容先下載到本地,然后使用loadDataWithBaseURL加載html。這樣就可以在html中使用?file:///android_asset/xxx.png?的鏈接來(lái)引用包里面assets下的資源了。示例如下:

    [java]?view plaincopy
  • private?void?loadWithAccessLocal(final?String?htmlUrl)?{??
  • ????new?Thread(new?Runnable()?{??
  • ????????public?void?run()?{??
  • ????????????try?{??
  • ????????????????final?String?htmlStr?=?NetService.fetchHtml(htmlUrl);??
  • ????????????????if?(htmlStr?!=?null)?{??
  • ????????????????????TaskExecutor.runTaskOnUiThread(new?Runnable()?{??
  • ????????????????????????@Override??
  • ????????????????????????public?void?run()?{??
  • ????????????????????????????loadDataWithBaseURL(htmlUrl,?htmlStr,?"text/html",?"UTF-8",?"");??
  • ????????????????????????}??
  • ????????????????????});??
  • ????????????????????return;??
  • ????????????????}??
  • ????????????}?catch?(Exception?e)?{??
  • ????????????????Log.e("Exception:"?+?e.getMessage());??
  • ????????????}??
  • ????????????TaskExecutor.runTaskOnUiThread(new?Runnable()?{??
  • ????????????????@Override??
  • ????????????????public?void?run()?{??
  • ????????????????????onPageLoadedError(-1,?"fetch?html?failed");??
  • ????????????????}??
  • ????????????});??
  • ????????}??
  • ????}).start();??
  • }??
  • 上面有幾點(diǎn)需要注意:

    • 從網(wǎng)絡(luò)上下載html的過(guò)程應(yīng)放在工作線程
    • html下載成功后渲染出html的步驟應(yīng)放在UI主線程,不然WebView會(huì)報(bào)錯(cuò)
    • html下載失敗則可以使用我們前面講述的方法來(lái)顯示自定義錯(cuò)誤界面

      完整的demo項(xiàng)目代碼我已放到:http://yunpan.cn/cgQPvJQxxkCBj?(提取碼:6712)。

    6.ViewPager里非首屏WebView點(diǎn)擊事件不響應(yīng)

    如果你的多個(gè)WebView是放在ViewPager里一個(gè)個(gè)加載出來(lái)的,那么就會(huì)遇到這樣的問(wèn)題。ViewPager首屏WebView的創(chuàng)建是在前臺(tái),點(diǎn)擊時(shí)沒(méi)有問(wèn)題;而其他非首屏的WebView是在后臺(tái)創(chuàng)建,滑動(dòng)到它后點(diǎn)擊頁(yè)面會(huì)出現(xiàn)如下錯(cuò)誤日志:


    20955-20968/xx.xxx.xxx E/webcoreglue﹕ Should not happen: no rect-based-test nodes found

    解決這個(gè)問(wèn)題的辦法是繼承WebView類(lèi),在子類(lèi)覆蓋onTouchEvent方法,填入如下代碼:


    [java]?view plaincopy
  • @Override??
  • public?boolean?onTouchEvent(MotionEvent?ev)?{??
  • ????if?(ev.getAction()?==?MotionEvent.ACTION_DOWN)?{??
  • ????????onScrollChanged(getScrollX(),?getScrollY(),?getScrollX(),?getScrollY());??
  • ????}??
  • ????return?super.onTouchEvent(ev);??
  • }??
  • 該方法的最先提出在WebView in ViewPager not receive user inputs。

    7.WebView硬件加速導(dǎo)致頁(yè)面渲染閃爍

    4.0以上的系統(tǒng)我們開(kāi)啟硬件加速后,WebView渲染頁(yè)面更加快速,拖動(dòng)也更加順滑。但有個(gè)副作用就是,當(dāng)WebView視圖被整體遮住一塊,然后突然恢復(fù)時(shí)(比如使用SlideMenu將WebView從側(cè)邊滑出來(lái)時(shí)),這個(gè)過(guò)渡期會(huì)出現(xiàn)白塊同時(shí)界面閃爍。解決這個(gè)問(wèn)題的方法是在過(guò)渡期前將WebView的硬件加速臨時(shí)關(guān)閉,過(guò)渡期后再開(kāi)啟,代碼如下:

    [java]?view plaincopy
  • if?(Build.VERSION.SDK_INT?>=?Build.VERSION_CODES.HONEYCOMB)?{??
  • ????webview.setLayerType(View.LAYER_TYPE_SOFTWARE,?null);??
  • }??
  • 8.避免addJavaScriptInterface帶來(lái)的安全問(wèn)題

    使用開(kāi)源項(xiàng)目Safe Java-JS WebView Bridge可以很好替代addJavaScriptInterface方法,同時(shí)增加了異步回調(diào)等支持,并且不存在了安全風(fēng)險(xiǎn)。

    9.WebView與上層父元素的TouchMove事件沖突

    在開(kāi)發(fā)過(guò)程中你可能會(huì)遇到這樣一種情況。端里面使用ViewPager嵌套了多個(gè)WebView頁(yè)面,同時(shí)某一個(gè)WebView中的頁(yè)面元素需要響應(yīng)TouchMove事件。詳細(xì)解決方案請(qǐng)移步:http://www.pedant.cn/2014/09/23/webview-touch-conflict


    10.清理Cache和歷史記錄

    在開(kāi)發(fā)過(guò)程中你可能會(huì)遇到這樣一種情況。端里面使用ViewPager嵌套了多個(gè)WebView頁(yè)面,同時(shí)某一個(gè)WebView中的頁(yè)面元素需要響應(yīng)TouchMove事件。詳細(xì)解決方案請(qǐng)移步:http://www.pedant.cn/2014/09/23/webview-touch-conflict

    [java]?view plaincopy
  • webView.clearCache(true);???
  • webView.clearHistory();??
  • 11.WebView Cookies清理


    [java]?view plaincopy
  • CookieSyncManager.createInstance(this);???
  • CookieSyncManager.getInstance().startSync();???
  • CookieManager.getInstance().removeSessionCookie();???

  • 12.處理WebView中的非超鏈接請(qǐng)求(如Ajax請(qǐng)求):?

    有時(shí)候需要加上請(qǐng)求頭,但是非超鏈接的請(qǐng)求,沒(méi)有辦法再shouldOverrinding中攔截并用webView.loadUrl(String url,HashMap headers)方法添加請(qǐng)求頭目前用了一個(gè)臨時(shí)的辦法解決:
    ? ? ? 首先需要在url中加特殊標(biāo)記/協(xié)議, 如在onWebViewResource方法中攔截對(duì)應(yīng)的請(qǐng)求,然后將要添加的請(qǐng)求頭,以get形式拼接到url末尾
    在shouldInterceptRequest()方法中,可以攔截到所有的網(wǎng)頁(yè)中資源請(qǐng)求,比如加載JS,圖片以及Ajax請(qǐng)求等等
    Ex:

    [java]?view plaincopy
  • @SuppressLint("NewApi")??
  • @Override??
  • public?WebResourceResponse?shouldInterceptRequest(WebView?view,String?url)?{??
  • ????//?非超鏈接(如Ajax)請(qǐng)求無(wú)法直接添加請(qǐng)求頭,現(xiàn)拼接到url末尾,這里拼接一個(gè)imei作為示例??
  • ??
  • ????String?ajaxUrl?=?url;??
  • ????//?如標(biāo)識(shí):req=ajax??
  • ????if?(url.contains("req=ajax"))?{??
  • ???????ajaxUrl?+=?"&imei="?+?imei;??
  • ????}??
  • ??
  • ????return?super.shouldInterceptRequest(view,?ajaxUrl);??
  • ??
  • }??
  • 13.WebView頁(yè)面中播放了音頻,退出Activity后音頻仍然在播放

    需要在Activity的onDestory()中調(diào)用


    [java]?view plaincopy
  • webView.destroy();??
  • 但是直接調(diào)用可能會(huì)引起如下錯(cuò)誤:

    [java]?view plaincopy
  • 10-10?15:01:11.402:?E/ViewRootImpl(7502):?sendUserActionEvent()?mView?==?null??
  • 10-10?15:01:26.818:?E/webview(7502):?java.lang.Throwable:?Error:?WebView.destroy()?called?while?still?attached!??
  • 10-10?15:01:26.818:?E/webview(7502):????at?android.webkit.WebViewClassic.destroy(WebViewClassic.java:4142)??
  • 10-10?15:01:26.818:?E/webview(7502):????at?android.webkit.WebView.destroy(WebView.java:707)??
  • 10-10?15:01:26.818:?E/webview(7502):????at?com.didi.taxi.ui.webview.OperatingWebViewActivity.onDestroy(OperatingWebViewActivity.java:236)??
  • 10-10?15:01:26.818:?E/webview(7502):????at?android.app.Activity.performDestroy(Activity.java:5543)??
  • 10-10?15:01:26.818:?E/webview(7502):????at?android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1134)??
  • 10-10?15:01:26.818:?E/webview(7502):????at?android.app.ActivityThread.performDestroyActivity(ActivityThread.java:3619)??
  • 10-10?15:01:26.818:?E/webview(7502):????at?android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:3654)??
  • 10-10?15:01:26.818:?E/webview(7502):????at?android.app.ActivityThread.access$1300(ActivityThread.java:159)??
  • 10-10?15:01:26.818:?E/webview(7502):????at?android.app.ActivityThread$H.handleMessage(ActivityThread.java:1369)??
  • 10-10?15:01:26.818:?E/webview(7502):????at?android.os.Handler.dispatchMessage(Handler.java:99)??
  • 10-10?15:01:26.818:?E/webview(7502):????at?android.os.Looper.loop(Looper.java:137)??
  • 10-10?15:01:26.818:?E/webview(7502):????at?android.app.ActivityThread.main(ActivityThread.java:5419)??
  • 10-10?15:01:26.818:?E/webview(7502):????at?java.lang.reflect.Method.invokeNative(Native?Method)??
  • 10-10?15:01:26.818:?E/webview(7502):????at?java.lang.reflect.Method.invoke(Method.java:525)??
  • 10-10?15:01:26.818:?E/webview(7502):????at?com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1187)??
  • 10-10?15:01:26.818:?E/webview(7502):????at?com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)??
  • 10-10?15:01:26.818:?E/webview(7502):????at?dalvik.system.NativeStart.main(Native?Method)??

  • 如上所示,webview調(diào)用destory時(shí),webview仍綁定在Activity上。這是由于自定義webview構(gòu)建時(shí)傳入了該Activity的context對(duì)象,因此需要先從父容器中移除webview,然后再銷(xiāo)毀webview:

    [java]?view plaincopy
  • rootLayout.removeView(webView);??
  • webView.destroy();??
  • 14.WebView長(zhǎng)按自定義菜單,實(shí)現(xiàn)復(fù)制分享相關(guān)功能

    這個(gè)功能首先可以從兩方面完成:

    ? (1) 在js中完成:

    ? ? 處理Android.selection.longTouch

    這里推薦一個(gè)開(kāi)源項(xiàng)目進(jìn)行參考,:

    https://github.com/btate/BTAndroidWebViewSelection

    ???(2) android層處理:

    ? ? ?首先使用OnTouchListener實(shí)現(xiàn)長(zhǎng)按實(shí)現(xiàn)監(jiān)聽(tīng),然后實(shí)現(xiàn)WebView的Context menu,最后調(diào)用webview中的emulateShiftHeld(),為了適配安卓不同版本,最好使用反射方式調(diào)用。

    總結(jié)

    以上是生活随笔為你收集整理的Android WebView开发问题汇总的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。