Android-Hybrid-問題收集&Android客戶端無法攔截Vue路由的問題

1. 在Android客戶端或Chrome瀏覽器彈出一個引導用戶關注公衆號的二維碼

描述

客戶端加載線上義診Url,偶爾會彈出一個要求關注丁香園公衆號的二維碼 

問題分析

後端會根據當前登錄用戶向前端界面注入一段如下js 

<script>
    window.initialState = {
        currentLoginUser: {
            avatar: "https://askpub.dxycdn.com/avatar/128/default.png",
            simUid: "0",
            userId: "0",
            showApplyAnswererHint: false,
            isApplied: false,
            wxFollow: false
        }
        , environment: {
            server: 'prd'
        }
    }
</script>

界面上根據wxFollow字段以及白名單決定是否彈出公衆號的二維碼
當用戶未登錄卻進入到了義診界面,或者已登錄但未關注公衆號,同時,Url未在白名單內時,將引導用戶關注公衆號 

解決方案

檢查wxFollow字段是否爲false,檢查白名單內有無義診的Url。
與產品討論,最終確認將義診Url加入到白名單,不再在此界面引導用戶關注公衆號。 

2. Android客戶端無法攔截Vue路由的問題

描述

客戶端加載使用vue編寫的單頁面應用,vue進行路由時,沒有回調WebView的shouldOverrideUrlLoading生命週期方法。onPageFinished生命週期方法被調用,但無法進行路由攔截。 

問題分析

客戶端加載使用vue編寫的單頁面應用,會將整個站點所有vue組件及較小的資源文件下載到本地,當用戶點擊醫生條目將要跳轉至醫生詳情頁時,並不會產生網絡資源的加載,也就不會回調客戶端WebView類似shouldOverrideUrlLoading的生命週期方法。同時,從vue-router.js可知,createHref創建出類似[/#/demo]的錨點。vue路由時,根據ua進行區分,對於支持pushState的使用window.history.pushState(window.history.replaceState)方法修改Url,不觸發hashchange,否則使用window.location.hash(window.location.replace)修改Url,觸發hashchange。客戶端不應僅根據hashchange來判斷vue的路由變化。 

示例: 

window.history.pushState({ key: "475593.080" }, '', "http://localhost:8080/#/demo")
window.history.replaceState({ key: "123" }, '', "http://localhost:8080/#/")
window.location.hash="#/demo"
window.location.replace="#/demo"

解決方案

可以在入口js中(main.js),使用router.beforeEach註冊一個全局前置鉤子,同時,爲確保客戶端可以監控到所有vue路由信息,可以將鉤子放置在router.start(new Vue())之前。當vue路由發生,全局前置鉤子將按照創建順序調用。鉤子方法爲異步解析執行,同時,路由將會等待所有鉤子resolve完。需要確保所有鉤子一定調用了next方法,否則鉤子將不會被resolve,造成路由阻塞。
問題一:不調用next方法,將阻塞路由,造成history混亂。
問題二:to、from參數轉化爲json時要考慮循環引用問題,JSON.stringify()無法處理這些問題,可以引入三方庫解決。
問題三:若vue路由時出現異常,整個跳轉將出現混亂。當界面上其它地方出現異常,同時使用window.onerror(Vue.config.errorHandler或者try-catch)處理了異常,並進行了統一處理,應當小心添加統一處理異常的代碼。 

示例: 

import Util from 'util'
router.beforeEach((to, from, next) => {
  if (window.AndroidJSBridger && window.AndroidJSBridger.beforeEach) {
      const toStr = to ? Util.inspect(to) : '{}';
      const fromStr = from ? Util.inspect(from) : '{}';
      //返回值boolean爲true表示客戶端已經攔截本次路由,將做進一步處理,返回false,表示客戶端不關心本次路由。
      const ret = window.AndroidJSBridger.beforeEach(to.fullPath,toStr,fromStr)
      if(ret){
        next(false);
      }else{
        next(true);
      }
  } else {
    next(true);
  }
})

3. 前端接收到兩個DXYJSBridgeReady事件的問題

描述

當h5界面運行在客戶端時,h5界面監聽了DXYJSBridgeReady事件,並收到多個DXYJSBridgeReady事件。 

問題分析

當h5界面運行在客戶端,需要調用客戶端一些Api函數,客戶端需要準備一些資源,並初始化這些Api函數。正是由於h5界面需要等待客戶端初始化環境,固監聽DXYJSBridgeReady事件,等客戶端環境初始化。
客戶端通過loadUrl(evaluateJavascript)向h5界面注入一段js,並通過window.document.dispatchEvent發送一個DXYJSBridgeReady事件,通知h5界面環境初始化完成。
當h5界面接收到多個DXYJSBridgeReady事件時,可能原因有如下幾種,需逐一排查:
1)客戶端反覆注入同一JS片段,並且沒有做防重複分發操作(解決-window.hasInitDXYJSBridgeReady標記位)
2)h5界面多處監聽DXYJSBridgeReady事件或沒做防重複監聽操作(解決-全局搜索DXYJSBridgeReady,並刪除)
3)h5界面主動分發了DXYJSBridgeReady事件(解決-全局搜索DXYJSBridgeReady,並刪除)
3)h5界面監聽DXYJSBridgeReady事件時,使用的立即執行函數(解決—查看監聽器的寫法,防止監聽器立即執行) 

解決方案

develop模式下,h5界面模擬原生客戶端進行事件分發。界面上主動分發了DXYJSBridgeReady事件。故只需要刪除模擬分發代碼片段即可。 

4. develop is not defined異常的問題

描述

查看義診頁,點擊醫生條目客戶端控制檯偶爾顯示develop is not defined異常,此時整個界面無法響應事件。 

問題分析

當點擊醫生條目時,觸發了h5界面上的某些資源加載代碼。此時WebView的生命週期方法onProgressChanged將被調用。客戶端會將此情況理解爲新界面的加載,並重新注入JS片段,爲“新的”h5界面準備環境。
出現develop is not defined異常可能的原因有如下幾種,需逐一排查:
1)客戶端爲“新的”h5界面準備環境時注入的JS片段中包含對未定義的develop變量的使用。(解決-查看所有客戶端注入的js片段)
2)h5界面進行跳轉時,調用中某些自定義函數或者三方庫中包含對未定義的develop變量的使用。(解決-全局搜索整站所有前端代碼) 

解決方案

客戶端爲h5界面準備環境時,需利用window.env,告知h5界面客戶端當前所處狀態。由於客戶端注入的js片段全部爲字符串逐段拼接,導致調用了未定義的develop變量。只需要將develop變量轉化爲字符串即可解決。爲規範化注入的JS,可以將待注入的js放入assets中,使用壓縮-讀取-替換的方式替代原有方案。壓縮時,應當小心由單行註釋或者末尾省略分號而帶來的問題。 

5. shouldOverrideUrlLoading用法過時問題

描述

WebView生命週期方法shouldOverrideUrlLoading使用了過舊的方式重寫,可能導致整個WebView生命週期方法調用的改變。 

問題分析

爲支持2.X版本,以往WebView的shouldOverrideUrlLoading的用法爲: 

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    boolean isProcessed = processJumpUrl(url);
    if(!isProcessed) {
        if(mWebView != null) {
            mWebView.loadUrl(url);
        }
        return false;
    } else {
        return isProcessed;
    }
}

4.X以後
若沒有設置WebViewClient則由系統(Activity Manager)處理該 url,通常是使用瀏覽器打開或彈出瀏覽器選擇對話框。
若設置WebViewClient且該方法返回true ,則說明由應用的代碼處理該url,WebView不處理,也就是程序員自己做處理。
若設置WebViewClient且該方法返回false,則說明由WebView處理該url,即用WebView加載該url。
用法如下:

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    boolean isProcessed = processJumpUrl(url);
    if (isProcessed){
        return true;
    }else {
        return super.shouldOverrideUrlLoading(view,url);
    }
}

解決方案

使用Google規定的方法重寫WebView的shouldOverrideUrlLoading生命週期方法。 

6. 解決義診界面連續點擊時與原生限流產生的衝突問題

描述

在客戶端上,連續點擊h5界面條目,客戶端無法有效攔截所有h5的路由事件。產生的現象是,單擊正常,而雙擊之後,h5跳轉到了登錄界面或者醫生詳情界面。 

問題分析

客戶端上,vue路由時,會詢問客戶端是否需要接管路由,若客戶端表示不關心本次路由,h5界面將繼續自身的路由操作。
對於h5界面發出的事件請求,客戶端做了限流操作,減少用戶因誤操作,多次點擊同一條目而產生的困擾。
單擊時,客戶端會接管路由,並正常進行處理。
雙擊時,客戶端忽略了第二次路由,導致h5界面觸發了原有路由規則。
此處,還有一點需要注意。限流操作時間閥值的計算應當小心修改,防止因頻繁更新初始時間產生連續點擊而不響應事件的效果。 

解決方案

客戶端做限流操作時,不能簡單的直接忽略前端的路由請求,而應當將限流操作的意圖通過某種方式,反饋到前端界面。可以通過假裝已處理第二次路由的方式,讓前端界面誤認爲本次路由已經被有效處理,而不再執行原有路由規則。 

7. 關於義診界面打開過慢的問題

描述

在客戶端上,特別是網絡較差的環境下,客戶端打開義診界面,加載速度非常緩慢。 

問題分析

1)vue單界面應用,爲提高用戶進入站點後,進行站內跳轉和操作的響應速度,使用了一次加載站點所有組件及較小的資源文件的方式。此方式以少量犧牲流量和界面初始化速度的方式換取了用戶進入站點之後的操作響應速度。可考慮拆分的方式或者分平臺自動打包的方式解決此問題。
2)vue應用可能包含一些不必要的三方庫,影響界面加載速度。
3)客戶端未正確設置緩存模式,導致界面二次加載過慢。
4)客戶端安裝包中可以提前包含部分三方庫文件或者其它資源文件,待界面初次打開時,劫持並直接返回本地資源。
5)客戶端針對部分或所有Url,與前端約定一套緩存規則,攔截Url-加載並緩存遠程資源-二次打開返回本地數據。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章