Android和H5交互-框架篇

就目前而言,app的開發(fā)主要分三個(gè)方向:native app洞焙、hybrid app以及web app。個(gè)人感覺三種app的體驗(yàn)感是逐漸遞減的。

hybrid app和web app的開發(fā)的不同之處就是前者需要自己提供和實(shí)現(xiàn)前端需要的接口嗅榕,而后者則是借助一些框架(比如icondcloud等)吵聪。實(shí)質(zhì)上都差不多凌那,但前者更靈活一些。如果你還不知道Hybrid App開發(fā)中H5和native如何進(jìn)行交互,那么相信你看完這篇《Android和H5交互-基礎(chǔ)篇》吟逝,也行就許明白帽蝶。

demo

其實(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)的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末檩赢,一起剝皮案震驚了整個(gè)濱河市吕嘀,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌贞瞒,老刑警劉巖偶房,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異军浆,居然都是意外死亡蝴悉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門瘾敢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拍冠,“玉大人尿这,你說我怎么就攤上這事∏於牛” “怎么了射众?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長晃财。 經(jīng)常有香客問我叨橱,道長,這世上最難降的妖魔是什么断盛? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任罗洗,我火速辦了婚禮,結(jié)果婚禮上钢猛,老公的妹妹穿的比我還像新娘伙菜。我一直安慰自己,他們只是感情好命迈,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布贩绕。 她就那樣靜靜地躺著,像睡著了一般壶愤。 火紅的嫁衣襯著肌膚如雪淑倾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天征椒,我揣著相機(jī)與錄音娇哆,去河邊找鬼。 笑死勃救,一個(gè)胖子當(dāng)著我的面吹牛迂尝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播剪芥,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼琴许!你這毒婦竟也來了税肪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤榜田,失蹤者是張志新(化名)和其女友劉穎益兄,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體箭券,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡净捅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了辩块。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蛔六。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡荆永,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出国章,到底是詐尸還是另有隱情具钥,我是刑警寧澤,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布液兽,位于F島的核電站骂删,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏四啰。R本人自食惡果不足惜宁玫,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望柑晒。 院中可真熱鬧欧瘪,春花似錦、人聲如沸敦迄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽罚屋。三九已至苦囱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間脾猛,已是汗流浹背撕彤。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留猛拴,地道東北人羹铅。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像愉昆,于是被迫代替她去往敵國和親职员。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,116評(píng)論 25 707
  • 使用App.png 本文結(jié)構(gòu) 目前App的幾種常見的開發(fā)模式 關(guān)于React-Native的一點(diǎn)小看法 React...
    ZeroJ閱讀 5,205評(píng)論 0 22
  • 這個(gè)太陽 賊大了 我的天 天空的陽光打在身上十分的舒服 心情舒暢 上課到6點(diǎn) 非常的餓 要給小吃街的常德...
    張帆哈閱讀 369評(píng)論 2 5
  • 新建一個(gè)Car類: 1.在Car.h中進(jìn)行屬性和方法的創(chuàng)建: 2.涉及到的知識(shí)點(diǎn): 3.在Car.m中進(jìn)行初始化方...
    無厘小阿先_閱讀 421評(píng)論 0 1
  • 小孩兒買了一瓶冰紅茶跛溉, 很容易中獎(jiǎng)的那種焊切。 逆著燈光興奮地說“我馬上要中獎(jiǎng)了!” 看了一眼這個(gè)黑色的影子 我腦子突...
    人存閱讀 342評(píng)論 0 0