html5+原生 js互調

隨着移動互聯網的高速發展,常規的開發速度已經漸漸不能滿足市場需求。原生H5混合開發應運而生,目前,市場上許多主流應用都有用到混合開發,例如支付寶、美團等。下面,結合我本人的開發經驗,簡單談一下對混合開發的認識以及實現方式。

混合開發的優點

優點顯而易見,由前端工程師寫一個頁面,多個平臺都可以運行,省了Android和iOS工程師不少事,無形中提高了開發效率,節約了開發成本。

缺點

凡是使用過的人都知道,H5的界面顯示在手機上,對點擊、觸摸、滑動等事件的響應並不如原生控件那樣流暢,甚至還會出現卡頓。這樣也很正常,如果體驗跟原生控件一樣好的話,也就沒android(ios)工程師什麼事了。

H5調用原生的方式

方式可能有多種,根據我本人的開發經驗,我接觸過兩種方式。

第一種

1.首先對WebView進行初始化

WebSettings settings = webview.getSettings();
settings.setJavaScriptEnabled(true); //允許在WebView中使用js
2.創建一個類JavaScriptMetod,專門用來給js提供可調用的方法

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。
發佈了31 篇原創文章 · 獲贊 15 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章