原文:http://blog.csdn.net/so_huangbo/article/details/55522205?utm_source=itdadao&utm_medium=referraljavascript
隨着移動互聯網的高速發展,常規的開發速度已經漸漸不能知足市場需求。原生H5混合開發應運而生,目前,市場上許多主流應用都有用到混合開發,例如支付寶、美團等。下面,結合我本人的開發經驗,簡單談一下對混合開發的認識以及實現方式。前端
優勢顯而易見,由前端工程師寫一個頁面,多個平臺均可以運行,省了Android和iOS工程師很多事,無形中提升了開發效率,節約了開發成本。java
凡是使用過的人都知道,H5的界面顯示在手機上,對點擊、觸摸、滑動等事件的響應並不如原生控件那樣流暢,甚至還會出現卡頓。這樣也很正常,若是體驗跟原生控件同樣好的話,也就沒android(ios)工程師什麼事了。android
方式可能有多種,根據我本人的開發經驗,我接觸過兩種方式。ios
1.首先對WebView進行初始化web
WebSettings settings = webview.getSettings(); settings.setJavaScriptEnabled(true); //容許在WebView中使用js
2.建立一個類JavaScriptMetod,專門用來給js提供可調用的方法json
3.建立該類的構造方法,提供兩個參數,WebView對象和上下文對象安全
private Context mContext; private WebView mWebView; public JavaScriptMethod(Context context, WebView webView) { mContext = context; mWebView = webView; }
4.建立一個字符串常量,做爲android與js通訊的接口,即字符串映射對象網絡
public static final String JAVAINTERFACE = "javaInterface";
5.接下來就是建立給js調用的方法,方法的參數接收一個json字符串(注意:在Android4.2以後,爲了提升代碼安全性,方法必須使用註解@JavascriptInterface,不然沒法調用)前端工程師
@JavascriptInterface //andorid4.2(包括android4.2)以上,若是不寫該註解,js沒法調用android方法 public void showToast(String json){ Toast.makeText(context, json, Toast.LENGTH_SHORT).show(); }
6.在WebView初始化代碼中執行以下代碼,
//建立上面建立類的對象 JavaScriptMetod m = new JavaScriptMetod(this, webview); //其實就是告訴js,我提供給哪一個對象給你調用,這樣js就能夠調用對象裏面的方法 //第二個參數就是該類中的字符串常量 webview.addJavascriptInterface(m, JavaScriptMetod.javaInterface);
如今,在js中就能夠調用JavaScriptMetod中的方法了,調用方式以下
//參數通常爲json格式 var json = {"name":"javascript"}; //javaInterface是上面所說的字符串映射對象 window.javaInterface.showToast(JSON.stringify(json));
網絡上介紹js與android原生交互的文章裏,大部分都是上面這種方式,可是這種方式並不適用於ios,也就是說,window.javaInterface.showToast(JSON.stringify(json))這樣的js代碼並不適用於ios,若是用以上的方法,就得分別爲android和ios各寫一套js代碼。這樣很顯然是不太合理的,因此在實際開發中,通常都使用接下來的第二種方法。
這種方法實現的思想是js發出一個url請求,並將所需的參數添加到該url中。android端經過webView.setWebViewClient()攔截url,解析url中攜帶的參數,並根據參數信息進行相應的操做。
1.與方法一相同,首先都須要對webview進行初始化
WebSettings settings = webview.getSettings(); settings.setJavaScriptEnabled(true); //容許在WebView中使用js
2.首先看js中的代碼是怎麼寫的,
$("#showtoast").click(function () { var json = {"data": "I am a toast"}; window.location.href="protocol://android?code=toast&data="+JSON.stringify(json); }); $("#call").click(function () { var json = {"data": "10086"}; window.location.href="protocol://android?code=call&data="+JSON.stringify(json); });
這裏定義兩個點擊事件,分別控制android顯示吐司和打電話的操做。其中,protocol://android爲自定義的H5與android間的通訊協議,與http請求進行區分。code規定了要進行的操做,data爲傳輸的數據。
2.android中的代碼
webView.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { /** * 經過判斷攔截到的url是否含有pre,來辨別是http請求仍是調用android方法的請求 */ String pre = "protocol://android"; if (!url.contains(pre)) { //該url是http請求,用webview加載url return false; } //該url是調用android方法的請求,經過解析url中的參數來執行相應方法 Map<String, String> map = getParamsMap(url, pre); String code = map.get("code"); String data = map.get("data"); parseCode(code, data); return true; } });
其中,getParamsMap()方法從攔截到的url解析出code,data參數,parseCode()方法將根據不一樣的code進行相應的操做,代碼以下:
private Map<String, String> getParamsMap(String url, String pre) { ArrayMap<String, String> queryStringMap = new ArrayMap<>(); if (url.contains(pre)) { int index = url.indexOf(pre); int end = index + pre.length(); String queryString = url.substring(end + 1); String[] queryStringSplit = queryString.split("&"); String[] queryStringParam; for (String qs : queryStringSplit) { if (qs.toLowerCase().startsWith("data=")) { //單獨處理data項,避免data內部的&被拆分 int dataIndex = queryString.indexOf("data="); String dataValue = queryString.substring(dataIndex + 5); queryStringMap.put("data", dataValue); } else { queryStringParam = qs.split("="); String value = ""; if (queryStringParam.length > 1) { //避免後臺有時候不傳值,如「key=」這種 value = queryStringParam[1]; } queryStringMap.put(queryStringParam[0].toLowerCase(), value); } } } return queryStringMap; } private void parseCode(String code, String data) { if(code.equals("call")) { try { JSONObject json = new JSONObject(data); String phone = json.optString("data"); //執行打電話的操做,具體代碼省略 PhoneUtils.call(this, phone); } catch (JSONException e) { e.printStackTrace(); } return; } if(code.equals("toast")) { try { JSONObject json = new JSONObject(data); String toast = json.optString("data"); Toast.makeText(this, toast, Toast.LENGTH_SHORT).show(); } catch (JSONException e) { e.printStackTrace(); } return; } }
最後,特別說明一下shouldOverrideUrlLoading()方法的返回值問題,該方法的返回值有三種:
1.返回true,即根據代碼邏輯執行相應操做,webview不加載該url;
2.返回false,除執行相應代碼外,webview加載該url;
3.返回super.shouldOverrideUrlLoading(),點進父類中,咱們能夠看到,返回的仍是false。