Android WebView 302斗爭之旅


一.背景

越來越多的業務接入,項目內多多少少會出現幾個H5頁面,只是單純的提供WebView容器接入H5頁面根本滿足不了需求,他們需要登錄態,需要制定協議控制Native的導航欄,或者需要JsBridge做一些更復雜的操作,這篇主要講登錄態出現的問題。

二.涉及的知識

Android WebView加載url的時候,我們是這樣做監聽的:

  • 頁面加載前會回調onPageStarted
  • 頁面加載完成會回調onPageFinished
  • 當頁面加載前且在onPageStarted后會回調shouldOverrideUrlLoading讓我們決定是否自己處理這個url
public class PerformanceWebClient extends WebViewClient {

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        return super.shouldOverrideUrlLoading(view, url);
    }

    @Override
    public void onPageStarted(WebView view, String url, Bitmap favicon) {
        super.onPageStarted(view, url, favicon);
    }

    @Override
    public void onPageFinished(WebView view, String url) {
        super.onPageFinished(view, url);
    }
}

三.自定義返回棧

14年最初接觸的項目,WebView頁面是需要登錄態的,處理方式就是shouldOverrideUrlLoading方法中,直接將url后面拼入參數,return true告知WebView組件url我們已經處理,你不用管了。

public class PerformanceWebClient extends WebViewClient {

    @Override
    public void onPageStarted(WebView view, String url, Bitmap favicon) {
        super.onPageStarted(view, url, favicon);
    }

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if (url.startsWith("http") || url.startsWith("https")) {
            view.loadUrl(appendParamsToUrl(url));
            return true;
        } else if (isScheme(url)) {
            return true;
        }
        return false;
    }

    @Override
    public void onPageFinished(WebView view, String url) {
        super.onPageFinished(view, url);
    }
}

簡單的代碼結構大致是這樣,但是迅速暴露出一個問題:302頁面返回棧。

我們主動load的頁面會加入到返回棧內,而當我們webview.goBack()的時候,加載的時候還是一個302,導致無法正常關閉頁面。

這時想到一個方法,自己定義返回棧。

問題是,一個url入棧的標准是什么?

舉一個栗子:

A==302==>B==302==>C

加載A的url查看回調順序是:

onPageStartedA==>onPageStartedB==>onPageStartedC==>onPageFinishedC

從這個角度來看,貌似onPageFinished中將url作為已加載的url挺靠譜的,但是現在存在一個問題,我每次回退棧都要以重新load的形式刷新頁面,性能和流量都有耗費。

還存在一個問題,在實際情況中遇到,某一個業務url加載形式例如:

A==302==>B==302==>C==302==>D

加載A的url查看回調順序是這樣的:

onPageStartedA==>onPageStartedB==>onPageStartedC==>onPageFinishedC==>onPageStartedD==>onPageFinishedD

C頁面是302到D的,但是也走了onPageStarted方法,猜測是做了某些操作再主動重定向的,這種url我們也不希望加入返回棧內,但是我們不能准確的檢測到。

四.嘗試優化

在后來接手的另一個項目中,也存在了302返回棧的問題,既要保留登陸態,又需要302不加入返回棧。

這時看到一個這樣的處理方式:

class MerchantOnTouchListener implements View.OnTouchListener {
    @Override
    public boolean onTouch(View view, MotionEvent motionEvent) {
        try {
            WebView.HitTestResult hr = ((WebView) view).getHitTestResult();
            if (hr != null && mLastUrl != null) {
                switch (hr.getType()) {
                    case WebView.HitTestResult.SRC_ANCHOR_TYPE:
                    case WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE:
                        break;
                    case WebView.HitTestResult.UNKNOWN_TYPE:
                        return true;
                        break;
                    default:
                        return false;
                }
                if (previous.isEmpty() || !previous.get(previous.size() - 1).equals(mLastUrl)) {
                    previous.add(mLastUrl);
                }
            }
        } catch (Exception ignored) {
        }
        return false;
    }
}

當我們觸摸屏幕時(通常就是開始點擊鏈接)使用HitTestResult去看狀態,如果得到的type是鏈接跳轉類型,那么將最后加載的url加入返回棧。

實際效果是:302被避免了,但是頁面內如果觸摸的地方url有變化(比如params變了)也會加入返回棧,回退次數還是增加了,而且解決不了重新加載的問題。

就在這時得知了最開始接觸的項目更換了登陸方式,App的登陸就是H5頁面,登陸成功后拿到Cookie,Cookie既可以給Native訪問Api使用,也可以在H5頁面做登錄態使用,頁面棧全部交給WebView容器處理。

以為這就結束了么?

但是又出現了一個問題:運營商劫持

國內的網絡環境大家比較了解,Headers的丟失率比較高,丟失了Cookie就等於丟失了登陸狀態,這是其中一點;其次是如果業務發展已經很龐大,很難從Native Token 走SSO的方式轉化為Cookie方式。

五.總結

目前經歷了幾個項目,一直沒有很優雅的解決302的問題,目前的做法也有一些瑕疵,希望在以后的工作中能找到更好的方法。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM