写在前面的:

一年前,在 android A20下面弄一个应用,当时要操作硬件串口uart,需要java与c进行交互,于是按照经典的流程: 用javah 生成一个头文件,然后我去实现头文件里面的函数。再后来,来了一哥们,弄了另外一种方法写Jni,搭建了一个框架,飘然而去,然后留下几个c的API让我去维护....半年过去了,最近,自己开始学习 c++部分的东西,于是开始来学习他的这部分代码。并在网上找了学习资料。

下面是我找到的资料和笔记。

/**************************************************************************************************************/

先把自己找的资料贴在这里,感谢作者分享!

http://blog.csdn.net/jianguo_liao19840726/article/details/6719224

简而言之:

1 android(java)代码中的本地方法列表(c/c++ 库提供的API),示例如下:

public class DataProvider {    private static final class DataProviderHolder {        private static final DataProvider instance = new DataProvider();    }    private DataProvider() {    }    public static DataProvider getInstance() {        return DataProviderHolder.instance;    }/*本地方法实例一*/    public synchronized native int Operator(String deviceMac, int deviceType,            int state);/*本地方法实例二*/    public synchronized native int OperatorCmd(String deviceMac,            int deviceType, int cmd, int state);/*本地方法实例三*/    public synchronized native String OperatorCmdString(String deviceMac,            int deviceType, int cmd, int state);/*本地方法实例四*/    public native int OpenTty(String serialPort);/*本地方法实例五*/    public synchronized native String WriteDevice(String value,int type,int cmd,int val);
/*开始呼唤上面列出来的本地实例*/
static { System.loadLibrary("zigbee_r_lock"); } }

2 在程序执行到

System.loadLibrary("zigbee_r_lock");

这句代码的时候,会执行 loadLibrary()中的一个回调函数(callback),这个回调名字是:JNI_OnLoad().


3 c/c++的程序员来实现 JNI_OnLoad()
示例如下:
 1 jint JNI_OnLoad(JavaVM* vm, void* reserved) 2 { 3     JNIEnv* env = NULL; 4     jint result = -1; 5  6     device = new Device() ; 7  8     if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { 9         return result;10     }11     assert(env != NULL);12 13     if (register_tuner_jni(env) < 0)14     {15         return result;16     }17 18     result = JNI_VERSION_1_4;19 20     return result;21 }

在 JNI_OnLoad()中,可以实现资源的分配,比如 new 一个对象;

注册本地方法;

指定jni 所使用的版本。

4 因为要注册的本地方法是各种各样的,于是自己实现一个注册方法即可: register_tuner_jni(JNIEnv *env),代码如下:

static int register_tuner_jni(JNIEnv *env){  return registerNativeMethods(env,"cn/acadiatech/telecom/box/engine/DataProvider", gMethods,  sizeof(gMethods) / sizeof(gMethods[0]));}

registerNativeMethods(env,"cn/acadiatech/telecom/box/engine/DataProvider", gMethods, sizeof(gMethods) / sizeof(gMethods[0]));

这句代码中的第二个参数,是一个类的名字。比如在eclipse某个xx.java文件中新建一个类后,它会生成一个pack或者类的路径。比如上面的DataProvider这个类就放在了eclipse的 src/cn/acadiatech/telecom/box/engine/DataProvider.java 文件中。于是,registerNativeMethods()的第二个参数<类名> 就写成了 "cn/acadiatech/telecom/box/engine/DataProvider" 如果这个类名搞错了,是跑不起来的。

看看代码吧:

 1 static int registerNativeMethods(JNIEnv* env, const char* className, 2     JNINativeMethod* gMethods, int numMethods) 3 { 4     jclass clazz; 5  6  7     clazz = env->FindClass(className); 8     if (clazz == NULL) { 9         return JNI_FALSE;10     }11     if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {12         return JNI_FALSE;13     }14 15     return JNI_TRUE;16 }

最关键的就是这句代码:

    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {        return JNI_FALSE;    }

其中的 gMethods 是一个函数指针列表 <数组>,如下:

 1 static JNINativeMethod  gMethods[] = { 2     {"OperatorCmd","(Ljava/lang/String;III)I",(void *)Operator_device_Cmd}, 3     {"OpenTty","(Ljava/lang/String;)I",(void*)Open_tty_device}, 4     {"CloseTty","()V",(void*)Close_tty_device}, 5     {"SetTtySpeed","(I)I",(void*)Set_speed_tty_device}, 6     {"SetTtyParity","(I)I",(void*)Set_parity_tty_device}, 7     {"SetTtyBits","(I)I",(void*)Set_bits_tty_device}, 8     {"SetTtyStopBits","(I)I",(void*)Set_stop_bits}, 9     {"SetTtyFlowControl","(I)I",(void*)Set_flow_control_tty_device},10     {"ListDevice","()Ljava/lang/String;",(void*)List_device},11     {"ReadDevice","()Ljava/lang/String;",(void*)Read_device},12     {"ListDevice_new","(Ljava/lang/String;III)Ljava/lang/String;",(void*)List_device_new},13     {"WriteDevice","(Ljava/lang/String;III)Ljava/lang/String;",(void*)Write_device},14     {"LedControl","(Ljava/lang/String;IIIII)Ljava/lang/String;",(void*)Led_Control},15 //    {"OperatorCmdString","(Ljava/lang/String;III)Ljava/lang/String;",(void*)Operator_device_Cmd_String},16 };

额... 对,数量是没有限制的,只是,自己需要将要注册函数的个数传输给 registerNativeMethods() 即可。

这个函数指针列表每一项分为三部分 :

[1] 在java中的 Native名字--- java使用的时候,就使用这个名字

[2] 类型 ---- 包括参数类型 + 返回值类型

[3] c/c++ 中函数的名字,c/c++程序员要实现的,就是实现这个函数名字里面的东西

说说类型部分吧:

第一点:

" () " 中的东西,表示了函数的参数类型,紧跟在括号后面的表示了返回值的类型。如果类型为 java 中的 String 类型,那么必须以下面这种格式写:

Ljava/lang/String;

注意它的格式: 以 L 开头,以 " ;" 分号结尾。

无论是返回值函数参数类型,都必须这种写法。

第二点:

注意类型的转换: 比如自己要在c/c++中给 java层返回 char * / string 这种类型的数据,是不能直接返回的,需要经过类型转换才行的。此为后话,后面再提。

5 实现函数指针列表中的函数(c/c++部分的函数名字)

static jintOpen_tty_device(JNIEnv *env ,jobject obj ,jstring tty){    const char* tty_name = env->GetStringUTFChars(tty,0) ;    return device->open_tty_device(tty_name) ;}
/* ** if success return this open device fd else error is -1  */int Device::open_tty_device(const char* tty_name ){        this->fd = ngb_Open((char*)tty_name) ;    if (this->fd < 0) {    }    return this->fd ;}

当然了,Device是一个类,里面包括了很多成员函数。于是,整个jni 的脉络框架就这样了。唯里面的细节部分需要注意了,比如资源的申请、类型的转换、具体的c++部分代码的实现了---这已经于jni 没关系了,而是c++的范畴了。

写在最后: 如果要调用硬件成功,android层需要添加一行 “加访问权限” 的代码,否则将无权限打开底层硬件---这仅仅是针对要限制权限的情况下。

笔记中所用到的代码位于github上:

https://github.com/boyisgood86/libzigbee

更多相关文章

  1. 没有一行代码,「2020 新冠肺炎记忆」这个项目却登上了 GitHub 中
  2. Android(安卓)HIDL 在Java 中使用
  3. Android(安卓)ActionBar使用方法(一)
  4. [置顶] android 如何在自定义对话框中获取edittext中的数据
  5. 更适合Android的集合 SparseArray/ArrayMap/ArraySet
  6. Android实现动态高斯模糊效果示例代码
  7. Android(安卓)WebView填坑记录
  8. power_supply子系统笔记
  9. 史上最详细创建 Android(安卓)AIDL 远程服务步骤

随机推荐

  1. Coco2d-x从Win32移植到Android
  2. Android(安卓)-- 检测耳机插入状态
  3. Android 6.0后强制弹出权限
  4. Android 去掉title bar的3个方法
  5. 联系人头像 android
  6. 一个android悬浮窗的语音识别demo
  7. Android获取手机中的所有音乐地址
  8. android 屏幕截屏
  9. Android学习07-----事件处理(2)单选按钮
  10. Timer使用