Android和H5交互-框架篇

進入新的公司已經快半年了,從一個Android native developer慢慢的轉變成hybrid app(Android和前端代碼都要寫)開發了。就目前而言,app的開發主要分三個方向:native app、hybrid app以及web app。個人感覺三種app的體驗感是逐漸遞減的。

hybrid app和web app的開發的不同之處就是前者需要自己提供和實現前端需要的接口,而后者則是借助一些框架(比如icon、dcloud等)。實質上都差不多,但前者更靈活一些。如果你還不知道Hybrid App開發中H5和native如何進行交互,那么相信你看完這篇《Android和H5交互-基礎篇》,也行就許明白。

其實H5和native的交互也就那么幾個步驟,為了前端能夠統一的調用原生提供的接口,通常前端和native(ios和Android)端會做好規范。前面一篇文章主要是介紹兩者間是如何進行交互的,那么這篇文章我向大家介紹一種基于兩者交互的簡單封裝。
如果你在為前端寫接口時,你可能會這么寫:

     /**
     * dec: js調用原生接口類
     * createBy yjzhao
     * createTime 2016/11/15 13:50
     */
    public class NativeApi {    /**
     * 撥打電話
     *
     * @param mobile 電話號碼
     */
    @JavascriptInterface
    public void openPhone(String mobile) {
        ...
    }    /**
     * 發短信 一個參數 ISP調用
     *
     * @param smsto 電話對方電話號碼
     */
    @JavascriptInterface
    public void opneMsg(String smsto) {
        ...
    }     /**
      * 網絡請求代理
      *
      * @param url  加載的網絡URL
      * @param data 請求的參數
      * @param jsRe 調用的函數名
      */
    @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 圖片地址(多個圖片用,隔開)
     */
    @JavascriptInterface
    public void browsePhoto(String urls){
        ....
    }    /**
     * 讀取文件
     *
     * @param url 路徑
     * @return
     */
    @JavascriptInterface
    public String loadFile(String url) {
       ....
    }
}

如果是將native接口寫成這樣的話那么前端js調用的話可能就會是這樣:

//撥打電話NativeAPI.openPhone(params);//發送短信NativeAPI.opneMsg(params);//發送網絡請求NativeAPI.reqProxy(params);//拍照NativeAPI.takePhoto(params);//選擇照片NativeAPI.selectPhoto(params);//查看照片NativeAPI.browsePhoto(params);//讀取文件NativeAPI.loadFile(params);

當然這么寫也沒問題,但是就覺得麻煩,你覺得呢?

如果你也是這么寫Android接口的話,你會發現在維護起來會有些問題的。第一這個類就會變得很臃腫,第二我們知道js調用Android接口時是運行在一個叫jsBrigde(我沒記錯的話)的子線程中,而Android調用js方法時是運行在main線程中的,如果需要回調js 方法,這里我們需要做一個線程的切換。如果我們將這個類中的每一個接口方法都獨立出去單獨寫一個類,然后通過統一的接口暴露給前端調用,在調用js方法時統一切換至主線程中,那這樣是不是會好一點呢?

那么如何封裝呢?我介紹下我的思路:

Android端:
step1 給js暴露一個統一調用的接口sendMessage
 private void addJavascriptInterface(WebView webView) {
    webView.addJavascriptInterface(new Object(){        @JavascriptInterface
        public void sendMessage(String jsonStr){
            mHandleJsMessage.handle(jsonStr);
        }
      },"native");
  }
step2 將js傳過來的數據進行統一的處理
 /**
 *  處理js傳遞過來的數據
 * @param jsonStr js傳遞的數據
 * @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;
  }/**
 *  根據js傳遞過來的action將事件分發下去
 * @param jsonStr js傳遞的數據
 * @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 將線程切換至主線程并將處理結果返回前端
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);
        }
    });
}

這個三個步驟就是核心思路,具體的實現就不在這貼代碼了,感興趣的可以查看源碼,地址文末會給出。
再看下前端js怎么封裝:

  /////////////////////////////////        調用原生接口        /////////////////////////////////;(function($) {"use strict"//使用嚴格模式
  function native(params) {
    params = params||{};    if (params==="undefind")return;    if (params.action==="undefind")return;    //固定的三個屬性和native端一樣,否則native端和解析出錯
    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數據格式是固定的:即

{    "action":"action",    "callback":"nativeCallback",    "data":{這里的數據格式和native端的處理action實現類數據格式需協商一致}
}

how to use?

android端:

  compile 'com.zyj:hybridbridge:0.1.0'//添加依賴

1、首先在activity中初始化

JsBridge.getInstance().init(this, webView)

2、然后為添加需要處理的action以及相應的處理類

JsBridge.getInstance().addJsAction(JsDeviceInfo.ACTION, JsDeviceInfo.class); //JsDeviceInfo的寫法實例(這個類需繼承JsAction這個抽象類并實現handleAction()方法)public class JsDeviceInfo extends JsAction { //這個action需和前端相對應public static final String ACTION = "deviceinfo";@Overrideprotected void handleAction(Activity context, String jsonStr) {    HandleResult resultEntity =new HandleResult();    DeviceInfoEntity deviceInfoEntity =new DeviceInfoEntity();
    deviceInfoEntity.setDeviceName("我的Android客戶端!");
    resultEntity.setData(deviceInfoEntity);    //處理完相關業務之后將結果發送出去,post之后會自動調用js的callback方法
    RxBus.getInstance().post(resultEntity);
  }
}

前端:

  function callback(backdata) {            //native處理完后會回調用這個方法
        }
  $.native({            action: "deviceinfo",            callback: callback
        });

看完之后是不是覺得不管是前端還是native端都很簡單?所有的action以及傳遞的參數格式都可以自定義,只需保證兩端統一即可。

來源:簡書

上一篇: Android和H5交互-基礎篇

下一篇: 完全理解android事件分發機制

分享到: 更多
北京pk10手机版走势图 四人麻将免费打 龙虎相斗是真的吗 3D最近100期走势图开门彩 彩赢计划软件下载 三分pk稳赚技巧 彩票七码计划稳吗 重庆时时开奖时间安排 赛车北京pk10技巧 时时彩网站 时时彩3把赢了200万 重庆时时彩软件 四川时时走势图开奖结果 浙江快乐时时走势图 江苏时时开奖走势图 pc28预测软件手机版