一.背景 越來越多的業務接入,項目內多多少少會出現幾個H5頁面,只是單純的提供WebView容器接入H5頁面根本滿足不了需求,他們需要登錄態,需要制定協議控制Native的導航欄,或者需要JsBridge做一些更複雜的操作,這篇主要講登錄態出現的問題。 二.涉及的知識 Android WebView ...
一.背景
越來越多的業務接入,項目內多多少少會出現幾個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的問題,目前的做法也有一些瑕疵,希望在以後的工作中能找到更好的方法。