這次項目中有一個主頁面?完全是H5的頁面,要求H5調用Native,js交互傳值,看起來貌似很簡單,網上教程一大堆,但是在實際開發過程中還是遇到很多問題,在這里記錄一下。
- 首先,設置user agent,使前端可區分請求來自APP ,這里我設置的是"android_app/1.0.0",名稱加版本號,具體設置什么大家可隨意。
WebSettings settings = webView.getSettings();
String ua = settings.getUserAgentString();
settings.setUserAgentString(ua + "; android_app/1.0.0");
2.H5頁面的登錄,因為我們的應用不需要登錄也能瀏覽,H5的頁面有些也是不需要登陸的,所以點擊H5頁面需要登錄的地方,要跳轉到Native的頁面登錄,登錄成功刷新H5頁面,設置cookie,使WebView頁面保持登錄狀態,具體代碼如下:
注:WebSettings的一些設置一定要放到設置cookie前面執行。
設置cookie要注意作用域的問題,以及setCookie的時候最好分步設置,大家可以看下面的代碼,我setCookie()了四次,不要把所有字符串拼接起來再一次setCookie,一次setCookie可能只會設置成功第一個分號前的cookie值。
public void synCookies(Context context, String host, String cookies) {
try {
CookieSyncManager.createInstance(context);
CookieManager cookieManager = CookieManager.getInstance();
// 5.0以上版本的webview做了較大的改動,如:同步cookie的操作已經可以自動同步、但前提是我們必須開啟第三方cookie的支持。
// cookieManager.setAcceptThirdPartyCookies(webView, true);//5.0以下的手機崩潰
cookieManager.setAcceptCookie(true);
cookieManager.removeSessionCookie();//移除
cookieManager.removeAllCookie();
//base64加密
// String base64Cookies = Base64Utils.encodeStr(getCookies());
//根據RFC822規定,BASE64Encoder編碼每76個字符,還需要加上一個回車換行。
//使用 commons-codec-1.10.jar 不會換行而且效率更高
//這里我使用的是sun.misc.BASE64Decoder.jar,每76個字符會換行,所以下面去掉換行,為什么不用上面的呢,使用commons-codec-1.10.jar,Android Studio編譯提示方法重復,沒找到解決辦法。
// base64Cookies = base64Cookies.replace("\n", "");
// Log.i("base64Cookies:", base64Cookies);
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_MONTH, 7);
String expiresTime = calendar.getTime().toGMTString();
Log.i("expiresTime =", expiresTime);
//分開設置,不然只會設置第一個分號之前的cookie
// cookieManager.setCookie(host, "Token=" + base64Cookies);
//不加密
cookieManager.setCookie(host, "Token=" + cookies);
//注意host的值,類似于這個網址:http://www.lxweimin.com/writer#/notebooks/2498434/notes/4304102/preview,host可以取:www.lxweimin.com或者.jianshu.com,注意作用域,不要把整個url都放上了。
cookieManager.setCookie(host, "Domain=" + host);
cookieManager.setCookie(host, "Path=/");
// Expires變量是一個只寫變量,它確定了Cookie有效終止日期。該屬性值DATE必須以特定的格式來書寫:
// 星期幾,DD-MM-YY HH:MM:SS GMT,GMT表示這是格林尼治時間。反之,不以這樣的格式來書寫,系統將無法識別。
// 該變量可省,如果缺省時,則Cookie的屬性值不會保存在用戶的硬盤中,而僅僅保存在內存當中,Cookie文件將隨著瀏覽器的關閉而自動消失。
cookieManager.setCookie(host, "Expires=" + expiresTime);
CookieSyncManager.getInstance().sync();
String newCookie = cookieManager.getCookie(host);
if (newCookie != null) {
Log.i("getCookie:", newCookie);
}
} catch (Exception e) {
Log.e("failedCookie=%s", e.toString());
}
}
3.自定義scheme、js交互傳值、調用js方法及document對象
例如:登錄scheme為 ?goto://just/loginref=http://www.baidu.com&callback=loginWeb
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//1.登錄scheme url = ?goto://just/login?callback=loginWeb
if (url.startsWith("goto://just/login")) {
//登錄,跳轉到native得LoginActivity
//成功后在onActivityResult()方法中執行js的回調方法loginWeb傳toekn值給H5
return true;
//2.跳轉新activity scheme url = ?goto://just/newweb?ref=http://www.baidu.com
} else if (url.startsWith("goto://just/newweb")) {
//頁面加載不在本頁webview加載,而是新打開此MainActivity(標準模式,為了循環復用)
String ref = "http://www.baidu.com";
Intent intent = new Intent(MainActivity.this, MainActivity.class);
intent.putExtra("webUrl", ref);
startActivity(intent);
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
//頁面錯誤
llError.setVisibility(View.VISIBLE);
}
public void onPageFinished(WebView view, String url) {
Log.d("WebView", "onPageFinished ");
//獲取整個頁面的Html
view.loadUrl("javascript:window.weixinObj.getHtml('<head>'+" +
"document.getElementsByTagName('html')[0].innerHTML+'</head>');");
//通過document.title 獲取頁面的title
view.loadUrl("javascript:window.weixinObj.getTitle(document.title)");
//此方法也可以通過document.title 獲取標題,但是需要Api19才能使用
// view.evaluateJavascript("document.title", new ValueCallback<String>() {
// @Override
// public void onReceiveValue(String title) {
//
// }
// });
super.onPageFinished(view, url);
}
});
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//登錄頁返回后執行
String callback = "loginWeb";
synCookies(this, host, cookies);
webView.loadUrl("javascript:" + callback + "('" + cookies + "')");
webView.reload();
}
private class InterfaceObject {
@JavascriptInterface
public void getHtml(String html) {
//獲取html的內容
}
@JavascriptInterface
public void getTitle(String title) {
//獲取標題
}
}
4.以上是我在項目中使用到的一些交互,經過測試是可用的,其實上面寫的這些網上有很多,但是比較分散,我就自己總結了一下,例子無法運行的(沒有調試,可能有些邏輯錯誤),因為沒有網頁測試,如果使用本地html的也不好模擬網絡上環境,所以只寫了一些邏輯,需要大家自行寫h5測試。
資源:Example下載