就目前而言,app的開發(fā)主要分三個(gè)方向:native app洞焙、hybrid app以及web app。個(gè)人感覺三種app的體驗(yàn)感是逐漸遞減的。
hybrid app和web app的開發(fā)的不同之處就是前者需要自己提供和實(shí)現(xiàn)前端需要的接口嗅榕,而后者則是借助一些框架(比如icon、dcloud等)吵聪。實(shí)質(zhì)上都差不多凌那,但前者更靈活一些。如果你還不知道Hybrid App開發(fā)中H5和native如何進(jìn)行交互,那么相信你看完這篇《Android和H5交互-基礎(chǔ)篇》吟逝,也行就許明白帽蝶。
其實(shí)H5和native的交互也就那么幾個(gè)步驟,為了前端能夠統(tǒng)一的調(diào)用原生提供的接口块攒,通常前端和native(ios和Android)端會(huì)做好規(guī)范励稳。前面一篇文章主要是介紹兩者間是如何進(jìn)行交互的,那么這篇文章我向大家介紹一種基于兩者交互的簡單封裝囱井。
如果你在為前端寫接口時(shí)驹尼,你可能會(huì)這么寫:
/**
* dec: js調(diào)用原生接口類
* createBy yjzhao
* createTime 2016/11/15 13:50
*/
public class NativeApi {
/**
* 撥打電話
*
* @param mobile 電話號(hào)碼
*/
@JavascriptInterface
public void openPhone(String mobile) {
...
}
/**
* 發(fā)短信 一個(gè)參數(shù) ISP調(diào)用
*
* @param smsto 電話對(duì)方電話號(hào)碼
*/
@JavascriptInterface
public void opneMsg(String smsto) {
...
}
/**
* 網(wǎng)絡(luò)請(qǐng)求代理
*
* @param url 加載的網(wǎng)絡(luò)URL
* @param data 請(qǐng)求的參數(shù)
* @param jsRe 調(diào)用的函數(shù)名
*/
@JavascriptInterface
public void reqProxy(String url, String data, String jsRe) {
...
}
/**
* 拍照
*/
@JavascriptInterface
public String takePhoto(final String callback) {
...
}
/**
* 選擇照片
*/
@JavascriptInterface
public String selectPhoto(final String callback) {
...
}
/**
* 查看圖片
* @param urls 圖片地址(多個(gè)圖片用,隔開)
*/
@JavascriptInterface
public void browsePhoto(String urls){
....
}
/**
* 讀取文件
*
* @param url 路徑
* @return
*/
@JavascriptInterface
public String loadFile(String url) {
....
}
}
如果是將native接口寫成這樣的話那么前端js調(diào)用的話可能就會(huì)是這樣:
//撥打電話
NativeAPI.openPhone(params);
//發(fā)送短信
NativeAPI.opneMsg(params);
//發(fā)送網(wǎng)絡(luò)請(qǐng)求
NativeAPI.reqProxy(params);
//拍照
NativeAPI.takePhoto(params);
//選擇照片
NativeAPI.selectPhoto(params);
//查看照片
NativeAPI.browsePhoto(params);
//讀取文件
NativeAPI.loadFile(params);
當(dāng)然這么寫也沒問題庞呕,但是就覺得麻煩新翎,你覺得呢程帕?
如果你也是這么寫Android接口的話,你會(huì)發(fā)現(xiàn)在維護(hù)起來會(huì)有些問題的地啰。第一這個(gè)類就會(huì)變得很臃腫愁拭,第二我們知道js調(diào)用Android接口時(shí)是運(yùn)行在一個(gè)叫jsBrigde(我沒記錯(cuò)的話)的子線程中,而Android調(diào)用js方法時(shí)是運(yùn)行在main線程中的,如果需要回調(diào)js 方法亏吝,這里我們需要做一個(gè)線程的切換岭埠。如果我們將這個(gè)類中的每一個(gè)接口方法都獨(dú)立出去單獨(dú)寫一個(gè)類,然后通過統(tǒng)一的接口暴露給前端調(diào)用顺呕,在調(diào)用js方法時(shí)統(tǒng)一切換至主線程中枫攀,那這樣是不是會(huì)好一點(diǎn)呢?
那么如何封裝呢株茶?我介紹下我的思路:
Android端:
step1 給js暴露一個(gè)統(tǒng)一調(diào)用的接口sendMessage
private void addJavascriptInterface(WebView webView) {
webView.addJavascriptInterface(new Object(){
@JavascriptInterface
public void sendMessage(String jsonStr){
mHandleJsMessage.handle(jsonStr);
}
},"native");
}
step2 將js傳過來的數(shù)據(jù)進(jìn)行統(tǒng)一的處理
/**
* 處理js傳遞過來的數(shù)據(jù)
* @param jsonStr js傳遞的數(shù)據(jù)
* @return 是否處理
*/
@TargetApi(Build.VERSION_CODES.KITKAT)
public boolean handle(String jsonStr) {
JsMessage jsMessage = new Gson().fromJson(jsonStr, JsMessage.class);
String action = jsMessage.getAction();
jsCallback = jsMessage.getCallback();
if (null == jsMessage.getAction())
return false;
if (HandleAction(jsonStr, action, mActionMap)) return true;
return false;
}
/**
* 根據(jù)js傳遞過來的action將事件分發(fā)下去
* @param jsonStr js傳遞的數(shù)據(jù)
* @param action js意圖
* @param map js意圖集合
* @return 是否處理存在處理次意圖的接口
*/
@TargetApi(Build.VERSION_CODES.KITKAT)
private boolean HandleAction(String jsonStr, String action, Map<String, Class<? extends JsAction>> map) {
for (String mapAction : map.keySet()) {
if (mapAction.equals(action)) {
try {
mJsAction = map.get(mapAction).newInstance();
if (mJsAction != null) {
mJsAction.handleAction(mContext, jsonStr);
}
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return true;
}
}
return false;
}
step3 將線程切換至主線程并將處理結(jié)果返回前端
public void callback(final WebView webView, final String callback, final Object result){
//切換至主線程
Observable.create(new Observable.OnSubscribe<Object>() {
@Override
public void call(Subscriber<? super Object> subscriber) {
subscriber.onNext("");
}
}).subscribeOn(Schedulers.immediate())
.observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<Object>() {
@Override
public void onCompleted() {}
@Override
public void onError(Throwable e) {}
@Override
public void onNext(Object o) {
if (null==result||null==callback||"".equals(callback))return;
String resultStr= new Gson().toJson(result);
String url = "javascript:"+callback+"("+resultStr+")";
webView.loadUrl(url);
}
});
}
這個(gè)三個(gè)步驟就是核心思路来涨,具體的實(shí)現(xiàn)就不在這貼代碼了,感興趣的可以查看源碼启盛,地址文末會(huì)給出蹦掐。
再看下前端js怎么封裝:
///////////////////////////////
// 調(diào)用原生接口 //
///////////////////////////////
;(function($) {
"use strict"http://使用嚴(yán)格模式
function native(params) {
params = params||{};
if (params==="undefind")return;
if (params.action==="undefind")return;
//固定的三個(gè)屬性和native端一樣,否則native端和解析出錯(cuò)
var Senddata={
action:params.action,
callback:"nativeCallback",
data:params.data,
}
window.nativeCallback = function(data) {
if (params.callback!=="undefind") {
params.callback(data);
}
}
var sendDataStr=JSON.stringify(Senddata);
window.native.sendMessage(sendDataStr);
}
$.native = native;
})($);
這段代碼是不是很簡單僵闯,值得注意的是js傳給native的json數(shù)據(jù)格式是固定的:即
{
"action":"action",
"callback":"nativeCallback",
"data":{這里的數(shù)據(jù)格式和native端的處理action實(shí)現(xiàn)類數(shù)據(jù)格式需協(xié)商一致}
}
how to use?
android端:
compile 'com.zyj:hybridbridge:0.1.0'//添加依賴
1卧抗、首先在activity中初始化
JsBridge.getInstance().init(this, webView)
2、然后為添加需要處理的action以及相應(yīng)的處理類
JsBridge.getInstance().addJsAction(JsDeviceInfo.ACTION, JsDeviceInfo.class);
//JsDeviceInfo的寫法實(shí)例(這個(gè)類需繼承JsAction這個(gè)抽象類并實(shí)現(xiàn)handleAction()方法)
public class JsDeviceInfo extends JsAction {
//這個(gè)action需和前端相對(duì)應(yīng)
public static final String ACTION = "deviceinfo";
@Override
protected void handleAction(Activity context, String jsonStr) {
HandleResult resultEntity =new HandleResult();
DeviceInfoEntity deviceInfoEntity =new DeviceInfoEntity();
deviceInfoEntity.setDeviceName("我的Android客戶端鳖粟!");
resultEntity.setData(deviceInfoEntity);
//處理完相關(guān)業(yè)務(wù)之后將結(jié)果發(fā)送出去社裆,post之后會(huì)自動(dòng)調(diào)用js的callback方法
RxBus.getInstance().post(resultEntity);
}
}
前端:
function callback(backdata) {
//native處理完后會(huì)回調(diào)用這個(gè)方法
}
$.native({
action: "deviceinfo",
callback: callback
});
看完之后是不是覺得不管是前端還是native端都很簡單?所有的action以及傳遞的參數(shù)格式都可以自定義向图,只需保證兩端統(tǒng)一即可泳秀。
如果你感興趣,源碼在這HybridBridge榄攀,歡迎start嗜傅,有什么問題可以留言我會(huì)維護(hù)改進(jìn)的。