在Cordova官网中有这么一张架构图:大家看右下角蓝色的矩形框"Custom Plugin"——自定义插件。意思就是如果您用Cordova打包Mobile应用时,发现您的移动应用里需要使用一些功能,这些功能用普通的JavaScript无法实现,而是需要调用移动平台的一些原生API才能实现时,我们就需要自己实现自定义插件。这些插件通过在特定的移动平台上采用原生开发实现,比如Android Studio中的Java开发,然后再通过JavaScript wrapper的方式暴露给您的Mobile应用。比如您是用Cordova在Android平台上打包生成APK文件,那么您的Mobile代码(JavaScript)里还是不会直接调用您用Java实现的Custom Plugin,而是调用Custom Plugin对应的JavaScript wrapper。

那么JavaScript wrapper本身是JavaScript代码,它是怎么调用到Custom Plugin的Java实现的?本文就会介绍这个细节。

下图是OData离线存储插件(OData Offline Store)的JavaScript实现代码的一部分。下图第232行会调用设备的native API进行离线存储的打开操作:

exec(win, error, 'OData', 'openOfflineStore', [this, options ? options : {}]);

这个exec函数从哪里来?由Cordova框架实现,通过语句 require(‘cordova/exec’)返回。

那么当应用执行到JavaScript代码:exec(win, error, 'OData', 'openOfflineStore', [this, options ? options : {}]); 的时候,程序流是如何从这个JavaScript的exec函数进入到Android平台的原生API执行呢?

打开PackagedApp文件夹里的android子文件夹,有一个JavaScript文件:cordova.js:

里面能看到函数exec的定义和实现:

进而去查看androidExec函数的实现细节:

第938行:var msgs = nativeApiProvider.get().exec(bridgeSecret, service, action, callbackId, argsJson);

第943行的五个参数含义:

success, fail, service, action, args

  • success & fail: JavaScript回调函数,当移动平台上的Java原生API执行完毕后,这个JavaScript回调函数会被调用到。
  • service: 待执行的Java Native API的Java实现类名称。
  • action: 待执行的Java Native API的Java实现类的方法名称。
  • args: JavaScript传递给Java native API的参数数组。

2. 在安卓平台上,JavaScript调用Java的技术实现方式有两种:定义在下图JavaScript代码中的jsToNativeModes对象中:PROMPT和JS_OBJECT。相对应的,Java调用JavaScript有三种模式:POLLING, LOAD_URL和ONLINE_EVENT:

看下面这段Java代码,暴露了一个方法getSomeString给JavaScript端消费:


import android.app.Activity; import android.os.Bundle; import android.webkit.WebView; public class WebViewGUI extends Activity { WebView mWebView; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mWebView = new WebView(this); mWebView.getSettings().setJavaScriptEnabled(true); mWebView.addJavascriptInterface(new JavaScriptInterface(),
"jsinterface"); mWebView.loadUrl("file:///android_asset/www/index.html"); setContentView(mWebView); } final class JavaScriptInterface { JavaScriptInterface() {
} public String getSomeString() { return "string"; } } }

在JavaScript代码里消费上述Java代码暴露的getSomeString方法:

<script>

var String = window.jsinterface.getSomeString();

</script>

我们再回过头来看看AndroidExec的实现:

var msgs = nativeApiProvider.get().exec(bridgeSecret, service, action, callbackId, argsJson);

在AndroidExec的实现里, nativeApiProvider的get方法返回一个实例,然后执行exec方法。而881行代码说明nativeApiProvider的实现位于文件夹cordova/android下面的nativeapiprovider.js里:

打开nativeapiprovider.js,在第21行的注释里我们得到了重要信息: currentApi要么来自Java文件ExposedJsApi.java,要么来自PromptBasedNativeApi.java。

Java文件ExposedJsApi.java可以在这个文件夹内找到:

platform/android/CordovaLib/src/org/apache/cordova

ExposedJsApi实际就是个Java interface,上面声明了一个exec方法:

JavaScript到Java的执行通过prompt调用完成:

Java类SystemExposedJsApi实现了这个interface,再将执行流转交给类CordovaBridge的实例.

CordovaBridge再调用PluginManager:

PluginManager首先根据名字找到负责处理该请求的Java plugin的实现类,再调用该实现类的方法:

以OData离线存储的实现类为例,我们在其实现代码里能发现有大量的IF-ELSE分支,每个分支处理不同的离线存储操作请求。

要获取更多Jerry的原创技术文章,请关注公众号"汪子熙"或者扫描下面二维码:

Cordova插件中JavaScript代码与Java的交互细节介绍的更多相关文章

  1. Cordova应用的JavaScript代码和自定义插件代码的调试

    我之前写过三篇Cordova相关的技术文章.当我们使用Cordova将自己开发的前端应用打包安装到手机上后,可能会遇到需要调试Cordova应用的时候. 本文就介绍Cordova应用的调试步骤. 如果 ...

  2. silverlight 中javascript 代码与托管代码的互调用 以及一些思考

    silverlight 客户端javascript 代码与托管代码的互调用时比较用意义的同时,因为silverlight本身就是一个插件,如果两者之间不能进行相互的调用,对于web 上的一些特殊的功能 ...

  3. Grunt 使用(二)uglify插件压缩javascript代码

    本文在配置grunt基本环境的基础下,讲解如何使用grunt-contrib-uglify进行javascript压缩 本文只介绍了grunt-contrib-uglify插件的一种压缩方式适用于大部 ...

  4. Webview 中 Javascript 无法调用 Java 对象

    [问题产生] Webview 通过 addjavascriptInterface 传递对象给前端,一切正常.但是 Android官方已提醒此功能是有安全风险,改用 safe-java-js-webvi ...

  5. jni中c代码调用java代码

    原理是使用反射的机制 java中反射的例子: Class<?> forName = Class.forName("com.example.ndkcallback.DataProv ...

  6. ionic使用cordova插件中的Screenshot截图分享功能

    需要实现操作,考试完成后需要将成绩生成一张图片,分享出去, import { Screenshot } from '@ionic-native/screenshot'; constructor(pri ...

  7. JavaScript翻译成Java

    这两天公司有一个需求,将一段加密的JavaScript代码转换为JAVA版. JavaScript中的某一段代码: 前期查看了整个JavaScript代码,发现代码中,方法里面嵌套方法,各种不合规的变 ...

  8. 使用Bootstrap的popover标签中嵌入插件,并且为插件注册事件实现Ajax与后台交互

    下午研究了一下bootstrap的popover写了个例子.如果项目很多地方都需要用到可以考虑封装成插件. javascript代码: <script type="text/javas ...

  9. 由闭包引起的对javascript代码可维护性的思考

    在最近的编程实践中由闭包的使用引起了我对javascript代码可维护性的思考.面向对象的其中一个特性封装性通过封装可以降低类与类之间或模块与模块之间耦合性从而使我们的设计更加高内聚低耦合,在大规模的 ...

随机推荐

  1. Flask_restful 插件实战笔记——基本概念与使用

       最近在Resetful接口设计上想法还是挺多的,也实现了一些需求!想着整理下Flask_restful插件的基本知识,方便日后的复习!   官方地址:https://flask-restful. ...

  2. Flutter实战视频-移动电商-06.Dio基础_Get请求和动态组件协作

    博客地址: https://jspang.com/post/FlutterShop.html#toc-0ee 编写页面代码 创建动态组件HomePage,原来的代码是静态的我们这里就去掉就可以了. 然 ...

  3. Flutter实战视频-移动电商-45.详细页_说明区域UI编写

    45.详细页_说明区域UI编写 pages/details_page/details_expain.dart 详情页面引用组件 效果展示: 最终代码: import 'package:flutter/ ...

  4. python 并发编程之IO 模型

    首先说一下 IO 发生时涉及的对象和步骤.以read 为例,会经历两个阶段: 1)等待数据准备 2)将数据从内核拷贝到进程中 二,阻塞Io(blocking IO) 在 Linux中  默认情况下所有 ...

  5. CCF 201512-4 送货 (并查集+DFS,欧拉路)

    问题描述 为了增加公司收入,F公司新开设了物流业务.由于F公司在业界的良好口碑,物流业务一开通即受到了消费者的欢迎,物流业务马上遍及了城市的每条街道.然而,F公司现在只安排了小明一个人负责所有街道的服 ...

  6. E20190226-hm

    shallow  adj. 浅的,肤浅的; 表面的,皮毛的; (水,器物等) 浅的; (呼吸) 浅的;  n. 浅处; 浅滩;

  7. Codeforces 174B【模拟构造】

    题意: 给你一个串只有小写字母和点,让你构造前缀是1-8,后缀是1-3长度的文件名: 思路: 那么以"."作为分割点,把字符串都拿出来,然后 首段长度<=8 OK; 中间&l ...

  8. 51nod 1416【DFS】

    思路: 暴力整个图,以这个为起点,然后看一下有没有找到一条路是会指向自己且元素个数>=4: #include <bits/stdc++.h> using namespace std; ...

  9. c# IOCP.ClientEx2.ReadWrite 加断点遭遇System.AccessViolationException 问题

    起因: 如果在Debug模式下,在IOCP.ClientEx2.ReadWrite.cs while (0 > (nPackSize = _ipcp.Pack(arg_n64PackId, ar ...

  10. cf786C(xjb)

    题目链接:http://codeforces.com/problemset/problem/768/C 题意:给出一个数组,经过k次操作后最大元素和最小元素分别是什么.. 操作:给当前数组排序,再将第 ...