Apache Cordova Android原理应用实例详解

目录
  • 前言
  • 技术选型
  • 技术原理
    • 1. 如何本地加载url对应的资源
    • 2. webview如何使用js调用app原生api
    • 3. app原生api如何回调webview中的js
    • 4. 多个plugin的情况
  • 关于踩到的坑
    • 1. 打包路径配置问题
    • 2. success不回调问题

前言

从原理到应用复盘一下自己做过的所有项目,希望能让我自己过两年仍然能看懂现在写的代码哈哈。在项目里我只负责了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只能调一次的问题,翻源码才看到)

以上就是Apache Cordova Android原理应用实例详解的详细内容,更多关于Apache Cordova Android的资料请关注我们其它相关文章!

(0)

相关推荐

  • Apache Pulsar 微信大流量实时推荐场景下实践详解

    目录 导语 作者简介 实践 1:大流量场景下的 K8s 部署实践 实践 2:非持久化 Topic 的应用 实践 3:负载均衡与 Broker 缓存优化 实践 4:COS Offloader 开发与应用 未来展望与计划 导语 本文整理自 8 月 Apache Pulsar Meetup 上,刘燊题为<Apache Pulsar 在微信的大流量实时推荐场景实践>的分享.本文介绍了微信团队在大流量场景下将 Pulsar 部署在 K8s 上的实践与优化.非持久化 Topic 的应用.负载均衡与 Bro

  • Apache Doris Join 优化原理详解

    目录 背景 & 目标 Doris 数据划分 Partition Bucket Join 方式 总览 Broadcast / Shuffle Join Bucket Shuffle Join Plan Rule Colocate Join Runtime Filter 优化 Join Reorder 优化 Join 调优建议 背景 & 目标 掌握 Apache Doris Join 优化手段及其实现原理 为代码阅读提供理论基础 Doris 数据划分 不同的 Join 方式非常依赖于对 Dor

  • Apache Pulsar集群搭建部署详细过程

    目录 一.集群组成说明 二.安装前置条件 三.ZooKeeper集群搭建 四.BookKeeper集群搭建 五.Broker集群搭建 六.docker安装pulsar-dashboard 一.集群组成说明 1.搭建Pulsar集群至少需要3个组件:ZooKeeper集群.BookKeeper集群和Broker集群(Broker是Pulsar的自身实例).这三个集群组件如下:ZooKeeper集群(3个ZooKeeper节点组成)Bookie集群(也称为BookKeeper集群,3个BookKee

  • Apache SkyWalking 修复TTL timer 失效bug详解

    目录 正文 SkyWalking OAP 角色 SkyWalking OAP 集群 Data TTL timer 配置 DataTTLKeeperTimer 定时任务 Bug 产生的原因 解决 Bug 正文 近期,Apache SkyWalking 修复了一个隐藏了近4年的Bug - TTL timer 可能失效问题,这个 bug 在 SkyWalking <=9.2.0 版本中存在. 关于这个 bug 的详细信息可以看邮件列表 lists.apache.org/thread/ztp4… 具体如

  • Apache Pulsar结合Hudi构建Lakehouse方案分析

    目录 1. 动机 2. 分析 3. 当前方案 4. 新的Lakehouse存储方案 4.1 新的存储布局 4.2 支持高效Upserts 4.3 将Hudi表当做Pulsar Topic 4.4 可扩展的元数据管理 5. 引用 1. 动机 Lakehouse最早由Databricks公司提出,其可作为低成本.直接访问云存储并提供传统DBMS管系统性能和ACID事务.版本.审计.索引.缓存.查询优化的数据管理系统,Lakehouse结合数据湖和数据仓库的优点:包括数据湖的低成本存储和开放数据格式访

  • Apache SkyWalking 监控 MySQL Server 实战解析

    目录 正文 监控 MySQL Server 安装过程 docker compose 部署 正文 Apache SkyWalking 在本月初发布了 SkyWalking Backend.UI 的 9.2.0 版本 以及 SkyWalking Java Agent 8.12.0 版本,其中就有笔者贡献的 hutool-http client 5.x 插件,以后大家通过 hutool 工具发送的 http 请求也可以出现在分布式链路中了. 另外,社区宣布计划在 2022 年 11 月 30 日结束所

  • Apache Doris Colocate Join 原理实践教程

    目录 What Colocate Join Why Colocate Join How Colocate Join 核心思路 术语定义 1 数据导入时保证本地性 2 Colocate Join Query Plan 3 Colocate Join Query Schedule 4 Colocate Join At Bucket Seq Level 5 Colocate Join Metadata Maintenance 6 How to decide a query can colocate j

  • Apache Cordova Android原理应用实例详解

    目录 前言 技术选型 技术原理 1. 如何本地加载url对应的资源 2. webview如何使用js调用app原生api 3. app原生api如何回调webview中的js 4. 多个plugin的情况 关于踩到的坑 1. 打包路径配置问题 2. success不回调问题 前言 从原理到应用复盘一下自己做过的所有项目,希望能让我自己过两年仍然能看懂现在写的代码哈哈.在项目里我只负责了Android的开发包括插件开发和集成,ios没摸到,有机会也得自己玩下,但是这篇文章不会接触. 技术选型 现在

  • Redis 实现队列原理的实例详解

    Redis 实现队列原理的实例详解 场景说明: ·用于处理比较耗时的请求,例如批量发送邮件,如果直接在网页触发执行发送,程序会出现超时 ·高并发场景,当某个时刻请求瞬间增加时,可以把请求写入到队列,后台在去处理这些请求 ·抢购场景,先入先出的模式 命令: rpush + blpop 或 lpush + brpop rpush : 往列表右侧推入数据 blpop : 客户端阻塞直到队列有值输出 简单队列: simple.php $stmt = $pdo->prepare('select id, c

  • Android xml解析实例详解

    Android  xml解析实例详解 实现效果图: XmlActivity package com.Android.xiong.gridlayoutTest; import android.app.Activity; import android.content.res.XmlResourceParser; import android.graphics.Color; import android.graphics.Typeface; import android.os.Bundle; impo

  • Android getevent用法实例详解

     Android getevent用法实例详解 最近在测试设备按键的常用命令,感觉这些命令都有的,但就是不知道怎么查找. 翻阅了几篇博文,才发现有一个getevent,就是指这样的命令. 首先需要说明的是getevent命令后面可以带上具体的input设备,列如getevent /dev/iput/event0,这样可以过滤掉一些不用显示的input的设备. 我在之前的使用中,还是有些找不到点子,也是一步一步使用起来的. 首先看-p 选项, -p选项用于输出input设备相关的一些信息,列如,

  • Android ProgressDialog的实例详解

    Android ProgressDialog的实例详解 当自己做一些耗时操作时,希望给用户一些提示信息,告诉用户正在进行耗时操作,这时就可以用到ProgressDialog. 1.新建一个全局变量ProgressDialog private ProgressDialog mDialog; 2.设置ProgressDialog的相关参数: progressDialog = new ProgressDialog( ProgressDialogActivity.this); //设置进度条风格,风格为

  • Android Dialog 动画实例详解

    Android Dialog 动画实例详解 动画描述: 动画与底部菜单一样出现和消失 制作过程: 1. 创建两个动画文件 window_in.xml: <?xml version="1.0" encoding="utf-8"?> <set xmlns:android="http://schemas.android.com/apk/res/android" > <translate android:duration=&

  • Spring IOC和aop的原理及实例详解

    这篇文章主要介绍了Spring IOC和aop的原理及实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架.特点是面向接口编程,松耦合. 1:IOC(控制反转) 别名(DI:依赖注入) 首先来一段ioc的实现原来代码: public class ClassPathXmlApplicationContext implements BeanFactory { privat

  • Python 异步协程函数原理及实例详解

    这篇文章主要介绍了Python 异步协程函数原理及实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一. asyncio 1.python3.4开始引入标准库之中,内置对异步io的支持 2.asyncio本身是一个消息循环 3.步骤: (1)创建消息循环 (2)把协程导入 (3)关闭 4.举例: import threading # 引入异步io包 import asyncio # 使用协程 @ asyncio.coroutine def

  • Java Linkedlist原理及实例详解

    这篇文章主要介绍了Java Linkedlist原理及实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 定义:linkedlist属于链表结构,方便添加和删除元素,但查询不方便,适用于对收尾的操作. 具有具体的对象,使用对象调用具体的方法 add // 添加元素 //在中间添加元素 arr.add("H"); addFirst:在集合最前面添加元素 // 在链表头部添加元素 arr.addFirst("F")

  • Java8默认方法Default Methods原理及实例详解

    这篇文章主要介绍了Java8默认方法Default Methods原理及实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Java 8 引入了新的语言特性--默认方法(Default Methods). Default methods enable new functionality to be added to the interfaces of libraries and ensure binary compatibility wit

随机推荐