Cordova中JavaScript/TypeScript调用原生代码

Cordova官网上没有说怎么使用JS调用本地代码,但是告诉了我们如何写一个插件。
Cordova插件的根本原理就是JS和原生代码的交互。
所以抠出Cordova插件中调用本地代码的部分就可以了。



Ionic2创建插件简易教程:


适用于:
Cordova中JavaScript调用Android原生代码
Ionic中      TypeScript调用Android原生代码
IOS已经验证,同样原理,可以使用。

开发环境版本号:
cordova  6.5.0
Ionic        2.2.1
node       6.10.1
npm        3.10.10

一、写原生代码类
cordova项目或者Ionic项目编译后,会产生对应平台下的源码。

使用cordova/Ionic命令编译为android平台下的项目,使用 AndroidStudio 打开android源码(platforms\android目录下 ),创建一个类,类的要求:
1.    继承 CordovaPlugin类
2.    重写 execute( String  action,  JSONArray  args,  CallbackContext  callbackContext)方法:
          execute方法中 根据传来的action参数进行匹配,做对应的业务处理,有对应的action返回true,找不到对应的action返回false。
业务处理成功调用 callbackContext.success(此方法会调用JavaScript/TypeScript中 cordova.exec中传入的'成功回调方法' ),
业务处理失败调用 callbackContext.error (此方法会调用 JavaScript/TypeScript cordova.exec中传入的'失败回调方法' )

示例:
package com.test.hello.myplugin;

import android.content.Context;
import android.content.SharedPreferences;

import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaPlugin;
import org.json.JSONArray;
import org.json.JSONException;


public  class Test  extends CordovaPlugin {

     @Override
     public  boolean execute( String action,  JSONArray args,  CallbackContext callbackContext)  throws  JSONException {
         if (action.equals( "testAction")) {
             String message = args.getString( 0);
             this.myMethod(message, callbackContext);
             return  true;
        }
         return  false;
    }

     private  void myMethod( String message,  CallbackContext callbackContext) {
         if (message !=  null && message.length() >  0) {

           SharedPreferences sp = cordova.getActivity().getSharedPreferences( "config"Context.MODE_PRIVATE);
           String ss = sp.getString( "test", "SharedPreferences的数据");
            callbackContext.success(ss);
        }  else {
            callbackContext.error( "Expected one non-empty string argument.");
        }
    }
}


二、配置config.xml文件
config.xml文件在res/xml目录下

   <feature  name= "Test" >
     <param  name= "android-package"  value= "com.test.hello.myplugin.Test"  />
   </feature>

name= "Test"    为JS或TS代码中 cordova.exec传入的'服务参数'
value= "com.test.hello.myplugin.Test"    为第一步中创建的类的完整类名

三、调用原生代码

cordova.exec(< successFunction>, < failFunction>, < service>, < action >, [< args >]);
参数说明:
successFunction        成功回调        
failFunction                 失败回调             
service                         服务名字
action                           action
args                              要传给原生代码的所有参数(以数组形式传递)

在JavaScript中调用本地代码示例:
   
    cordova.exec( function(msg){
          alert( ' test 成功了' + msg)
        },  function(msg){
          alert( ' test 失败了')
        },  "Test"" testAction ", [ '传递的参数']);
    }
  

在TypeScript中直接调用cordova.exec 会报错, 需要声明cordova为any类型,在import语句后添加
declare  let cordova: any;

四、编译运行--------- 重点

此时如果使用cordova/Ionic命令运行项目的话,无法正常调用原生代码。
因为使用cordova/Ionic编译命令,会重新生成config.xml文件,之前的配置过的config.xml的feature 会丢失。
此时需要再次使用 AndroidStudio打开android源码再配置一遍 config.xml文件:
   <feature  name= "Test" >
     <param  name= "android-package"  value= "com.test.hello.myplugin.Test"  />
   </feature>
配置完后,使用 AndroidStudio编译运行app(不能使用cordova/Ionic命令编译运行)



五、 重点----为了解决 config.xml文件被覆盖问题,在第二步中不再编辑config.xml文件,
而是编辑\platforms\android目录下的android.json文件,
config_munge 节点下添加如下片段:
{
    "xml": "<feature name=\"Test\"><param name=\"android-package\" value=\"com.test.hello.myplugin.Test\" /></feature>",
    "count": 1
}
这样配置后,就不必使用AndroidStudio编译运行了,直接使用cordova/Ionic命令编译运行即可。
示例:
"config_munge": {
    "files": {
        "res/xml/config.xml": {
            "parents": {
                "/*": [
                    {
                        "xml": "<feature name=\"SplashScreen\"><param name=\"android-package\" value=\"org.apache.cordova.splashscreen.SplashScreen\" /><param name=\"onload\" value=\"true\" /></feature>",
                        "count": 1
                    },
                    {
                        "xml": "<feature name=\"StatusBar\"><param name=\"android-package\" value=\"org.apache.cordova.statusbar.StatusBar\" /><param name=\"onload\" value=\"true\" /></feature>",
                        "count": 1
                    },
                    {
                        "xml": "<feature name=\"Whitelist\"><param name=\"android-package\" value=\"org.apache.cordova.whitelist.WhitelistPlugin\" /><param name=\"onload\" value=\"true\" /></feature>",
                        "count": 1
                    },
                    {
                        "xml": "<feature name=\"Test\"><param name=\"android-package\" value=\"com.test.hello.myplugin.Test\" /></feature>",
                        "count": 1
                    }
                ]
            }
        }
    }
}






Ionic2简易创建插件教程:
<think>我们正在处理一个关于Ionic Angular应用中使用BLE(蓝牙低功耗)插件的问题。用户报告说,在Ionic应用中使用`startNotification()`函数接收到的数据与Android原生代码(`Peripheral.java`)中的`onCharacteristicChanged`函数打印出的数据不一致。 ### 问题分析 1. **BLE通信流程**: - 在BLE中,当设备(Peripheral)上的某个特征值(Characteristic)发生变化时,它会通知中心设备(Central,这里是手机)。 - 手机上的BLE库(如Android的BluetoothGatt)会收到通知,触发`onCharacteristicChanged`回调。 - 在Ionic应用中,我们通过`startNotification()`方法订阅这个特征值的通知,当收到通知时,会触发我们提供的回调函数。 2. **数据流路径**: - 设备(Peripheral)发送数据 -> Android系统BluetoothGatt回调`onCharacteristicChanged` -> Cordova BLE插件(Java层)处理并转发给JavaScript层 -> Ionic应用中的JavaScript回调。 3. **可能的问题点**: - **数据转换问题**:在Java层(Cordova插件)将数据转换为JavaScript可接受的格式(如Base64字符串)时可能出现问题。 - **数据传递问题**:数据在从Java层传递到JavaScript层的过程中可能被修改。 - **打印时机问题**:两处打印的时机可能不同(例如,Java层打印原始数据,而JavaScript层打印的是转换后的数据)。 - **编码问题**:数据在传输过程中可能涉及不同的编码方式(如UTF-8、Base64等),导致显示不一致。 ### 调试建议 #### 1. **检查Java层代码Cordova插件)** - 在Cordova插件的Java代码中,找到处理特征值变化的回调方法(通常是`onCharacteristicChanged`)。 - 在该方法中,打印接收到的原始字节数组(使用`Arrays.toString()`)以及转换后的Base64字符串(如果插件将数据转换为Base64字符串传递给JavaScript)。 - 比较这个打印与Ionic应用中的JavaScript回调接收到的数据(Base64字符串)是否一致。 例如,在Java中: ```java @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { byte[] value = characteristic.getValue(); Log.d(TAG, "Java onCharacteristicChanged: raw bytes = " + Arrays.toString(value)); String base64 = Base64.encodeToString(value, Base64.DEFAULT); Log.d(TAG, "Java onCharacteristicChanged: base64 = " + base64); // 然后通过回调将base64传递给JavaScript } ``` #### 2. **检查JavaScript代码** - 在Ionic应用中,确保正确订阅通知,并在回调中打印接收到的数据(Base64字符串)。 - 将JavaScript层收到的Base64字符串与Java层打印的Base64字符串进行比较。 例如,在Ionic中: ```typescript this.ble.startNotification(deviceId, serviceUUID, characteristicUUID).subscribe( (buffer: ArrayBuffer) => { // 通常插件会返回ArrayBuffer,但根据插件文档,也可能返回Base64字符串 // 这里我们假设返回的是ArrayBuffer,我们可以将其转换为Base64字符串以便比较 const base64 = this.arrayBufferToBase64(buffer); console.log('JS received base64:', base64); }, error => console.error('Notification error', error) ); // 将ArrayBuffer转换为Base64字符串 arrayBufferToBase64(buffer: ArrayBuffer): string { const bytes = new Uint8Array(buffer); let binary = ''; for (let i = 0; i < bytes.byteLength; i++) { binary += String.fromCharCode(bytes[i]); } return btoa(binary); } ``` #### 3. **比较数据** - 运行应用,触发特征值变化,分别查看Java层和JavaScript层的日志输出。 - 如果Java层打印的Base64字符串与JavaScript层打印的Base64字符串相同,则说明数据传递正确,问题可能出在后续处理(如JavaScript层对Base64字符串的解析)。 - 如果不同,则说明在Cordova插件内部数据传递过程中出现了问题。 #### 4. **其他可能原因** - **多个通知订阅**:确保没有重复订阅同一个特征值的通知,这可能导致数据被多次处理。 - **线程问题**:在Java层,确保在将数据传递给JavaScript层时没有在错误的线程上操作(Cordova插件通常需要在WebView线程执行回调)。 - **插件版本问题**:检查使用的BLE插件版本,查看是否有已知问题或更新。 ### 解决方案 如果发现数据在Java层和JavaScript层不一致,可以尝试以下步骤: 1. **更新插件**:确保使用最新版本的BLE插件。 2. **检查插件代码**:如果问题依然存在,可能需要检查或修改Cordova插件的Java代码,确保数据转换正确。例如,确保在将字节数组转换为Base64字符串时没有添加额外的换行符(使用`Base64.NO_WRAP`标志)。 3. **使用其他数据格式**:如果插件支持,可以尝试使用其他数据格式(如十六进制字符串)来传递数据,以便于调试。 ### 示例代码调整 在Java层(Cordova插件)中,修改Base64编码方式: ```java String base64 = Base64.encodeToString(value, Base64.NO_WRAP); // 避免换行符 ``` 在JavaScript层,确保正确解析Base64字符串(如果返回的是Base64字符串,则不需要转换;如果返回的是ArrayBuffer,则按上述方法转换)。 ### 结论 通过上述步骤,应该能够定位数据不一致的原因。通常,问题出现在数据转换或传递过程中。通过比较Java层和JavaScript层的日志,可以缩小问题范围。 §§相关问题§§ 1. 如何在Ionic应用中调试Cordova插件? 2. 如何确保BLE数据传输的准确性? 3. 在Ionic中使用BLE插件时,如何处理不同平台(iOS/Android)的差异?</think>### Ionic Angular BLE插件数据不一致问题分析 在Ionic Angular应用中使用`@awesome-cordova-plugins/ble/ngx`插件时,`startNotification()`接收的数据与Java层`Peripheral.java`中`onCharacteristicChanged()`打印的数据不一致,通常由以下原因导致: #### 1. **数据格式转换问题** - **BLE插件**默认将原始字节流转换为**Base64字符串**传递到JavaScript层 - **Java层**`onCharacteristicChanged()`直接操作原始字节数组 - 转换过程可能导致差异: ```typescript // Ionic中订阅通知 this.ble.startNotification(deviceId, serviceUUID, charUUID).subscribe( buffer => { // buffer是Base64字符串 const rawData = this.base64ToArrayBuffer(buffer); console.log('JS Received:', this.bytesToHex(rawData)); } ); ``` #### 2. **字节序处理差异** - Java层使用**大端序(Big-Endian)** 处理多字节数据 - JavaScript使用**小端序(Little-Endian)** ```java // Java层处理示例 byte[] value = characteristic.getValue(); int intValue = ByteBuffer.wrap(value).order(ByteOrder.BIG_ENDIAN).getInt(); ``` #### 3. **数据分片机制** - BLE协议限制单次传输**20字节** - 长数据会被自动分片传输: ```java // Peripheral.java中的分片处理 @Override public void onCharacteristicChanged(...) { // 可能只打印当前分片而非完整数据 } ``` #### 4. **调试验证步骤 1. **统一日志格式**: ```java // Java层添加HEX打印 Log.d("BLE", "Java Data: " + bytesToHex(characteristic.getValue())); ``` ```typescript // Ionic层添加相同HEX转换 private bytesToHex(buffer: ArrayBuffer): string { return Array.from(new Uint8Array(buffer)) .map(b => b.toString(16).padStart(2, '0')) .join(':'); } ``` 2. **检查MTU设置**: ```typescript // Ionic中请求更大MTU this.ble.requestMtu(deviceId, 512).then(() => {...}); ``` 3. **验证插件版本兼容性**: ```bash # 检查插件版本 npm list @awesome-cordova-plugins/ble cordova plugin list | grep cordova-plugin-ble-central ``` #### 5. **典型解决方案 1. **强制使用ArrayBuffer**: ```typescript // 修改node_modules中的插件代码 // 找到lib/index.js 搜索_base64ToArrayBuffer // 替换 return base64ToArrayBuffer(buffer); ``` 2. **自定义Native层桥接**: ```java // 在Peripheral.java中直接暴露原始数据 public void sendRawDataToJS(byte[] data) { PluginResult result = new PluginResult(PluginResult.Status.OK, data); result.setKeepCallback(true); callbackContext.sendPluginResult(result); } ``` 3. **使用Ionic Native包装器**: ```typescript // 创建自定义Observable包装 startRawNotification(deviceId: string, ...): Observable<ArrayBuffer> { return new Observable(observer => { (window as any).ble.startNotification(..., (data: ArrayBuffer) => observer.next(data), error => observer.error(error) ); }); } ``` > **关键验证点**:比较Java层`bytesToHex()`输出与Ionic层`bytesToHex()`输出,若HEX字符串完全一致,则问题出在数据解析逻辑;若不一致,则是数据传输层问题[^5]。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值