網站首頁 編程語言 正文
前言
從原理到應用復盤一下自己做過的所有項目,希望能讓我自己過兩年仍然能看懂現在寫的代碼哈哈。在項目里我只負責了Android的開發包括插件開發和集成,ios沒摸到,有機會也得自己玩下,但是這篇文章不會接觸。
技術選型
現在Hybrid混合開發框架很多,Apche Cordova/React Native/Flutter等等等等。然而公司需求是2018年6月提的,Flutter 1.0在半年之后才出生,所以當時團隊TL比較了RN與Cordova后基于以下幾點選擇了Cordova。
- 學習成本低,項目周期比較緊,只有1個月就要發版本,團隊內沒有熟悉RN的成員,學習成本低開發時間短非常重要
- 技術成熟,文檔多不容易踩坑(當然現在RN也很成熟,但是當時RN的人確實沒有很多,生態不成熟)
我們的應用跑在一臺定制的Android終端,性能不是很好,在最后某些js動畫場景里,以及一些js-native的調用里卡的不行,最后優化了下,效果也算還不錯。這里建議如果對性能要求很高的項目,一定慎重考慮Cordova這種webview方案
項目最后選擇了React + Mirrorx + Cordova的方案(當時有Dva, 但是由于Dva沒有英文文檔,而我們的項目是需要美國分團隊共同維護的,所以選擇了Mirrorx)
技術原理
架構圖
本質上其實就是往app里面塞一個webview,通過file協議, 本地加載index.html
那么這里會有三個問題
- 如何本地加載url對應的資源
- webview如何使用js調用app原生api(JSBridge)
- app原生api如何回調webview中的js
- 多個plugin的情況下,cordova是如何通過cordova.exec(...’pluginName’...)定位到相應的plugin的 以下帶著問題一個個進行解釋
1. 如何本地加載url對應的資源
我們項目中通過eject暴露Webapck配置, 配置打包路徑直接打進www文件夾。 通過Android的webview.loadUrl方法通過file協議load本地index.html。在Google inject中也可以看到對應的url. 對應的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調用app原生api
2.1 通過addJavascriptInterface 及 @JavascriptInterface實現
2.2 通過WebClientChrome中的三個方法onJsAlert, onJsConfirm, onJsPrompt實現
當chromium webkit內核的webview調用window.alert, window.confirm, window.prompt方法時,對應的WebClientChrome的那三個方法同樣會被執行
在cordova中會首先判斷有沒有設置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即設備支持@JavascriptInterface注解, 則走addJavascriptInterface,否則走onJsPrompt
3. app原生api如何回調webview中的js
js端生成callback function id,傳給native, native需要回調js時,回調對應的callbackId給js
private String callbackId; // 在js端生成,保存在native端
private CordovaWebView webView;
protected boolean finished; //這個callback是否結束,如果結束在js端(cordova.js)就會把這個callbackid從列表中刪掉,否則這個callback將一直存在,也就是說明你可以用這個callbackContext一直和js保持通信
回調方式:
/** 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’...)定位到相應的plugin的
當Cordova框架啟動時候,CordovaActivity類中的onCreate方法調用loadUrl方法。在第一次loadUrl方法時,就會去初始化PluginManager并加載plugin,PluginManager加載plugin,將plugin的Class名字保存到一個hashmap中,用service名字作為key值。當JS端通過JavascriptInterface接口的SystemExposedJsApi對象請求Android時,PluginManager會從hashmap中查找到plugin,如果該plugin還未實例化,利用java反射機制實例化該plugin,并執行plugin的execute方法。
關于踩到的坑
1. 打包路徑配置問題
cordova build的時候會默認使用項目www目錄作為資源目錄,打包進assets中。 項目工期緊,直接用了cra創建的項目,我們使用eject,再修改打包路徑(實際當時有其他插件可以暴露打包路徑配置)。
// webpack.config.prod.js
module.exports = {
...
output: {
...
// The build folder.
path: resolveApp('www'),
}
}
2. success不回調問題
場景: 我們項目集成其他項目組的自研plugin的功能后,用戶輸入device ID和divice Name后,點擊connect按鈕,就可以通過mobile連接上其他項目組的硬件設備
前端調用test.connect(deviceID, deviceName, callback), 其中callback的邏輯是接收并判斷java端傳回的message。在連接上device后, java使用contextCallback.success(”success”)來進行回調 ,callback接收到message為success, 就更改mobile端我們前端的相應狀態為連接成功
第一次連接之后,即java調用了contextCallback.success(“success”), 然后js中調用了callback(‘success’) ,項目頁面顯示已連接, 斷開后,第二次再次調用contextCallback.success(“success”)無法正常連接,無法正常調用js這邊的callback
原因:
finished一開始沒有初始化,走下面的else,被賦值為true, 然后調用webView.sendResult,第二次調用就直接return了。
解決方案:keepCallback (當時官方文檔沒有提到callback只能調一次的問題,翻源碼才看到)
原文鏈接:https://juejin.cn/post/7163108849967169543
相關推薦
- 2022-10-04 混合棧跳轉導致Flutter頁面事件卡死問題解決_IOS
- 2022-03-12 使用VMware虛擬機安裝Linux(CentOS7鏡像)_Linux
- 2023-01-10 C#實現無損壓縮圖片的示例詳解_C#教程
- 2022-08-18 C#開發Windows?UWP系列之對話框MessageDialog和ContentDialog_C
- 2022-08-10 Golang泛型的使用方法詳解_Golang
- 2022-06-18 Android?ProgressBar實現進度條效果_Android
- 2022-11-20 RC4加密關鍵變量及算法特點原理詳解_腳本加解密
- 2022-01-05 出現escript: exception error: undefined function rab
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支