Apache?Cordova?Android原理應(yīng)用實例詳解
前言
從原理到應(yīng)用復(fù)盤一下自己做過的所有項目,希望能讓我自己過兩年仍然能看懂現(xiàn)在寫的代碼哈哈。在項目里我只負(fù)責(zé)了Android的開發(fā)包括插件開發(fā)和集成,ios沒摸到,有機(jī)會也得自己玩下,但是這篇文章不會接觸。
技術(shù)選型
現(xiàn)在Hybrid混合開發(fā)框架很多,Apche Cordova/React Native/Flutter等等等等。然而公司需求是2018年6月提的,F(xiàn)lutter 1.0在半年之后才出生,所以當(dāng)時團(tuán)隊TL比較了RN與Cordova后基于以下幾點選擇了Cordova。
- 學(xué)習(xí)成本低,項目周期比較緊,只有1個月就要發(fā)版本,團(tuán)隊內(nèi)沒有熟悉RN的成員,學(xué)習(xí)成本低開發(fā)時間短非常重要
- 技術(shù)成熟,文檔多不容易踩坑(當(dāng)然現(xiàn)在RN也很成熟,但是當(dāng)時RN的人確實沒有很多,生態(tài)不成熟)
我們的應(yīng)用跑在一臺定制的Android終端,性能不是很好,在最后某些js動畫場景里,以及一些js-native的調(diào)用里卡的不行,最后優(yōu)化了下,效果也算還不錯。這里建議如果對性能要求很高的項目,一定慎重考慮Cordova這種webview方案
項目最后選擇了React + Mirrorx + Cordova的方案(當(dāng)時有Dva, 但是由于Dva沒有英文文檔,而我們的項目是需要美國分團(tuán)隊共同維護(hù)的,所以選擇了Mirrorx)
技術(shù)原理
架構(gòu)圖
本質(zhì)上其實就是往app里面塞一個webview,通過file協(xié)議, 本地加載index.html
那么這里會有三個問題
- 如何本地加載url對應(yīng)的資源
- webview如何使用js調(diào)用app原生api(JSBridge)
- app原生api如何回調(diào)webview中的js
- 多個plugin的情況下,cordova是如何通過cordova.exec(...’pluginName’...)定位到相應(yīng)的plugin的 以下帶著問題一個個進(jìn)行解釋
1. 如何本地加載url對應(yīng)的資源
我們項目中通過eject暴露Webapck配置, 配置打包路徑直接打進(jìn)www文件夾。 通過Android的webview.loadUrl方法通過file協(xié)議load本地index.html。在Google inject中也可以看到對應(yīng)的url. 對應(yīng)的cordova代碼如下
public class CordovaViewTestActivity extends Activity implements CordovaInterface { CordovaWebView cwv; /* Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); cwv = (CordovaWebView) findViewById(R.id.tutorialView); cwv.loadUrl("file:///android_asset/www/index.html"); }
2. webview如何使用js調(diào)用app原生api
2.1 通過addJavascriptInterface 及 @JavascriptInterface實現(xiàn)
2.2 通過WebClientChrome中的三個方法onJsAlert, onJsConfirm, onJsPrompt實現(xiàn)
當(dāng)chromium webkit內(nèi)核的webview調(diào)用window.alert, window.confirm, window.prompt方法時,對應(yīng)的WebClientChrome的那三個方法同樣會被執(zhí)行
在cordova中會首先判斷有沒有設(shè)置navtiveApi
var nativeApi = this._cordovaNative || require('cordova/android/promptbasednativeapi');
在Android部分
var msgs = nativeApiProvider.get().exec(bridgeSecret, service, action, callbackId, argsJson); // If argsJson was received by Java as null, try again with the PROMPT bridge mode. // This happens in rare circumstances, such as when certain Unicode characters are passed over the bridge on a Galaxy S2. See CB-2666. if (jsToNativeBridgeMode == jsToNativeModes.JS_OBJECT && msgs === "@Null arguments.") { androidExec.setJsToNativeBridgeMode(jsToNativeModes.PROMPT); androidExec(success, fail, service, action, args); androidExec.setJsToNativeBridgeMode(jsToNativeModes.JS_OBJECT); } else if (msgs) { messagesFromNative.push(msgs); // Always process async to avoid exceptions messing up stack. nextTick(processMessages); }
如果有nativeApi即設(shè)備支持@JavascriptInterface注解, 則走addJavascriptInterface,否則走onJsPrompt
3. app原生api如何回調(diào)webview中的js
js端生成callback function id,傳給native, native需要回調(diào)js時,回調(diào)對應(yīng)的callbackId給js
private String callbackId; // 在js端生成,保存在native端 private CordovaWebView webView; protected boolean finished; //這個callback是否結(jié)束,如果結(jié)束在js端(cordova.js)就會把這個callbackid從列表中刪掉,否則這個callback將一直存在,也就是說明你可以用這個callbackContext一直和js保持通信
回調(diào)方式:
/** Uses webView.loadUrl("javascript:") to execute messages. */ public static class LoadUrlBridgeMode extends BridgeMode {
/** Uses webView.evaluateJavascript to execute messages. */ public static class EvalBridgeMode extends BridgeMode {
public static class OnlineEventsBridgeMode extends BridgeMode
4. 多個plugin的情況
多個plugin的情況下,cordova是如何通過cordova.exec(...’pluginName’...)定位到相應(yīng)的plugin的
當(dāng)Cordova框架啟動時候,CordovaActivity類中的onCreate方法調(diào)用loadUrl方法。在第一次loadUrl方法時,就會去初始化PluginManager并加載plugin,PluginManager加載plugin,將plugin的Class名字保存到一個hashmap中,用service名字作為key值。當(dāng)JS端通過JavascriptInterface接口的SystemExposedJsApi對象請求Android時,PluginManager會從hashmap中查找到plugin,如果該plugin還未實例化,利用java反射機(jī)制實例化該plugin,并執(zhí)行plugin的execute方法。
關(guān)于踩到的坑
1. 打包路徑配置問題
cordova build的時候會默認(rèn)使用項目www目錄作為資源目錄,打包進(jìn)assets中。 項目工期緊,直接用了cra創(chuàng)建的項目,我們使用eject,再修改打包路徑(實際當(dāng)時有其他插件可以暴露打包路徑配置)。
// webpack.config.prod.js module.exports = { ... output: { ... // The build folder. path: resolveApp('www'), } }
2. success不回調(diào)問題
場景: 我們項目集成其他項目組的自研plugin的功能后,用戶輸入device ID和divice Name后,點擊connect按鈕,就可以通過mobile連接上其他項目組的硬件設(shè)備
前端調(diào)用test.connect(deviceID, deviceName, callback), 其中callback的邏輯是接收并判斷java端傳回的message。在連接上device后, java使用contextCallback.success(”success”)來進(jìn)行回調(diào) ,callback接收到message為success, 就更改mobile端我們前端的相應(yīng)狀態(tài)為連接成功
第一次連接之后,即java調(diào)用了contextCallback.success(“success”), 然后js中調(diào)用了callback(‘success’) ,項目頁面顯示已連接, 斷開后,第二次再次調(diào)用contextCallback.success(“success”)無法正常連接,無法正常調(diào)用js這邊的callback
原因:
finished一開始沒有初始化,走下面的else,被賦值為true, 然后調(diào)用webView.sendResult,第二次調(diào)用就直接return了。
解決方案:keepCallback (當(dāng)時官方文檔沒有提到callback只能調(diào)一次的問題,翻源碼才看到)
以上就是Apache Cordova Android原理應(yīng)用實例詳解的詳細(xì)內(nèi)容,更多關(guān)于Apache Cordova Android的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android單點觸控實現(xiàn)圖片平移、縮放、旋轉(zhuǎn)功能
這篇文章主要介紹了Android單點觸控實現(xiàn)圖片平移、縮放、旋轉(zhuǎn)功能的相關(guān)資料,需要的朋友可以參考下2016-02-02Android自定義AvatarImageView實現(xiàn)頭像顯示效果
這篇文章主要為大家詳細(xì)介紹了Android自定義AvatarImageView實現(xiàn)頭像顯示效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-09-09Android應(yīng)用退出登錄的實現(xiàn)方法
每一個app都會有一個”退出登陸”的功能,當(dāng)點擊退出之后需要將所有的Activity都finish掉,開始是想將棧中的所有Activity清除掉,但是沒有找到方法,后來用廣播實現(xiàn)了。下面小編給大家分享android應(yīng)用退出登錄的實現(xiàn)方法,需要的朋友參考下2017-04-04Android Intent實現(xiàn)頁面跳轉(zhuǎn)的方法示例
本篇文章主要介紹了Android Intent實現(xiàn)頁面跳轉(zhuǎn)的方法示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-03-03創(chuàng)建子線程對Android進(jìn)行網(wǎng)絡(luò)訪問
這篇文章介紹了Android中創(chuàng)建子線程進(jìn)行網(wǎng)絡(luò)訪問的方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考2021-11-11android自定義開關(guān)控件-SlideSwitch的實例
本篇文章主要介紹了android自定義開關(guān)控件-SlideSwitch的實例,實現(xiàn)了手機(jī)控件開關(guān)的功能,感興趣的小伙伴們可以參考一下。2016-11-11android中選中菜單的顯示跳轉(zhuǎn)和隱式跳轉(zhuǎn)的實例介紹
android中選中菜單的顯示跳轉(zhuǎn)和隱式跳轉(zhuǎn)的實例介紹,需要的朋友可以參考一下2013-05-05