WebView相關(guān)問題總結(jié)

在進(jìn)行APP+H5混合開發(fā)的時(shí)候,一些功能是用native方法實(shí)現(xiàn)的,如登陸,一些功能是用H5實(shí)現(xiàn)的。所以往往需要將在native方法登陸的狀態(tài)同步到H5中避免再次登陸。這種情況在Android開發(fā)中比較常見靠抑,下面總結(jié)一下webview在項(xiàng)目中遇到的坑,順便給即將入坑的小伙伴一點(diǎn)小小的建議适掰,希望有所作用颂碧,相互學(xué)習(xí)進(jìn)步(大佬繞過……)

WebView常見需注意的API:


WebChromeClient是輔助WebView處理Javascript的對(duì)話框,網(wǎng)站圖標(biāo)攻谁,網(wǎng)站title稚伍,加載進(jìn)度等 :
onCloseWindow(關(guān)閉WebView)   

onCreateWindow()   

onJsAlert (WebView上alert是彈不出來東西的,需要定制你的WebChromeClient處理彈出)   

onJsPrompt   

onJsConfirm   

onProgressChanged   

onReceivedIcon   

onReceivedTitle 

WebView 調(diào)取本地文件上傳戚宦,部分代碼如下:

/**
 * 
 *  繼承WebChromeClient可以做些其他的工作
 */

class ChromeClient extends WebChromeClient {
 .
 .
 .
}


private ChromeClient mWebChromeClient = new ChromeClient()        {

    // android 5.0 這里需要使用android5.0 sdk
    public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback,
            WebChromeClient.FileChooserParams fileChooserParams) {
        Log.d("", "onShowFileChooser");
        if (mUploadMessageForAndroid5 != null) {
            mUploadMessageForAndroid5.onReceiveValue(null);
        }
        mUploadMessageForAndroid5 = filePathCallback;

        /**
         * 標(biāo)準(zhǔn)意圖个曙,被發(fā)送到相機(jī)應(yīng)用程序捕獲一個(gè)圖像,并返回它受楼。通過一個(gè)額外的extra_output控制這個(gè)圖像將被寫入垦搬。
         * 如果extra_output是不存在的,
         * 那么一個(gè)小尺寸的圖像作為位圖對(duì)象返回艳汽。如果extra_output是存在的猴贰,那么全尺寸的圖像將被寫入extra_output
         * URI值。
         */
        takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
            File photoFile = null;
            try {
                // 設(shè)置MediaStore.EXTRA_OUTPUT路徑,相機(jī)拍照寫入的全路徑
                photoFile = createImageFile();
                takePictureIntent.putExtra("PhotoPath", mCameraPhotoPath);
            } catch (Exception ex) {
                Log.e("WebViewSetting", "Unable to create Image File", ex);
            }
            if (photoFile != null) {
                // cameraUri = Uri.fromFile(photoFile);
                cameraUri = getUriForFile(context, photoFile);
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, cameraUri);
                System.out.println(mCameraPhotoPath);
            } else {
                takePictureIntent = null;
            }
        }
        showChioceDialog();
        return true;
    }

    // 針對(duì) 3.0--
    public void openFileChooser(ValueCallback<Uri> uploadMsg) {
        Log.d("asdf", "openFileChooser1");
        openFileChooserImpl(uploadMsg);

    }

    // 針對(duì) 3.0+
    public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
        Log.d("asdf", "openFileChooser2");
        openFileChooserImpl(uploadMsg);
    }

    // For Android 4.1
    public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
        Log.d("asdf", "openFileChooser3");
        openFileChooserImpl(uploadMsg);

    }
};

兼容Android 7.0訪問權(quán)限

/**
 * 適配7.0及以上
 * 
 * @param context
 * @param file
 * @return
 */
private static Uri getUriForFile(Context context, File file) {
    if (context == null || file == null) {
        throw new NullPointerException();
    }
    Uri uri;
    if (Build.VERSION.SDK_INT >= 24) {
        uri = FileProvider.getUriForFile(context.getApplicationContext(), "com.****.fileprovider", file);
    } else {
        uri = Uri.fromFile(file);
    }
    return uri;
}

文件選擇回調(diào)上傳處理:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    UMShareAPI.get(this).onActivityResult(requestCode, resultCode, data);
    if (requestCode == FILECHOOSER_RESULTCODE) {
        if (null == mUploadMessage && null == mUploadMessageForAndroid5)
            return;
        if (mUploadMessage != null) {
            Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
            mUploadMessage.onReceiveValue(result);
            mUploadMessage = null;
        } else if (mUploadMessageForAndroid5 != null) {
            Uri result = (data == null || resultCode != RESULT_OK) ? null : data.getData();
            if (result != null) {
                mUploadMessageForAndroid5.onReceiveValue(new Uri[] { result });
            } else {
                mUploadMessageForAndroid5.onReceiveValue(new Uri[] {});
            }
            mUploadMessageForAndroid5 = null;
        }
    }
    if (requestCode == INPUT_FILE_REQUEST_CODE) {
        if (null == mUploadMessage && null == mUploadMessageForAndroid5)
            return;
        afterOpenCamera();
        Uri uri = cameraUri;
        Uri[] uris = new Uri[1];
        uris[0] = uri;
        if (mUploadMessageForAndroid5 != null) {
            mUploadMessageForAndroid5.onReceiveValue(uris);
            mUploadMessageForAndroid5 = null;
        } else {
            mUploadMessage.onReceiveValue(uri);
            mUploadMessage = null;
        }
    }
}

WebView 圖片延遲加載

有些頁(yè)面如果包含網(wǎng)絡(luò)圖片河狐,在移動(dòng)設(shè)備上我們等待加載圖片的時(shí)間可能會(huì)很長(zhǎng)米绕,所以我們需要讓圖片延時(shí)加載,這樣不影響我們加載頁(yè)面的速度馋艺,同樣代碼說話:
定義變量:

boolean blockLoadingNetworkImage=false;

在WebView初始化的時(shí)候設(shè)置栅干,就是這么簡(jiǎn)單就可以了:

blockLoadingNetworkImage = true;

webView.setWebViewClient(new WebViewClient() {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            //返回值是true的時(shí)候控制去WebView打開,為false調(diào)用系統(tǒng)瀏覽器或第三方瀏覽器
            return true;
        }

        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            super.onPageStarted(view, url, favicon);
            if (!blockLoadingNetworkImage){
                webView.getSettings().setBlockNetworkImage(true);
            }
        }

        @Override
        public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            if (blockLoadingNetworkImage){
                webView.getSettings().setBlockNetworkImage(false);
            }
        }
    });

JS調(diào)用native

js調(diào)用原生大概有兩種方法

截取url捐祠,獲取指定的url碱鳞,例如頁(yè)面上有個(gè)撥打電話的調(diào)用,我們就可以在wenview中這樣截弱庵:

 webView.setWebViewClient(new WebViewClient() {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            //返回值是true的時(shí)候控制去WebView打開窿给,為false調(diào)用系統(tǒng)瀏覽器或第三方瀏覽器
            Log.e("zhaogl", url);
            if (url.startsWith("tel:")){//撥打電話
                String[] ss = url.split(":");
                if (ss.length > 1 && ss[1] != null){
                    CommonUtil.call(WebViewActivity.this,ss[1]);
                }
            }else if (url.equalsIgnoreCase("http://www.baidu.com/")){
                WebViewActivity.this.finish();
            }else {
                view.loadUrl(url);
            }
            return true;
        }
    });

調(diào)用Java寫好的方法:
首先我們要定義一個(gè)方法給js調(diào)用,這里我把這個(gè)方法封裝到一個(gè)類中:

public class InJavaScript {
private static InJavaScript instance;

public InJavaScript() {
}

public static InJavaScript getInstance() {
    if (instance == null){
        instance = new InJavaScript();
    }
    return instance;
}

/**
  * 分享
 * @param str  內(nèi)容
 * @param targetUrl url
 */
@JavascriptInterface
public void runOnAndroidShare(String str,String targetUrl) {
    WebViewEvent event = new WebViewEvent();
    event.isShare = true;
    event.content = str;
    event.url = targetUrl;
    EventBus.getDefault().post(event);
}

/**
 * 關(guān)閉 window.close 不起作用率拒,代替之
 */
@JavascriptInterface
public void closeWindowForAndroid(){
    WebViewEvent event = new WebViewEvent();
    event.isCloseWindow = true;
    EventBus.getDefault().post(event);
}

}

寫好的java類及方法如果想讓js調(diào)用崩泡,還需要有如下設(shè)置:

webView.addJavascriptInterface(InJavaScript.getInstance(), "injs");

js中就可以用以下方式調(diào)用:

function sendToAndroid(){  
window.injs.runOnAndroidShare(str,str);//調(diào)用android的函數(shù)  
}

注意猬膨,第二中方法在4.2以下版本存在js安全漏洞允华,但是目前市場(chǎng)上的安卓大部分都已經(jīng)在4.2以上了,所以如果你的項(xiàng)目安全要求不是那么高,可以正常使用⊙ゼ牛現(xiàn)在有很多第三方的框架解決這個(gè)問題,大家可以去自己查找召耘。

解決部分手機(jī)支持webview顯示空白

因?yàn)橛捎∠蠊P記備注總結(jié)過后的百炬,參考博客沒有備注,如有雷同敬請(qǐng)留言添加

LAYER_TYPE_SOFTWARE:
無論硬件加速是否打開污它,都會(huì)有一張Bitmap(software layer)剖踊,并在上面對(duì)WebView進(jìn)行軟渲染。
好處:
在進(jìn)行動(dòng)畫衫贬,使用software可以只畫一次View樹德澈,很省。
什么時(shí)候不要用:
View樹經(jīng)常更新時(shí)不要用固惯。尤其是在硬件加速打開時(shí)梆造,每次更新消耗的時(shí)間更多。因?yàn)殇秩就赀@張Bitmap后還需要再把這張Bitmap渲染到hardware layer上面去葬毫。

LAYER_TYPE_HARDWARE:
硬件加速關(guān)閉時(shí)镇辉,作用同software。
硬件加速打開時(shí)會(huì)在FBO(Framebuffer Object)上面做渲染贴捡,在進(jìn)行動(dòng)畫時(shí)忽肛,View樹也只需要畫一次。

兩者區(qū)別:
1烂斋、一個(gè)是渲染到Bitmap枫夺,一個(gè)是渲染到FB上躁劣。
2、hardware可能會(huì)有一些操作不支持。
兩者相同:
都是開了一個(gè)buffer法竞,把View畫到這個(gè)buffer上面去。

LAYER_TYPE_NONE:
這個(gè)就比較簡(jiǎn)單了坐漏,不為這個(gè)View樹建立單獨(dú)的layer

PS:GLSurfaceView和WebView默認(rèn)Layertype都是none苫幢。

GLSurfaceView:
給GLSurfaceView設(shè)置為software或者h(yuǎn)ardware后,發(fā)現(xiàn)什么也畫不出來了图张。得出結(jié)論:GLSurfaceView的Layer type只能是none

WebView:
以前使用WebView時(shí)碰到過一個(gè)問題锋拖,如果在WebView上面使用Animation,WebView的繪畫區(qū)域不動(dòng)祸轮。當(dāng)時(shí)的解決方案是在進(jìn)行動(dòng)畫之前對(duì)WebView進(jìn)行截屏(drawingcache)兽埃。按上面的道理試了一下,設(shè)置一個(gè)hardware或者software的layer就OK了适袜。
現(xiàn)在又碰到了另外一個(gè)問題柄错,打開硬件加速后,在一些機(jī)器上面(我的是3.2)WebView有時(shí)會(huì)出現(xiàn)某一塊區(qū)域白屏的問題。默認(rèn)的layer type是none售貌,改為hardware也不行给猾,設(shè)置為software就解決了。當(dāng)然關(guān)閉硬件加速也好了颂跨,可是那樣的話程序整體就比較慢了敢伸。所以最終方案是整體硬件加速,出問題的WebView設(shè)置software

  • 可以讓3D的繪制更快一些:getHolder().setType(SurfaceHolder.SURFACE_TYPE_HARDWARE);
  • 在硬件加速開啟的情況下GLSurfaceView一旦被從View樹上摘下來恒削,會(huì)使整個(gè)窗口背景變黑池颈,即使設(shè)置layer type為software也不管用。
    經(jīng)過兩天的排查钓丰,發(fā)現(xiàn)了原因躯砰,我的程序是在C層由drawFrame(屬于GLThread線程)來驅(qū)動(dòng)進(jìn)行繪畫,當(dāng)GLSurfaceView被摘下來時(shí)携丁,GLSurfaceView的destroy方法被調(diào)用琢歇,我在destroy方法(屬于UI線程)中直接調(diào)用 了GLThread線程的結(jié)束方法。而GLSurfaceView.creat,sizeChanged,destroyed在UI線程则北,Render.create,sizeChanged,drawFrame在GLThread線程矿微。因此,出現(xiàn)了UI線程直接調(diào)用GLThread線程的方法的問題尚揣。最終通過GLSurfaceView.queueEvent向GLThread線程發(fā)送Runnable涌矢,問題得到解決。
    看來快骗,還是軟渲染的容錯(cuò)能力比較強(qiáng)娜庇,一開硬件加速,底層就比較脆弱了方篮。
    結(jié)論:一定要搞清楚哪個(gè)是UI線程名秀,哪個(gè)是GLThread線程。
  • hardware acclerator是對(duì)整個(gè)窗口進(jìn)行加速藕溅,在硬件加速打開時(shí)View.isHardwareAcclerator返回true匕得。但每個(gè)View可能被渲染到的Canvas是不同的,比如View可能被通過setLayer設(shè)置了Layer巾表,這時(shí)汁掠,Canvas.isHardwareAccelerator返回false
  • Android提供了三種硬件加速是否打開的控制級(jí)別,分別是Application,Activity,Window,View集币。

WebView的cookie機(jī)制相關(guān)問題(后續(xù)繼續(xù)補(bǔ)充……)

webview cookie同步

參考博文

因?yàn)閍ndroid不會(huì)自動(dòng)同步cookie到WebView考阱。做iOS開發(fā)則不用擔(dān)心這個(gè)問題,因?yàn)閕os內(nèi)部已經(jīng)實(shí)現(xiàn)了cookie同步鞠苟。本文將會(huì)介紹兩種cookie同步的方式乞榨,并重點(diǎn)分析WebView的cookie機(jī)制秽之。在開始之前先講一下基于session的登錄驗(yàn)證。
Cookie的重要作用是回話識(shí)別(SeesionId)和狀態(tài)長(zhǎng)期保持(在瀏覽器保存需要長(zhǎng)期保持的數(shù)據(jù))吃既。

Cookie在安卓中的使用方式--標(biāo)示會(huì)話考榨,附加信息

  • 通過Session標(biāo)示一次會(huì)話,舉個(gè)例子:注冊(cè)時(shí)鹦倚,判斷客戶端注冊(cè)錯(cuò)誤次數(shù)(注冊(cè)次數(shù)已經(jīng)超過限制董虱,顯示驗(yàn)證碼)
  • 傳遞附加數(shù)據(jù),舉個(gè)例子:傳遞單點(diǎn)登陸的token申鱼。

這里需要注意的是在設(shè)置cookie之后,是不能設(shè)置以下屬性的云头,否則cookie是無效的(不只是這些屬性捐友,這里只是舉例,最好的方式是在執(zhí)行l(wèi)oadurl之前再設(shè)置cookie)

mWvSignUp.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);  
  mWvSignUp.getSettings().setJavaScriptEnabled(true);  
  mWvSignUp.getSettings().setDatabaseEnabled(true);  
  mWvSignUp.getSettings().setDomStorageEnabled(true);  

一些ajax請(qǐng)求需要帶入cookie怎么辦溃槐?

Android 5.0以下

mWvSignUp.setWebViewClient(new WebViewClient() {  
      /** 
        * 5.0以下 
        * @param view 
        * @param url 
        * @return 
        */  
       @Override  
       public WebResourceResponse shouldInterceptRequest(WebView view, String url) {  
           syncCookie(url);  
           return super.shouldInterceptRequest(view, url);//將加好cookie的url傳給父類繼續(xù)執(zhí)行  
       }  
});  

Android 5.0以上

mWvSignUp.setWebViewClient(new WebViewClient() {  
@SuppressLint("NewApi")  
@Override  
public WebResourceResponse shouldInterceptRequest(WebViewview, WebResourceRequest request) {  
String url = request.getUrl().toString();  
syncCookie(url);   
return super.shouldInterceptRequest(view, url);//因?yàn)楦?.0以下的方法返回值是同一個(gè)類匣砖,所以這里偷懶直接調(diào)動(dòng)4.0方法生成請(qǐng)求  
});
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市昏滴,隨后出現(xiàn)的幾起案子猴鲫,更是在濱河造成了極大的恐慌,老刑警劉巖谣殊,帶你破解...
    沈念sama閱讀 206,013評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拂共,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡姻几,警方通過查閱死者的電腦和手機(jī)宜狐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蛇捌,“玉大人抚恒,你說我怎么就攤上這事÷绨瑁” “怎么了俭驮?”我有些...
    開封第一講書人閱讀 152,370評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)春贸。 經(jīng)常有香客問我混萝,道長(zhǎng),這世上最難降的妖魔是什么祥诽? 我笑而不...
    開封第一講書人閱讀 55,168評(píng)論 1 278
  • 正文 為了忘掉前任譬圣,我火速辦了婚禮,結(jié)果婚禮上雄坪,老公的妹妹穿的比我還像新娘厘熟。我一直安慰自己屯蹦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,153評(píng)論 5 371
  • 文/花漫 我一把揭開白布绳姨。 她就那樣靜靜地躺著登澜,像睡著了一般。 火紅的嫁衣襯著肌膚如雪飘庄。 梳的紋絲不亂的頭發(fā)上脑蠕,一...
    開封第一講書人閱讀 48,954評(píng)論 1 283
  • 那天,我揣著相機(jī)與錄音跪削,去河邊找鬼谴仙。 笑死,一個(gè)胖子當(dāng)著我的面吹牛碾盐,可吹牛的內(nèi)容都是我干的晃跺。 我是一名探鬼主播,決...
    沈念sama閱讀 38,271評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼毫玖,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼掀虎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起付枫,我...
    開封第一講書人閱讀 36,916評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤烹玉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后阐滩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體二打,經(jīng)...
    沈念sama閱讀 43,382評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,877評(píng)論 2 323
  • 正文 我和宋清朗相戀三年叶眉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了址儒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,989評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡衅疙,死狀恐怖莲趣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情饱溢,我是刑警寧澤喧伞,帶...
    沈念sama閱讀 33,624評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站绩郎,受9級(jí)特大地震影響潘鲫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜肋杖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,209評(píng)論 3 307
  • 文/蒙蒙 一溉仑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧状植,春花似錦浊竟、人聲如沸怨喘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)必怜。三九已至,卻和暖如春后频,著一層夾襖步出監(jiān)牢的瞬間梳庆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評(píng)論 1 260
  • 我被黑心中介騙來泰國(guó)打工卑惜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留膏执,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,401評(píng)論 2 352
  • 正文 我出身青樓露久,卻偏偏與公主長(zhǎng)得像胧后,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子抱环,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,700評(píng)論 2 345

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,496評(píng)論 25 707
  • 本篇文章是基于谷歌有關(guān)Graphic的一篇概覽文章的翻譯:http://source.android.com/de...
    lee_3do閱讀 7,097評(píng)論 2 21
  • 1.1Controlling Hardware Acceleration 從Android3.0(API Leve...
    android之子閱讀 3,836評(píng)論 0 11
  • ¥開啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個(gè)線程,因...
    小菜c閱讀 6,358評(píng)論 0 17
  • 2017-11-29 1. 感恩智能手機(jī)每天溫馨的給到我服務(wù):叫我起床纸巷; 給到我關(guān)于天氣的訊息镇草;跟家人們隨時(shí)隨地交...
    感恩奇跡閱讀 206評(píng)論 0 0