[疑難雜症] Android WebView 沒法打開天貓頁面

歡迎轉載,但請務必註明出處!html

http://ryanhoo.github.io/blog/2014/09/17/android-webview-setdomstorageenabledhtml5

Android WebView 忽然沒法打開天貓的詳情頁,一直停留在加載狀態。而在此以前,應用裏是徹底能夠正常訪問的,經過搜索,找到解決方法,簡單設置一行代碼
webView.getSettings().setDomStorageEnabled(true) 便可解決問題,但背後的緣由又是什麼呢?java

Android WebView Can't Load TMall Pages

咱們不能只是作解決問題的程序員,還要作好奇的程序員,跟我來一探究竟吧。android

定位問題

本着發現問題 -> 分析問題 -> 解決問題的步驟,接下來咱們要定位問題的來源。git

Chrome Inspcet Device

我首先想到的是,Android 4.4 之後的 WebView 是基於 chromium 內核的,這帶來了一個福利是,咱們能夠在 chrome 中 inspect device,像普通網頁同樣,進行 debug,精準、快速定位問題來源。正好我手頭有 Nexus 5 刷的是 Android L Preview 還有 Mi3 是最新的 MIUI V6,打開 Chrome,Tools -> Inspect Devices,就能夠看到原生應用裏的 WebView 頁面了。程序員

Chrome Inspect Devices

點擊 inspect 便可進入 Developer Tools,一切盡在掌控,能夠清楚看到 console 輸出的錯誤信息。github

Chrome Developer Tools

Android Logcat

實際上,網頁加載中 js 輸出的錯誤信息也完整的打印在了 Logcat 中。web

09-17 11:28:20.265  25993-25993/ I/chromium﹕ [INFO:CONSOLE(2)] "TypeError: Cannot read property 'hotelLon' of null
  at BaseDerived.g.extend.loadItemData (http://g.tbcdn.cn/trip/h5-hotel/0.3.52/??pages/tuan/detail.js:2:8436)
  at BaseDerived.g.extend.initializer (http://g.tbcdn.cn/trip/h5-hotel/0.3.52/??pages/tuan/detail.js:2:340)
  at BaseDerived.initializer (http://g.tbcdn.cn/trip/h5-hotel/0.3.52/??libs/seed.js,config.js,widgets/bridge/index.js,widgets/mtop/index.js?v=1203354280_108918:3:24184)
  at BaseDerived.Base (http://g.tbcdn.cn/trip/h5-hotel/0.3.52/??libs/seed.js,config.js,widgets/bridge/index.js,widgets/mtop/index.js?v=1203354280_108918:3:24486)
  at BaseDerived.S.extend.callSuper (http://g.tbcdn.cn/trip/h5-hotel/0.3.52/??libs/seed.js,config.js,widgets/bridge/index.js,widgets/mtop/index.js?v=1203354280_108918:3:26305)
  at BaseDerived (eval at extend (http://g.tbcdn.cn/trip/h5-hotel/0.3.52/??libs/seed.js,config.js,widgets/bridge/index.js,widgets/mtop/index.js?v=1203354280_108918:3:27954), <anonymous>:1:51)
  at BaseDerived.S.extend.callSuper (http://g.tbcdn.cn/trip/h5-hotel/0.3.52/??libs/seed.js,config.js,widgets/bridge/index.js,widgets/mtop/index.js?v=1203354280_108918:3:26305)
  at new BaseDerived (eval at extend (http://g.tbcdn.cn/trip/h5-hotel/0.3.52/??libs/seed.js,config.js,widgets/bridge/index.js,widgets/mtop/index.js?v=1203354280_108918:3:27954), <anonymous>:1:51)
  at Object.<anonymous> (http://h5.m.taobao.com/trip/hotel/tuan/detail.html?itemId=40987076007:426:22)
  at o (http://g.tbcdn.cn/trip/h5-hotel/0.3.52/??libs/seed.js,config.js,widgets/bridge/index.js,widgets/mtop/index.js?v=1203354280_108918:3:4799)", source: http://g.tbcdn.cn/trip/h5-hotel/0.3.52/??libs/seed.js,config.js,widgets/bridge/index.js,widgets/mtop/index.js?v=1203354280_108918 (2)
09-17 11:28:20.265  25993-25993/ I/chromium﹕ [INFO:CONSOLE(2)] "Uncaught TypeError: Cannot read property 'hotelLon' of null", source: http://g.tbcdn.cn/trip/h5-hotel/0.3.52/??libs/seed.js,config.js,widgets/bridge/index.js,widgets/mtop/index.js?v=1203354280_108918 (2)

分析問題

追根溯源

以上兩種方式找到的錯誤信息(相同的)列出了錯誤棧信息,第一條錯誤是發生在 http://g.tbcdn.cn/trip/h5-hotel/0.3.52/??pages/tuan/detail.js 的第 2 行第 8436 列。在瀏覽器中打開這個 js 文件,顯而易見它是被壓縮過的,複製在 IDE 如 Sublime Text 中格式化,根據錯誤信息 "TypeError: Cannot read property 'hotelLon' of null 搜索 hotelLon,能夠找到相關代碼。chrome

loadItemData: function() {
    var a = this,
        c = !1;
    a.loading.show(), a.curLongitude = a.getRequestParam("longitude") 
        || a.getRequestParam("lng") 
        || localStorage.hotelLon 
        || "", a.curLatitude = a.getRequestParam("latitude") 
        || a.getRequestParam("lat") 
        || localStorage.hotelLat 
        || "", a.fetchItemData(function(d) {
        return a.loading.hide(), a.detailData = c ? MockData.data : d.data, $.isEmptyObject(d.data) && !c ? d.ret && d.ret[0] ? void a.showError(".J_detail", d.ret[0].split("::")[1].split("##")[0] + o) : void a.showError(".J_detail", "\u4eb2\uff0c\u8be5\u9152\u5e97\u6682\u65e0\u56e2\u8d2d\u4fe1\u606f\u3002" + o) : "1" == a.detailData.isPreItem ? void(window.location.href = b("http://h5.m.taobao.com/awp/core/detail.htm?id=${itemId}&ttid=${ttid}", {
            itemId: a.getRequestParam("itemid"),
            ttid: a.ttid
        })) : (a.detailData = a.dirtyDataProcess(a.detailData), a.render($(".J_detail"), a.detailData), a.renderTitle(a.detailData), a.countdownInit(), a.submitButtonStatusInit(), n.init(function() {}), a.sliderInit(), a.detailData.propertiesMap && a.skuModule.init($(".J_sku"), a.detailData), a.priceModule.init($(".J_discount")), a.loadRateInfo(a.detailData.hotelList[0].hid, a.detailData.hotelList[0].shid), a.bindSliderClick(), a.bindServiceClick(), a.bindRateClick(), a.bindSubHallClick(), a.bindDescClick(), a.bindMapClick(), a.bindSkuModuleEvent(), a.bindRecommendItemClick(), a.bindFavoriteClick(), a.bindShareClick(), a.bindSubmitEvent(), a.isTripClient() && ($(".J_discount").removeClass("sticky"), $(".J_favorite").hide(), $(".fav-line").hide()), i.locStorage.set("hotel-tuan-detailHotelList", JSON.stringify({
            hotelList: a.detailData.hotelList
        })), $(".call-seller").parent().on("touchstart", function() {
            a.sendPoint("Button", "TuanHotel", "Call")
        }), void $(".wangxin-waptowx").on("touchstart", function() {
            a.sendPoint("Button", "TuanHotel", "Contact")
        }))
    }, function() {
        a.loading.hide(), a.showError(".J_detail", "\u4eb2\uff0c\u7f51\u7edc\u5f00\u5c0f\u5dee\uff0c\u8bfb\u53d6\u6570\u636e\u5931\u8d25\u3002" + o)
    })
},

代碼分析

在上面這段代碼的 loadItemData 方法中,第 6 行 localStorage.hotelLon,在這裏讀取了 localStorage 的 hotelLon 信息,顧名思義,就是酒店的經度,可是提示的錯誤信息是沒法讀取 hotelLon。若是對 localStorage 有所瞭解的話,應該知道即使沒有 hotelLon,也不會報錯,而是 undefinedapi

localStorage fetch a non-existed variable

localStorage

到這裏,須要補充一下 localStorage 的知識。

HTML5 Storage主要有:

  • sessionStorage: 會話 (session) 級別的數據存儲,會話結束後,相關的數據就會被清除掉。
  • localStorage: 用於持久化的本地存儲,除非主動刪除數據,不然數據是永遠不會過時的。

做爲 HTML5 標準的一部分,絕大多數的瀏覽器都是支持 localStorage 的,可是鑑於它的安全特性(任何人都能讀取到它,儘管有相應的限制,將敏感數據存儲在這裏依然不是明智之舉),Android 默認是關閉該功能的。

解決問題

通過層層分析,問題指向如何開啓 localStorage 了。

// Gets whether the DOM Storage APIs are enabled.
webView.getSettings().setDomStorageEnabled(true);

參考

  1. DOM Storage guide
  2. Dive into HTML5: LOCAL STORAGE
  3. HTML5之Web Storage(本地存儲)詳解
  4. StakeOverflow: What does 「enable DOM storage API」 mean?
相關文章
相關標籤/搜索