- 新建Java JNI 类 MyJni.java,并编写JNI native方法。
public class MyJNI { static { System.loadLibrary("jni_native"); } public static native String getStringFromNative(); public static native int OpenDeviceNode(); public static native int CloseDeviceNode(); public static native int IICWriteRead(byte[] wBuf, int wLen, byte[] rBuf, int rLen); }
- 生成头文件,cmd进入到工程路径 NdkDemoappsrcmainjava,执行命令生成.h头文件。
javah -classpath . -jni com.csu.jni.MyJNI
在MyJNI.java文件的同级目录(mainjavacom.csu.jni)生成一个.h的头文件com_csu_jni_MyJNI.h。
#include#include #include #include #include #include #include #include #include #include #ifndef _Included_com_csu_jni_MyJNI #define _Included_com_csu_jni_MyJNI #ifdef __cplusplus extern "C" { #endif JNIEXPORT jstring JNICALL Java_com_csu_jni_MyJNI_getStringFromNative (JNIEnv *, jclass); JNIEXPORT jint JNICALL Java_com_csu_jni_MyJNI_OpenDeviceNode (JNIEnv *, jclass); JNIEXPORT jint JNICALL Java_com_csu_jni_MyJNI_CloseDeviceNode (JNIEnv *, jclass); JNIEXPORT jint JNICALL Java_com_csu_jni_MyJNI_IICWriteRead (JNIEnv *, jclass, jbyteArray, jint, jbyteArray, jint); #ifdef __cplusplus } #endif #endif
- main下java同级目录新建jni目录,将step6生成的.h文件拷贝到jni目录,然后新建com_csu_jni_MyJNI.c文件,实现.h文件中的方法。
// // Created by Admin on 2021/11/17. // #include "com_csu_jni_MyJNI.h" #define LOG_TAG "MyJNI" #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) #define DEVICE_NAME "/dev/myjni" static int fd = -1; int file_read(jbyte *rbuf, jint rlen) { int ret; if (fd < 0) { LOGE("file_read(), JNI IS NOT OPENEDn"); } LOGI("JNI file_read(), rlen=%d, rbuf=%dn", rlen, rbuf[0]); ret = read(fd, rbuf, rlen); if (ret < 0) { LOGE("JNI read failed, ret=%dn", ret); } LOGI("JNI read success, ret=%dn", ret); return ret; } int file_write(jbyte *wbuf, jint wlen) { int ret; if (fd < 0) { LOGE("file_write(), JNI IS NOT OPENEDn"); } LOGI("JNI file_write(), wlen=%d, wbuf=%dn", wlen, wbuf[0]); ret = write(fd, wbuf, wlen); if (ret < 0) { LOGE("JNI write failed, ret=%dn", ret); } LOGI("JNI write success, ret=%dn", ret); return ret; } JNIEXPORT jstring JNICALL Java_com_csu_jni_MyJNI_getStringFromNative(JNIEnv *env, jclass clazz) { return (*env)->NewStringUTF(env, "My JNI string"); } JNIEXPORT jint JNICALL Java_com_csu_jni_MyJNI_OpenDeviceNode(JNIEnv *env, jclass clazz) { fd = open(DEVICE_NAME, O_RDWR|O_NONBLOCK); if (fd < 0) { LOGE("MyJNI open device failed, fd =%dn", fd); } LOGI("MyJNI open device Success, fd =%dn", fd); return fd; } JNIEXPORT jint JNICALL Java_com_csu_jni_MyJNI_CloseDeviceNode(JNIEnv *env, jclass clazz) { close(fd); return 0; } JNIEXPORT jint JNICALL Java_com_csu_jni_MyJNI_IICWriteRead(JNIEnv *env, jclass clazz, jbyteArray wBuf, jint wLen, jbyteArray rBuf, jint rLen) { int ret = 0; // 将Java层的数据拷到缓冲区 jbyte* bufw = (*env)->GetByteArrayElements(env, wBuf, NULL); //jbyte* bufr = (*env)->GetByteArrayElements(env, rBuf, NULL); //jint wlen = (*env)->GetArrayLength(env, wBuf); jint len = (*env)->GetArrayLength(env, rBuf); // 驱动会把要读取数据的寄存器放在首位返回,所以从驱动获取的数据buf长度要+1 len = len + 1; // 新建一个数组,用于将驱动数据拷贝到缓冲区 jbyteArray jarr = (*env)->NewByteArray(env, len); jbyte* jbuf = (*env)->GetByteArrayElements(env, jarr, NULL); LOGI("JNI IICWriteRead(), wlen=%d, rlen=%dn", wLen, rLen); if (wLen > 1) { ret = file_write(bufw, wLen); LOGI("JNI IICWriteRead(), wlen=%d, ret=%dn", wLen, ret); } // get register addr jbuf[0] = bufw[0]; if (rLen > 0) { ret = file_read(jbuf, rLen); LOGI("JNI IICWriteRead(), rlen=%d, ret=%d, rbuf=%d.%d.%d.%dn", rLen, ret, jbuf[1], jbuf[2], jbuf[3], jbuf[4]); // 驱动返回缓存区的数据,首位为寄存器地址,真正获取的数据从下标1开始 memcpy(jbuf, jbuf + 1, len - 1); // 将缓冲区的数据返回给Java形参 (*env)->SetByteArrayRegion(env, rBuf, 0, rLen, jbuf); } // 释放资源,避免内存泄漏 (*env)->ReleaseByteArrayElements(env, rBuf, jbuf, 0); (*env)->DeleteLocalRef(env, jarr); LOGI("JNI IICWriteRead(), ret=%dn", ret); return ret; }
- jni目录下新建Android.mk文件。
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := jni_native LOCAL_SRC_FILES := com_csu_jni_MyJNI.c LOCAL_LDLIBS :=-llog APP_ABI := all include $(BUILD_SHARED_LIBRARY)
- cmd进入到工程路径 NdkDemoappsrcmainjava,执行命令编译生成.so文件。
ndk-build
在jni同级目录下会生成两个文件夹libs和obj。
-
将libs文件夹下的内容拷贝到module项目名下的libs目录。
-
module的build.gradle 文件的android{}节点中添加对libs的引用:
sourceSets { main { jniLibs.srcDirs = ['libs'] } }
- Activity中调用JNI方法
MainActivity.java
public class MainActivity extends AppCompatActivity { private static final String TAG = "lxy"; private TextView tvInfo; private int id = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); String str = MyJNI.getStringFromNative(); Log.i(TAG, "onCreate: rs = " + str); Button btnOpen = findViewById(R.id.btn_open); Button btnClose = findViewById(R.id.btn_close); Button btnFwVersion = findViewById(R.id.btn_read_fw_version); Button btnWrite = findViewById(R.id.btn_write); Button btnRead = findViewById(R.id.btn_read); tvInfo = findViewById(R.id.tv_info); btnOpen.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (id > 0) { Log.i(TAG, "onClick: device is opened!!!"); updateTextInfo("device is opened!!!"); } else { id = MyJNI.OpenDeviceNode(); updateTextInfo("device open, id = " + id); Log.i(TAG, "onClick: device open id = " + id); } } }); btnClose.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (id > 0) { int rs = MyJNI.CloseDeviceNode(); Log.i(TAG, "onClick: close device rs = " + rs); updateTextInfo("close device, rs = " + rs); id = 0; } else { Log.i(TAG, "onClick: device is not open"); updateTextInfo("device is not open"); } } }); btnFwVersion.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (id > 0) { byte[] wBuf = {0x10}; byte[] rBuf = new byte[256]; int rs = MyJNI.IICWriteRead(wBuf, 1, rBuf, 4); Log.i(TAG, "onClick: ret = " + rs + ",rBuf= " + rBuf[0] + "." + rBuf[1] + "." + rBuf[2] + "." + rBuf[3]); updateTextInfo("ret = " + rs + ",rBuf= " + rBuf[0] + "." + rBuf[1] + "." + rBuf[2] + "." + rBuf[3]); } } }); btnWrite.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (id > 0) { write(); } } }); btnRead.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (id > 0) { read(); } } }); } @Override protected void onDestroy() { super.onDestroy(); int rs = MyJNI.CloseDeviceNode(); Log.i(TAG, "onClick: close device rs = " + rs); } private void write() { byte[] wbuf = {0x00, 0x00, 0x01, 0x02, 0x03}; byte[] rbuf = new byte[10]; int rs = MyJNI.IICWriteRead(wbuf, 5, rbuf, 0); Log.i(TAG, "write(): ret=" + rs); updateTextInfo("write to fw : " + Arrays.toString(wbuf)); } private void read() { byte[] wbuf = {0x00}; byte[] rbuf = new byte[256]; int rs = MyJNI.IICWriteRead(wbuf, 1, rbuf, 6); Log.i(TAG, "read(): ret=" + rs + ", rbuf = " + Arrays.toString(rbuf)); updateTextInfo("read from fw : " + Arrays.toString(Arrays.copyOf(rbuf, 6))); } private void updateTextInfo(final String s) { runOnUiThread(new Runnable() { @Override public void run() { tvInfo.setText(s); } }); } }
布局xml文件:
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)