宜信投米RA | 安卓跨平臺開發實踐

移動端目前市面上主流的平臺主要有Android和IOS兩大平臺,爲了儘量複用代碼和節省開發成本,各大巨頭都開發了本身的跨平臺框架,好比Facebook的React-Native,阿里的Weex,Cordova,以及今年Google開發者大會上介紹的Flutter框架。這些框架各有優缺點,可是到目前爲止都沒有大規模的推廣開來,在我看來主要有一下幾個緣由:javascript

一、開發者生態圈還不夠成熟,RN是三大跨平臺框架關注人最多,生態最活躍的框架, 可是到目前爲止也沒有到1.0版本(最新的release是0.57.8),更別說後來者的Weex和Flutter了。生態不成熟,意味着開發文檔少,可使用的開源控件少,好比在RN上想作一個最基本的下拉刷新和上拉加載更多的listview都比較費勁。Weex已經貢獻給Apache,已經好久沒有更新release了。Flutter如今還在beta版本,其發展還有待觀察。html

二、性能問題,雖然這幾大框架都對渲染性能作了優化,可是相比原生仍是差一些,RN和weex都本身實現來了一個瀏覽器內核(JSCore),所以多了一層js解析,渲染會慢一些。好比RN的listview若是數據量太大就會出現卡頓。Flutter雖然自帶繪製引擎,可是跟原生比起來仍是有一些距離。vue

三、兼容問題,雖然這三大平臺的初衷都是爲了跨平臺(Write/Learn once, run everywhere),可是實際應用中仍是須要耗費不少的精力去兼容和適配,好比RN在Android低端機器上表現就不盡如人意,連曾經RN的堅實擁護者Airbnb都宣佈放棄使用RN了。java

四、開發集成成本,三個框架都須要學習新的語言React,vue,dart,weex的最大優點就是入門簡單,可是版本迭代慢,RN上手門檻高,開發調試難度大,集成RN和weex框架還會加入不少so文件,增長安裝包的大小至少在10M左右,這還不包括第三方的library。Flutter由於剛出來,應用的還很少,其效果還有待觀察。可是移動開發的跨平臺是大勢所趨,能夠節省開發成本,提升開發效率,迅速響應業務變化,如今主流的應用仍是使用H5和原生的通訊來實現跨平臺的開發。android

Android和IOS平臺都有本身內置的瀏覽器內核webkit框架,跨平臺的本質就是用H5/JS編寫的代碼可以分別運行在Android和IOS的WebView中,從而實現了一套代碼兩個平臺都能運行的目的。git

圖片描述
在Android平臺上要實現Native和JS的通訊主要經過WebViewClient和WebChromeClient兩個類來實現。WebViewClient的做用是幫助WebView處理各類通知,事件請求,其主要的方法有:onLoadResource,onPageStart,onPageFinished,onReceiveError,shouldOverrideUrlLoading等方法;github

WebChromeClient處理JS頁面的事件響應,好比網頁中的對話框,網頁圖標,網站標題,網頁的加載進度等事件,對應的響應方法有onJsAlert, onJsConfirm, onJsConsole, onProgressChanged, onReceiveIcon, onReceiveTitle等。web

要實現Java和JS通訊就要:shell

1,解決Java調JS;2,JS調Java。瀏覽器

Java調用JS經過loadUrl和evaluateJavaScript兩個方法。經過webview.loadUrl(「javascript:alert(‘hello world’)」),能夠在android平臺將js代碼注入到html頁面,loadUrl方法能夠直接調用js中定義的函數,也能夠把android本地的assets目錄下的js文件以字符串的形式注入到html頁面中,可是這個注入時機必定要等到html頁面加載完畢才能作,即在WebViewClient.onPageFinished的回調函數中調用,這樣就至關於在html頁面中直接引用了js資源文件。

對於客戶端來講,java調用js本質上是拼接js字符串的過程,可是調用loadUrl不能直接獲取js函數的返回值。要實現Java調用js函數後,能夠獲取js函數的返回值可使用webview.evaluateJavaScript方法,可是該方法只有在android4.4及以上的版本纔可使用。其餘用法和loadUrl一致。

JS調用Java能夠分爲三種:1,對象映射;2,URL攔截;3,JS方法攔截。

對象映射是經過webview.addJavascriptInterface(new JSObject(), 「javaObject」),這樣能夠js代碼中能夠直接調用javaObject對象,從而實現JS調用Java的功能,可是這個方法在android4.2如下會有安全漏洞,利用反射機制調用Android API getRuntime執行shell命令進行攻擊,好比遍歷sdcard,發送短信,安裝木馬APK等。

URL攔截是指在html頁面經過iframe.src,window.open,documention.location或者href,這四種方法均可以在html頁面中打開一個鏈接,從而會觸發Java中的WebViewClient.shouldOverrideUrlLoading方法。例如在js中執行
圖片描述
在shouldOverrrideUrlLoading中能夠根據約定的協議格式(Scheme)和協議名(Authority)獲取從JS中傳輸過來的數據(Data)。

在JS中調用alert,console,prompt,confirm等方法就會觸發WebChromeClient的onJsAlert,onConsoleMessage,onJsPrompt,onJsConfirm方法的回調。好比在js中能夠調用
圖片描述
在onJsPrompt的message中能夠獲取prompt的內容,而後根據約定的協議格式能夠獲取數據。

爲了解決JS和Native的通訊問題,須要使用一個JSBridge框架(https://github.com/lzyzsd/JsB...)用來負責H5和Java之間的通訊,須要解決的問題:

一、JS互相Java調用後如何回調,將responseData傳遞回去;

2,JS調用Java有三種方法,若是選擇哪種方法比較合適。

針對問題1,能夠在java端和js端定義一個數據結構: Message={callbackId:xxx, handleName:xxx,responseData:xxx,responseId:xxx}。將回調函數保存在callbackId中,當JS或者Java處理完數據回調的時候再講保存在callbackId的回調函數存放在responseId,相應的回調的數據存放在responseData中,這樣就能響應JS或者Java調用後的回調消息。

Js調用Java的方法雖然有三種,可是addJavaScriptInterface存在安全性問題通常不建議使用,JS中的alert,console方法都會在Html頁面比較經常使用,confirm和prompt雖然不經常使用可是某些手機系統版本上會有對話框彈出,不通用,因此比較好的選擇是url攔截,能夠經過iframe.src觸發shouldOverrideUrlLoading。

JsBridge框架的使用主要分爲:

1,在H5頁面加載完畢注入一個本地的js文件;

2,Java代碼中註冊BridgeHandler,用來處理JS發送過來的消息;

3,在本地注入的js文件中定義_handleMessageFromNative,用來接收java傳遞過來的消息;

4,由於客戶端注入js是異步的,因此須要在js文件中註冊Event監聽器,成功後通知H5。

Native調用JS,例如經過webview.loadUrl("javascript:WebViewJavascriptBridge._handleMessageFromNative('{"callbackId":"JAVA_CB_2_559","data":"just data from java"}')");

這樣就能夠調用JS的handleMessageFromNative方法,傳遞的數據格式是Message,callBackId響應js的回調,發送前會存儲到HashMap中,js回調的時候根據JAVA_CB_2_559找到對應的的回調函數處理js的響應數據,具體流程以下:

圖片描述
Js調用Java,經過sendMessageQueue將傳遞的信息轉換成Message= {data: {…}, callbackId: "cb_1_1234"}其中callbackId是js的回調函數。

而後經過iframe.src=’yy://return/_fetchQueue/[{"data":"xxxx","callbackId":"cb_1_4321"}]’,觸發shouldOverrideUrlLoading方法,java處理完js傳遞過來的data後,將回調函數cb_1_4321設置到Message={responseId: cb_1_4321, responseData:XXX},這樣在js中就能處理回調函數。

具體的流程圖以下:
圖片描述

JsBridge框架提供兩種Handler方法,registerHandler方法須要傳入handler的名字,這樣須要IOS,Android,H5三方約定這個名字,由於H5開發的的業務需求變化比較快,並且H5調用原生的方法也是隨機的,因此每次都用registerHandler約定名字須要使用全局變量存放這些handler方法名不便於擴展,因此實際開發中使用defaultHanlder,H5調用原生方法的的時候約定一個cmd,即約定data={cmd:xxx,time:data:{…}},只要在原生代碼中對cmd命令有對應的功能,那麼H5頁面就能夠隨時調用原生的方法。

JSBridge的改進建議,因爲webview調用js方法的時候必須在主線程才能生效,因此偶然會出現java調用js失敗。

另外,Js調用Java偶爾也會失敗,由於iframe機制不能保證每次都能觸發shouldOverrideUrlLoading回調。

目前JSBridge採用的是url scheme的方式,若是不考慮Android4.2如下,IOS7如下,能夠採用的交互,好比直接使用addJavaScriptInterface在JS頁面注入一個Native對象,將以前觸發u步驟變爲使用這個Native對象向Native發送消息。這種方法只是一個可行的方案,實際使用過程當中目前的JSBridge方案基本上知足業務需求了。

做者:周智         宜信技術學院

相關文章
相關標籤/搜索