Android WebView 和 手機後退按鈕 的故事

[故事概要]
         今天調查一個線上Bug。
         產品本身是一個Android 應用。 已APK的方式安裝和運行在Android設備上。整體的架構是naive 開發搭框架,中間嵌入WebView。這些都沒什麼好說的。
         這個線上Bug的問題是,在某些特定畫面遷移過程中,Android設備的後退按鈕會失效。 絕大多數的畫面都是可以的。
         比如以下這種Case的畫面遷移:
            A一覽畫面  ->  B 登陸畫面 -> C 登陸確認畫面  -> D 登陸終瞭畫面  -> A 一覽畫面
         對應每個階段的畫面URL是:
           /list      ->   /xxxregiest?id=12 -> /xxxregiest->   /xxxregiest  ->   /list    
         現象則是,在整個遷移的鏈條走完之後,又回到A 一覽畫面之後, 此時再點擊 Android設備的後退按鈕,什麼反應都沒有。。。。
 
 
[調查和分析]
         首先,想到用Android設備自帶的瀏覽器來做一次操作。
         在Android自帶的瀏覽器上,當走完整個遷移鏈條之後,此時點返回按鈕,會出現《post送信重復提交》的信息。。。。
         於是我果斷想到,這肯定是因為畫面遷移之間,有form提交,所以WebView回退的時候,失敗瞭。
 
         於是我換瞭個平板測試,結果發現,在 B 登陸畫面 -> C 登陸確認畫面的遷移上,回退按鈕是有效的。
         但是B 登陸畫面 -> C 登陸確認畫面的遷移也是依靠form提交完成的阿?
         問題究竟在哪裡呢?
         啊啊啊!!!我看到瞭,那一霎那。
        回退按鈕無效的根本原因在於:
               /xxxregiest 不是Rest的,也就是說在load這個url的時候,後臺程序會根據post送信來決定forward到哪裡去。
              B 登陸畫面 -> C 登陸確認畫面  -> D 登陸終瞭畫面,進行瞭兩次post數據的提交。並且系統用瞭token來避免重復提交。
              也就是說,兩次post數據提交的過程是不可逆的。 當到最後,再點擊回退按鈕的時候,隻是單純的重復提交整個數據鏈的最後一次post,必然是失敗的。
 
[對應]
       最開始我想到的方法是,在form提交的js中,調用後臺JsInterface的代碼,做webView.clearHistory()。
       這樣,讓不想回退的畫面,在前畫面提交以前就把webView裡面的歷史紀錄全部清掉。
       但是後來發現,clearHistory隻能在當前畫面初期化完成之後才可以生效。
      
      最後,能夠簡單有效的就是捕捉當前的keydown事件,在裡面根據當前的URL死判畫面的遷移。
      簡而言之,重寫整個Web畫面的後退流程。
      代碼的例子:
[java] 
@Override 
    public boolean onKeyDown(int keyCode, KeyEvent event) { 
 
        if (webViewUrl == null) { 
            return super.onKeyDown(keyCode, event); 
        } 
 
        try { 
            switch (keyCode) { 
            case KeyEvent.KEYCODE_BACK: 
 
                 //取得當前url               
                String currentUrl = webView.getUrl(); 
                 //根據業務邏輯重排流程 
                if (currentUrl.indexOf("xxx_regist") != -1) { 
                    return true; 
                } else if (currentUrl.indexOf("list") != -1) { 
                    webView.clearHistory(); 
                    webView.loadUrl(this.getResources().getString(R.string.top_page)); 
                    return true; 
                } 
                //預留下來的,交給webView自己判斷 
                if (webView.canGoBack()) { 
                    webView.goBack(); 
                    return true; 
                } else { 
                   //如果沒有可以back的url,並且是首頁,則跳出退出對話框  
                   if (webViewUrl.equals(this.getResources().getString(R.string.top_page))) { 
                        DialogFactory.exitApplicationDialog(this).show(); 
                        return false; 
                    }                  
                   } 
        } catch (Exception e) { 
        } 
        return super.onKeyDown(keyCode, event); 
    } 

 

作者:nanjingjiangbiao

發佈留言