JavaScript in Android

Android 中动态加载 JavaScript

通过JavaScript解析蓝牙指令及蓝牙返回数据

Raw指令存储格式

将ByteArray转换成HexString,根据指令类型及指令名称生成JSONObject。 服务器以JSON格式保存蓝牙指令。

1
2
3
4
5
{
	"cmdType": {
		"cmdName": "HexString(蓝牙指令)"
	}
}

ByteArray 与 HexString互相转换

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
// Convert a hex string to a byte array
function hexToBytes(hex) {
    for (var bytes = [], c = 0; c < hex.length; c += 2)
        bytes.push(parseInt(hex.substr(c, 2), 16));
    return bytes;
}

// Convert a byte array to a hex string
function bytesToHex(bytes) {
    for (var hex = [], i = 0; i < bytes.length; i++) {
        var current = bytes[i] < 0 ? bytes[i] + 256 : bytes[i];
        hex.push((current >>> 4).toString(16));
        hex.push((current & 0xF).toString(16));
    }
    //设置分隔符号,转换成大写
    return hex.join("").toUpperCase();
}

JavaScript生成蓝牙指令

输入

参数类型是否必需备注
cmdTypeString设备类型、指令类型
cmdNameString指令名称
parametersJSONObject指令参数,按需动态生成
1
2
3
4
5
6
7
{
    cmdType:"WoBlindTilt",
    cmdName:"move",
    parameters:{
        percent:100
    } 
}

解析

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
const cmdJson = '';
const cmdObj = JSON.parse(cmdJson);

var CmdGenerator = (function() {
  
    function getCmd(request) {
        var requestJson = JSON.parse(request);
        var cmdType = requestJson.cmdType;
        var cmdName = requestJson.cmdName;
        var typeObj = cmdObj[cmdType];
        if(typeObj == undefined) {
            return "no such cmdType: " + cmdType;
        }
        var rawCmd = typeObj[cmdName];
        if(rawCmd == undefined) {
            return "no such cmdName: " + cmdName + " with cmdType: " + cmdType;
        }
        var parameters = requestJson.parameters;
        if(parameters != undefined) {
            return getByCmdType(cmdType,cmdName,parameters);
        } else {
            return bytesToHex(hexToBytes(rawCmd));
        }
    }

    function getByCmdType(cmdType,cmdName,parameters) {
        var typeObj = cmdObj[cmdType];
        switch(typeObj) {
            case cmdObj.WoBlindTilt:
                return getByBlindTiltCmdName(cmdName,parameters);
            default :
                return "no such cmdType: " + cmdType + " with parameters";
        }
    }

    function getByBlindTiltCmdName(cmdName,parameters) {
        var rawCmd = cmdObj.WoBlindTilt[cmdName];
        switch(rawCmd) {
            case cmdObj.WoBlindTilt.move:
                return getBlindTiltMoveCmd(rawCmd,parameters);
            case cmdObj.WoBlindTilt.setDirectionCmd:
                return getBlindTiltDirectionCmd(rawCmd,parameters);
            default:
                return "no such cmdName: " + cmdName +  " with cmdType: WoBlindTilt" + " with parameters";
        }
    }

    function getBlindTiltMoveCmd(rawCmd,parameters) {
        return rawCmd + hex8(parameters.percent);
    }

    function getBlindTiltDirectionCmd(rawCmd,parameters) {
        var data = parameters.direction | 0x02;
        return rawCmd + hex8(data);
    }

    function hexToBytes(hex) {
        for (var bytes = [], c = 0; c < hex.length; c += 2)
            bytes.push(parseInt(hex.substr(c, 2), 16));
        return bytes;
    }

    function bytesToHex(bytes) {
        for (var hex = [], i = 0; i < bytes.length; i++) {
            var current = bytes[i] < 0 ? bytes[i] + 256 : bytes[i];
            hex.push((current >>> 4).toString(16));
            hex.push((current & 0xF).toString(16));
        }
        return hex.join("").toUpperCase();
    }

    function hex8(val) {
        val &= 0xFF;
        var hex = val.toString(16).toUpperCase();
        return ("00" + hex).slice(-2);
    }

    return {
        getCmd: getCmd
    };

})();

输出

原始指令(无需组装、拼接参数)

  • HexString

组合指令

  • 在JavaScript中组合后转换为HexString

JavaScript解析蓝牙返回数据

获取设备返回的结果后,通过JavaScript解析数据

输入

参数类型是否必需备注
cmdTypeString设备类型、指令类型
cmdNameString指令名称
inputString设备返回的结果
1
2
3
4
5
6
7
8
9
{

    cmdType:"WoBlindTilt",

    cmdName:"move",

    input:"0100000000"

}

解析

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
var Parser = (function () {

    function parseToJSON(request) {
        var requestJson = JSON.parse(request);
        var cmdType = requestJson.cmdType;
        var cmdName = requestJson.cmdName;
        var typeObj = cmdObj[cmdType];
        if (typeObj == undefined) {
            return "no such cmdType: " + cmdType;
        }
        var rawCmd = typeObj[cmdName];
        if (rawCmd == undefined) {
            return "no such cmdName: " + cmdName + " with cmdType: " + cmdType;
        }
        var input = requestJson.input;
        if (input != undefined) {
            return parseByCmdType(cmdType, cmdName, input);
        } else {
            return "input undefined!";
        }
    }

    function parseByCmdType(cmdType, cmdName, input) {
        var typeObj = cmdObj[cmdType];
        switch (typeObj) {
            case cmdObj.WoBlindTilt:
                return parseByBlindTiltCmdName(cmdName, input);
            default:
                return "no such cmdType: " + cmdType + " with input";
        }
    }

    function parseByBlindTiltCmdName(cmdName, input) {
        var rawCmd = cmdObj.WoBlindTilt[cmdName];
        switch (rawCmd) {
            case cmdObj.WoBlindTilt.move:
                return parseBlindTiltMoveCmd(input);
            case cmdObj.WoBlindTilt.setDirectionCmd:
                return parseBlindTiltDirectionCmd(input);
            default:
                return "no such cmdName: " + cmdName + " with cmdType: WoBlindTilt" + " with input";
        }
    }

    function parseBlindTiltMoveCmd(input) {
        var bytes = hexToBytes(input);
        var execCode = 0xff & bytes[0];
        var percent = 0xff & bytes[1];
        var output = {};
        output.currentPosition = percent;
        var json = buildJSON(execCode);
        json.output = output;
        return JSON.stringify(json);
    }

    function parseBlindTiltDirectionCmd(input) {
        var bytes = hexToBytes(input);
        var execCode = 0xff & bytes[0];
        var json = buildJSON(execCode);
        return JSON.stringify(json);
    }

    function buildJSON(execCode) {
        var json = {};
        json.execCode = execCode;
        return json;
    }

    function hexToBytes(hex) {
        for (var bytes = [], c = 0; c < hex.length; c += 2)
            bytes.push(parseInt(hex.substr(c, 2), 16));
        return bytes;
    }

    return {
        parseToJSON: parseToJSON
    };
})();

输出

参数类型是否必需备注
execCodeint指令执行状态
outputJSONObject返回数据,按需动态生成

executeCode

0x011OK执行完毕
0x022ERROR执行出错
0x033BUSY设备忙,请稍后重试
0x044通信版本号不兼容
0x055设备不支持此命令
0x066设备电量低
0x077设备已经加密(密钥已存在)
0x088设备未加密
0x099密码错误
0x0A10设备不支持这种加密方式
0x0B11找不到多跳设备
0x0C12连接网络失败
0x0D13当前模式下不支持此命令
0x0E14与需要保持连接的设备连接断开(从机离线返回0x0E)
0x0F15设置周期获取信息时Notify返回的命令执行状态(0x570E01)

示例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{

    execCode:1,

    output:{

        currentPosition:0

    }

}

整体流程

uml

Android常用解析引擎

Rhino

  • 不支持不全面,出现异常 Int8Array not defined;
  • 未找到实现通过 JS 函数调用JS 函数的方法;
  • 性能较低
  • 包体积小

React Native

  • 支持更全面
  • 目前不需要单独集成
  • Android 调用 JS 方法待研究

J2V8

  • 支持较新的标准
  • 单独引入会显著增大app打包体积(15MB左右,包含armeabi、arm64)÷
comments powered by Disqus