一. JNI介绍

JNI(Java Native Interface),通过使用 Java本地接口,实现Java(或kotlin)代码和其他语言(c/c++)的代码的交互,交互是JNI的精髓,意味着java和c++之间可以很方便地进行相互访问变量,调用对方的函数,如java可以调用c++的函数,c++也能调用java的函数。
介绍JNI中几个比较重要的变量:

一。JavaVM:虚拟机的代表,一个进程创建一个。可以从JavaVM中获取JNIEnv指针,kotlin层访问C++层的时候会自动创建,C++访问kotlin时不会自动创建,因此可以考虑在JNI_OnLoad函数中将自动创建的JavaVM存为全局的。
二。JNIEnv:指代Java本地接口环境,定义了JNI函数接口,线程持有,只在当前线程有效,子线程可以通过AttachCurrentThread函数将当前线程被附加到JVM上,并返回一个属于当前线程的JNIEnv指针(后面讲例子)。
三。jobject:kotlin类的实例引用,JNI函数执行完后会释放,因此如果想保存,需要new出来:env->NewGlobalRef(object),释放:env->DeleteGlobalRef(object)。
四。jclass:kotlin类的类引用,可以通过object获取:(jclass)env->NewGlobalRef(env->GetObjectClass(object)),需要释放:env->DeleteGlobalRef(clazz)。
(JNI函数第二个参数有两种:jobject和jclass,kotlin中声明为静态的JNI函数参数是jclass,非静态是jobject)
五。jmethodID, jfieldID :指向内部 Runtime 数据结构的指针,用于缓存的静态对象:第一次查找会做一次字符串比较,但后面再次调用就能直接读取而变得很快;不需要手动释放的,当然也不能作为 JNI 全局引用。

二. kotlin调C++

JNI原理简单说就是动态库加载和函数的动态注册(相较于静态注册,效率高,接口更清晰)。下面以超声波模块为例说明。
c++层代码编译成动态库(libultrasonic_native.so),kotlin层通过调用System.loadLibrary()方法加载此动态库,此时虚拟机就会调用JNI库中的 JNI_OnLoad()方法进行动态注册,即实现kotlin层和c++层的函数映射, 映射后,kotlin层调_openUltrasonic方法,就会调到c++的openUltrasonic方法。

kotlin层:

 System.loadLibrary("ultrasonic_native") //kotlin层加载动态库
 
//kotlin层,JNI函数声明
external fun openUltrasonic(devices: Array<String>): Boolean
external fun closeUltrasonic()

C++层:

JNINativeMethod methodMapping[] =    //kotlin层和c++层的函数映射,
{
		{"_openUltrasonic",  "([Ljava/lang/String;)Z",          (void *) UltrasonicNative::openUltrasonic},
		{"_closeUltrasonic", "()V",                             (void *) UltrasonicNative::closeUltrasonic},
};  //参数依次是:kotlin函数名,参数,c++函数名

int regist(JNIEnv *env) //动态注册
{
		jclass clazz = env->FindClass("com/pudutech/mirsdk/hardware/ultrasonic/Ultrasonic"); //找到kotlin层的类
		CHECK_RTN_VAL(!clazz, JNI_ERR)
		CHECK_RTN_VAL(env->RegisterNatives(clazz, methodMapping, ARRAY_SIZE(methodMapping)) < 0, JNI_ERR) //注册

		env->DeleteLocalRef(clazz);
		return JNI_OK;
}

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
        JNIEnv* env;
        if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
            return JNI_ERR; // JNI version not supported.
        }
				vmGlobal = vm;
        regist(env); //动态注册

        return  JNI_VERSION_1_6;
}

三. C++调kotlin

C++可以访问或修改kotlin类的成员变量,或者调用kotlin类的成员函数。

  1. C++获取kotlin类的成员变量的值
    首先获取kotlin类的成员变量mNativeContext的参数ID(可以认为是指向该成员变量的指针),调用GetLongField获取该成员变量的值,类型不同,方法名不同。如int类型调GetIntField。需要注意:获取参数ID是获取类引用的参数ID,因此使用类引用jclass,获取值是获取类实例的值,因此要用类实例的引用jobject。
static pudu::Ultrasonic *getNativeUltrasonic(JNIEnv *env, jobject object) {
	jclass clazz = env->GetObjectClass(object); //获取kotlin的类
	jfieldID ultrasonicContext = env->GetFieldID(clazz, "mNativeContext", "J"); //获取kotlin类的成员变量的参数ID
	pudu::Ultrasonic *ultrasonic = reinterpret_cast<pudu::Ultrasonic *>(env->GetLongField(object, ultrasonicContext)); //获取long类型的成员变量的值
	return ultrasonic;
}		
  1. C++修改kotlin类的成员变量的值
    第一步同样是获取kotlin类的成员变量mNativeContext的参数ID,然后调用SetLongField方法修改该成员变量的值。
static void setNativeUltrasonic(JNIEnv *env, jobject object, pudu::Ultrasonic *ultrasonic) {
	jclass clazz = env->GetObjectClass(object);
	jfieldID ultrasonicContext = env->GetFieldID(clazz, "mNativeContext", "J");
	env->SetLongField(object, ultrasonicContext, reinterpret_cast<jlong>(ultrasonic));
}
  1. C++调用kotlin类成员函数
    方法和获取成员变量类似,首先调用GetMethodID获取kotlin类成员函数postEventFromNative的方法ID,然后调用CallVoidMethod执行该函数。
jmethodID nativeFunction = env->GetMethodID(clazz, "postEventFromNative", "(Ljava/lang/String;)V"); //获取kotlin类成员函数postEventFromNative的方法ID
CHECK_DO_RTN(!nativeFunction, LOGW(TAG) << "get method postEventFromNative failed")

env->CallVoidMethod(obj, nativeFunction, env->NewStringUTF(msg.c_str())); //运行kotlin函数, 第二个参数是方法ID,后面是该函数的参数

比较常规的一种用法是:主线程在C++层打开设备,然后开一个新线程来循环获取数据,获取到数据后通过调用kotlin类成员函数将获取到的数据抛给上层处理。方法如下:

第一步,给传感器设备类Ultrasonic设置回调函数,这里需要用到我们之前new出来并保存的kotlin类实例jobject和临时获取的属于当前子线程的env:

static void setUploadDataClb(pudu::Ultrasonic *ultrasonic) {
	nativeClb uploadDataclb = [&](void *object, string msg) //回调函数
	{
		CHECK_DO_RTN(!object, LOGW(TAG) << "object is NULL")
		JNIEnv *env;
		bool isAttach = false;
		
		auto ret = vmGlobal->GetEnv((void **) &env, JNI_VERSION_1_6);
		if (ret == JNI_EDETACHED) {
			ret = vmGlobal->AttachCurrentThread(&env, NULL); //将当前线程被附加到JVM上,并获取一个属于当前线程的JNIEnv指针
			isAttach = true;
		}
		CHECK_DO_RTN(ret != JNI_OK, LOGW(TAG) << "get env failed:")

		jobject obj = reinterpret_cast<jobject>(object);
		static jclass clazz = (jclass)env->NewGlobalRef(env->GetObjectClass(obj)); //静态局部变量,只初始化一次,不用反复申请释放内存,提升效率

		static jmethodID nativeFunction = env->GetMethodID(clazz, "postEventFromNative", "(Ljava/lang/String;)V"); //获取kotlin函数的方法ID
		CHECK_DO_RTN(!nativeFunction, LOGW(TAG) << "get method postEventFromNative failed")

		env->CallVoidMethod(obj, nativeFunction, env->NewStringUTF(msg.c_str())); //调用kotlin方法
		CHECK_DO (isAttach, vmGlobal->DetachCurrentThread())
	};
	ultrasonic->setUploadDataClb(uploadDataclb); //超声波实例set回调函数
}

第二部,打开设备后开辟子线程循环获取数据,线程函数reading获取数据:

unique_ptr<thread> readThread = make_unique<thread>(bind(&Ultrasonic::reading, this));
readThread->detach();

第三步,获取到数据后调用回调函数:

if(uploadDataClb) {
    uploadDataClb(upperObject, oneDataJson.dump()); //调回调函数,执行kotlin函数,发送超声波信息给上层
}

四. JNI相关的软件设计

面向对象的单一职责和开放封闭原则告诉我们要做到高内聚低耦合,对扩展开发,对修改封闭,这对于时常变动,经常需要适配的传感器模块显得尤为重要。以RGBD为例,当前有四款RGBD,我们要实现的是四款RGBD的代码模块的高内聚(内紧),每一个模块都是设备层,传输层,解析层紧密相关,高效合作的整体,也要实现四个模块间的低耦合(外松),模块间几乎没有依赖,不会牵一发动全身。

重构后的RGBD做了两个解耦,一是不同产品类型的RGBD的解耦,如realsense和奥比各自有自己的模块,相互没有任何依赖;二是同一产品类型的多个RGBD间的解耦,如一个机器人上有两个奥比RGBD,这两个RGBD各自有自己的实例,如下图所示,两个RGBD是两个相互独立的整体,各自实例化了kotlin层的实例和C++层的实例, 两个实例如同一个实例,成员变量互通,成员函数互相访问。两个RGBD各自持有自己的变量,各自独立地获取数据,然后调自己的kotlin实例的方法上抛数据。相较于重构前一个类实例管理多个RGBD,结构更清晰,依赖更少,扩展性得到很大提升。
在这里插入图片描述

代码

native_interface.cpp:

#include "Ultrasonic.h"
#include "g3log/g3log.hpp"

#include <stdlib.h>
#include <stdint.h>
#include <string>
#include <jni.h>

namespace {
	JavaVM* vmGlobal = nullptr;
    const char *TAG = "Ultrasonic";
	const char *ULTRASONIC_CTX = "mNativeContext";

	class UltrasonicNative {
	public:
		static jboolean openUltrasonic(JNIEnv *env, jobject object, jobjectArray devices) {
			vector<string> devicesVector = jstringArray2Vector(env, devices);
            pudu::Ultrasonic *ultrasonic = new pudu::Ultrasonic();
            setNativeUltrasonic(env, object, ultrasonic);

			ultrasonic->setUpperObject(env->NewGlobalRef(object));
			setUploadDataClb(ultrasonic);
			return ultrasonic->openUltrasonic(devicesVector);
		}

		static void closeUltrasonic(JNIEnv *env, jobject object) {
            pudu::Ultrasonic *ultrasonic = getNativeUltrasonic(env, object);
            CHECK_RTN(!ultrasonic)

			ultrasonic->closeUltrasonic();
		}

		static pudu::Ultrasonic *getNativeUltrasonic(JNIEnv *env, jobject object) {
			jclass clazz = env->GetObjectClass(object);
			jfieldID ultrasonicContext = env->GetFieldID(clazz, ULTRASONIC_CTX, "J");
			pudu::Ultrasonic *ultrasonic = reinterpret_cast<pudu::Ultrasonic *>(env->GetLongField(object, ultrasonicContext));
			return ultrasonic;
		}

		static void setNativeUltrasonic(JNIEnv *env, jobject object, pudu::Ultrasonic *ultrasonic) {
			jclass clazz = env->GetObjectClass(object);
			jfieldID ultrasonicContext = env->GetFieldID(clazz, ULTRASONIC_CTX, "J");
			env->SetLongField(object, ultrasonicContext, reinterpret_cast<jlong>(ultrasonic));
		}

		static void setUploadDataClb(pudu::Ultrasonic *ultrasonic) {
			nativeClb uploadDataclb = [&](void *object, string msg)
			{
				CHECK_DO_RTN(!object, LOGW(TAG) << "object is NULL")
				JNIEnv *env;

				bool isAttach = false;
				auto ret = vmGlobal->GetEnv((void **) &env, JNI_VERSION_1_6);
				if (ret == JNI_EDETACHED) {
					ret = vmGlobal->AttachCurrentThread(&env, NULL);
					isAttach = true;
				}
				CHECK_DO_RTN(ret != JNI_OK, LOGW(TAG) << "get env failed:")
				jobject obj = reinterpret_cast<jobject>(object);
				static jclass clazz = (jclass)env->NewGlobalRef(env->GetObjectClass(obj));

				static jmethodID nativeFunction = env->GetMethodID(clazz, "postEventFromNative", "(Ljava/lang/String;)V");
				CHECK_DO_RTN(!nativeFunction, LOGW(TAG) << "get method postEventFromNative failed")

				env->CallVoidMethod(obj, nativeFunction, env->NewStringUTF(msg.c_str()));
				CHECK_DO (isAttach, vmGlobal->DetachCurrentThread())
			};
			ultrasonic->setUploadDataClb(uploadDataclb);
		}

		static string jstring2string(JNIEnv *env, jstring s) {
			const char *jstr = env->GetStringUTFChars(s, NULL);
			int length = env->GetStringLength(s);
			string str = string((char *) jstr, length);
			env->ReleaseStringUTFChars(s, jstr);
			return str;
		}

		static vector<string> jstringArray2Vector(JNIEnv *env, jobjectArray devices) {
			vector<string> devicesVector;

			jsize stringArrayLength = env->GetArrayLength(devices);
			for (int i = 0; i < stringArrayLength; i++) {
				jobject jobj = env->GetObjectArrayElement(devices, i);
				jstring jstr = static_cast<jstring>(jobj);
				string str = jstring2string(env, jstr);
				devicesVector.push_back(str);
			}
			return devicesVector;
		}

		static char* jbyteArray2String(JNIEnv *env, jbyteArray byteArray, int &arrayLength)
		{
			jbyte *bytes = env->GetByteArrayElements(byteArray, 0);
			arrayLength = env->GetArrayLength(byteArray);
            LOGW(TAG) << "byteArray len:" << arrayLength;

			char* array = new char[arrayLength + 1];
			memset(array, 0, arrayLength + 1);
			memcpy(array, bytes, arrayLength);
			array[arrayLength] = '\0';

			env->ReleaseByteArrayElements(byteArray, bytes, 0);
			return array;
		}
    };


	JNINativeMethod methodMapping[] = {
			{"openUltrasonic",  "([Ljava/lang/String;)Z",          (void *) UltrasonicNative::openUltrasonic},
			{"closeUltrasonic", "()V",                             (void *) UltrasonicNative::closeUltrasonic},
	};

	int registerPuduNativeClass(JNIEnv *env) {
		jclass clazz = env->FindClass("com/pudutech/mirsdk/hardware/ultrasonic/Ultrasonic");
		CHECK_RTN_VAL(!clazz, JNI_ERR)
		CHECK_RTN_VAL(env->RegisterNatives(clazz, methodMapping, ARRAY_SIZE(methodMapping)) < 0, JNI_ERR)

		env->DeleteLocalRef(clazz);
		return JNI_OK;
	}

	void unregisterPuduNativeClass(JNIEnv *env) {
		jclass clazz = env->FindClass("com/pudutech/mirsdk/hardware/ultrasonic/Ultrasonic");
		CHECK_RTN(!clazz)
		CHECK_RTN(env->UnregisterNatives(clazz) < 0)

		env->DeleteLocalRef(clazz);
	}
}

#ifdef __cplusplus
extern "C" {
#endif

    JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
    {
        JNIEnv* env;
        if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
            return JNI_ERR; // JNI version not supported.
        }
		vmGlobal = vm;
        registerPuduNativeClass(env);

        return  JNI_VERSION_1_6;
    }

    JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) {
        JNIEnv *env;
        CHECK_RTN(vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) // JNI version not supported.

        unregisterPuduNativeClass(env);
    }

#ifdef __cplusplus
}
#endif

Ultrasonic.h:

#pragma once
#include <memory>
#include <vector>
#include <string>
#include <chrono>
#include <thread>
#include <mutex>

#define CHECK_RTN(condition) if(condition){return;}
#define CHECK_DO_RTN(condition, needDo) if(condition){needDo; return;}
#define CHECK_RTN_VAL(condition, val) if(condition){return val;}
#define CHECK_DO_RTN_VAL(condition, needDo, val) if(condition){needDo; return val;}
#define CHECK_DO(condition, needDo) if(condition){needDo;}

using namespace std;
typedef std::function<void(void* object, string msg)> nativeClb;

template <typename T, size_t N>
char(&ArraySizeHelper(T(&array)[N]))[N];
#define ARRAY_SIZE(array) (sizeof(ArraySizeHelper(array)))

namespace pudu{
    class Ultrasonic{
        public:
            Ultrasonic();
            ~Ultrasonic();
            bool openUltrasonic(vector<string> devicesVector);
            void closeUltrasonic();
            void setUploadDataClb(const nativeClb &clb);
            void setUpperObject(void *object);

        private:
            int getValidId(const string &device);
            int setSerialPort();

            bool sendToUltrasonic(unsigned char *buffer, size_t dataLen);
            bool receiveFromUltrasonic(unsigned char *buffer, size_t dataLen);
            void reading();
            int parseData(unsigned char high, unsigned char low);

        private:
            bool isRunning;
            int fd;
            int baudRate;
            nativeClb uploadDataClb;
            void* upperObject;
            string data;
    };
}

Ultrasonic.cpp

#include "Ultrasonic.h"
#include "g3log/g3log.hpp"
#include "base/json.h"

#include <errno.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <string>
#include <dirent.h>
#include <linux/usbdevice_fs.h>
#include <chrono>
#include <fstream>
#include <cmath>
#include <time.h>
#include <chrono>

namespace pudu {
    using nlohmann::json;
    const char *TAG = "Ultrasonic";
    const string TTY = "/sys/class/tty/";
    const int READ_LEN = 3;
    const int WRITE_LEN = 4;
    const int ULTRASONIC_SIZE = 8;
    const int FLAG_BIT = 165; //0xa5
    unsigned char command[][3] = {{0xd8, 0x02, 0xb0}, {0xd2, 0x02, 0xb0}, {0xd6, 0x02, 0xb0}, {0xdc, 0x02, 0xb0}, {0xd0, 0x02, 0xb0}, {0xd4, 0x02, 0xb0}, {0xde, 0x02, 0xb0}, {0xda, 0x02, 0xb0}};

    Ultrasonic::Ultrasonic() : isRunning(false), fd(-1), baudRate(B115200), uploadDataClb(nullptr), upperObject(nullptr)
    {

    }

    bool Ultrasonic::openUltrasonic(vector<string> devicesVector) {
        CHECK_RTN_VAL(isRunning, true)
        isRunning = true;
        CHECK_DO (fd != -1,close(fd); fd = -1)

        int id = -1;
        for (auto device: devicesVector) {
            id = getValidId(device);
            CHECK_DO (id != -1, break)
        }
        CHECK_DO_RTN_VAL(id == -1, LOGWF(TAG, "getValid id error"), false)

        string ttyDev = "/dev/ttyUSB" + to_string(id);
        string cmd = "su -c \"chmod 777 ";
        cmd = cmd + ttyDev + "\"";
        system(cmd.c_str());

        LOGDF(TAG, "start open Ultrasonic: %s", ttyDev.c_str());
        fd = open(ttyDev.c_str(), O_RDWR | O_NOCTTY | O_SYNC); //no block: O_NDELAY
        CHECK_DO_RTN_VAL(fd < 0, LOGWF(TAG, "open Ultrasonic[%s] failed: %s", ttyDev.c_str(), strerror(errno)), false)

        int rlt = setSerialPort();
        CHECK_DO_RTN_VAL(rlt, close(fd); fd = -1, false)

        unique_ptr<thread> readThread = make_unique<thread>(bind(&Ultrasonic::reading, this));
        readThread->detach();

        return true;
    }

    void Ultrasonic::closeUltrasonic() {
        isRunning = false;
        close(fd);
        fd = -1;
    }

    void Ultrasonic::reading()
    {
        int index = 0, data = 0;
        bool rlt = false;
        unsigned char buffer[WRITE_LEN] = {0};

        while (isRunning) {
            CHECK_DO(index >= ULTRASONIC_SIZE, index = 0)

            rlt = sendToUltrasonic(command[index], READ_LEN);
            CHECK_DO(!rlt, continue;)

            rlt = receiveFromUltrasonic(buffer, WRITE_LEN);
            LOGDF(TAG, "ultrasonic data: %d,%d,%d", (int)buffer[0], (int)buffer[1], (int)buffer[2]);
            CHECK_DO(!rlt || (int)buffer[0] != FLAG_BIT, continue;)

            data = parseData(buffer[1], buffer[2]);
            if(uploadDataClb) {
                json oneDataJson;
                oneDataJson["location"] = (int)command[index][0];
                oneDataJson["distance"] = data;
                uploadDataClb(upperObject, oneDataJson.dump()); //发送超声波信息给算法
            }
            index++;
        }
        LOGDF(TAG, "thread end");
    }

    bool Ultrasonic::sendToUltrasonic(unsigned char *buffer, size_t dataLen)
    {
        ssize_t len = write(fd, buffer, dataLen);
        CHECK_DO_RTN_VAL(len != dataLen, tcflush(fd, TCOFLUSH), -1)
        return len == dataLen;
    }

    bool Ultrasonic::receiveFromUltrasonic(unsigned char *buffer, size_t dataLen)
    {
        int len = read(fd, buffer, dataLen);
        CHECK_DO_RTN_VAL(len < 0, LOGDF(TAG, "read data failed") , -1)
        return len == dataLen;
    }

    int Ultrasonic::parseData(unsigned char high, unsigned char low)
    {
        return (int)high * 16 * 16 + (int)low;
    }

    int Ultrasonic::setSerialPort ()
    {
        struct termios tty;
        memset (&tty, 0, sizeof(tty));
        CHECK_DO_RTN_VAL(tcgetattr(fd, &tty) != 0, LOGWF(TAG, "tcgetattr failed, error %d", errno), -1)
        cfsetospeed (&tty, baudRate);
        cfsetispeed (&tty, baudRate);

        tty.c_cflag |= (CLOCAL | CREAD ); //CLOCAL--忽略 modem 控制线,本地连线, 不具数据机控制功能, CREAD--使能接收标志
        tty.c_cflag &= ~CSIZE; // 先把数据位清零
        tty.c_cflag |= CS8;  // 把数据位设置为8位

        tty.c_cflag &= ~PARENB;  //无奇偶校验

        tty.c_cflag &= ~CSTOPB;  //设置停止位1

        tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); //c_lflag控制串口驱动如何管理输入的字符,此处关闭数据回传
        tty.c_iflag &= ~(IXON | IXOFF | IXANY);

        tty.c_cc[VMIN]  = 4;            // 至少读到4个字符read才会返回
        tty.c_cc[VTIME] = 0.1;            //0.1/10 s超时

        tcflush(fd, TCIOFLUSH);
        CHECK_DO_RTN_VAL (tcsetattr (fd, TCSANOW, &tty) != 0, LOGWF(TAG, "tcsetattr failed, error %d", errno), -1)
        return 0;
    }

    void Ultrasonic::setUploadDataClb(const nativeClb &clb)
    {
        uploadDataClb = clb;
    }

    void Ultrasonic::setUpperObject(void *object) {
        upperObject = object;
    }

    int Ultrasonic::getValidId(const string &device) {
        struct dirent *entry;
        int ret = 0;
        auto deleter = [](DIR *p) {if(p) closedir(p); };

        unique_ptr<DIR, decltype(deleter)> dir(opendir(TTY.c_str()), deleter);
        CHECK_DO_RTN_VAL(dir == nullptr, LOGEF(TAG, "open dir error,path:%s", TTY.c_str()), -1)

        while ((entry = readdir(dir.get())) != nullptr) {
            CHECK_DO (entry->d_type != DT_LNK, continue;)
            string path = TTY + entry->d_name;
            char linkPath[256] = {0};
            ret = readlink(path.c_str(), linkPath, 256);
            CHECK_DO (ret == -1, LOGDF(TAG, "readlink error,errno=%d, path=%s", errno, path.c_str()); continue;)

            string pidPath = TTY + linkPath + "/../../../../idProduct";
            string vidPath = TTY + linkPath + "/../../../../idVendor";

            fstream pidStream(pidPath, ios::in);
            fstream vidStream(vidPath, ios::in);
            CHECK_DO (!pidStream || !vidStream, continue;)

            string pidTmp((istreambuf_iterator<char>(pidStream)), istreambuf_iterator<char>());
            string vidTmp((istreambuf_iterator<char>(vidStream)), istreambuf_iterator<char>());
            CHECK_DO (!pidTmp.empty(), pidTmp.pop_back())
            CHECK_DO (!vidTmp.empty(), vidTmp.pop_back())
            LOGDF(TAG, "pid:%s, vid:%s", pidTmp.c_str(), vidTmp.c_str());

            if(vidTmp + pidTmp == device) {
                auto found = path.find("ttyUSB");
                CHECK_DO(found == string::npos, continue)

                int index = atoi(path.substr(found + 6).c_str());
                return index;
            }
        }
        return -1;
    }
}
cmake_minimum_required(VERSION 3.6.0)

include(cppbase/CMakeLists.txt)
include_directories(${CMAKE_INCLUDE_CURRENT_DIR})

add_library(ultrasonic_native SHARED Ultrasonic.cpp native_interface.cpp)
target_link_libraries(ultrasonic_native log pudutech_log)

kotlin实例:

package com.pudutech.mirsdk.hardware.ultrasonic

import com.pudutech.base.Pdlog

/**
 * Ultrasonic native 声明
 */
class Ultrasonic {
    private var internalCallback: IUltrasonicInternalCallback? = null
    private val TAG = "Ultrasonic"
    private var mNativeContext: Long = 0 //native 与 kotlin的上下文关联,存实例的地址

    companion object {
        init {
            System.loadLibrary("ultrasonic_native")
        }
        /**
         * 对外提供打开Ultrasonic
         */
        fun open(): Ultrasonic {
            return Ultrasonic()
        }
    }

    init {
        var devices = arrayOf("1a867523")
        var ret: Boolean = openUltrasonic(devices)
        Pdlog.i(TAG, "open ultrasonic rlt: $ret")
    }

    fun close() {
        closeUltrasonic()
    }

    fun setCallback(callback: IUltrasonicInternalCallback) {
        this.internalCallback = callback
    }

    fun postEventFromNative(str: String) {
        Pdlog.i(TAG, "###ultrasonic data: $str")
        internalCallback?.onCallback(str)
    }

    /*******************JNI函数*********************/
    external fun openUltrasonic(devices: Array<String>): Boolean
    external fun closeUltrasonic()
}
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐