#include <string.h> #include <jni.h> jint Java_com_jni_MethodCall1_jniCalljavaMethod(JNIEnv* env, jobject thiz) //env:当前该线程的内容,包含线程全部的东西;thiz:当前类的实例,指.java文件的内容 { jint si; jfieldID fid; // 一个字段,实际上对应java类里面的一个字段或属性; jclass cls = (*env)->GetObjectClass(env, thiz); // 类的对象实例 jmethodID mid = (*env)->GetStaticMethodID(env, cls, "javaMethod", "()V"); // 一个方法的id //(I)V (I)I if (mid == NULL) { return -1; } (*env)->CallStaticVoidMethod(env, cls, mid); //调用callback方法 fid = (*env)->GetStaticFieldID(env, cls, "value", "I"); //取出value字段 if (fid == NULL) { return -2; } si = (*env)->GetStaticIntField(env, cls, fid); //取出字段对应的值(fid字段对应的值) return si; // return (*env)->NewStringUTF(env, "init success"); }
3.完善Android.mk文件,参照二里面第四步;
4.运行代码
MethodCall1 mc1 = new MethodCall1(); Log.e(tag, MethodCall1.value + "->" + mc1.jniCalljavaMethod());
四、方法签名规则
JNI类型签名规则 Java类型 类型签名 Java类型 类型签名boolean Z long J
byte B float F
char C double D
short S 类 L全限定类名;
int I 数组 [元素类型签名
上面是各种数据类型对应的签名字符
基本数据类型的签名很简单,只是一个选定的字母;
类的签名规则是:"L" + 全限定类名+";"三部分组成,其中全限定类名以"/"分隔;
方法的签名组成:"(参数签名)" + "返回值签名"
例如Java方法long fun(int n, String str, int[] arr);
根据上面的签名规则可以得到其签名为:(ILjava/lang/String;[I)J
上面的签名分为两部分:括号里面为函数的参数,参数的内容分三部分"I","Ljava/lang/String;","[I",之间没有空格;括号外边是函数的返回类型签名。需要注意的是如果函数返回类型为void则其中返回类型签名为V;
五、动态注册函数
前面二和三都是c/c++里面方法的名称来映射函数,其实jni还为我们提供了动态注册函数的功能;
1.添加java文件 DynamicRegisterMethod.java
package com.jni; public class DynamicRegisterMethod { static { System.loadLibrary("dynamicregistermethod"); } public native String dynamicRegisterMethod(); }
2.添加 c 文件
#include <string.h> #include <jni.h> #ifndef _Included_org_spring_SpringUtils #define _Included_org_spring_SpringUtils jstring JNICALL java_dynamicRegisterMethod(JNIEnv * env, jobject obj) { return (*env)->NewStringUTF(env, "dynamicRegisterMethod"); } static JNINativeMethod gmethods[] = { { "dynamicRegisterMethod", "()Ljava/lang/String;", (void*) java_dynamicRegisterMethod } }; static int registerNativeMethods(JNIEnv * env, const char* className, JNINativeMethod* gMethods, int numMethods) { jclass clazz; clazz = (*env)->FindClass(env, className); if (clazz == NULL) return JNI_FALSE; if (((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0)) { return JNI_FALSE; } return JNI_TRUE; } static int registerNatives(JNIEnv* env) { if (!registerNativeMethods(env, "com/jni/DynamicRegisterMethod", gmethods, sizeof(gmethods) / sizeof(gmethods[0]))) { return JNI_FALSE; } return JNI_TRUE; } jint JNI_OnLoad(JavaVM* vm, void* reserved) { jint result = -1; JNIEnv* env = NULL; if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_4)) { goto fail; } if (registerNatives(env) != JNI_TRUE) { goto fail; } result = JNI_VERSION_1_4; fail: return result; } #endif
3.在Android.mk文件中进行编译的配置(省略,参考前面的例子)
4.ndk-build编译项目
DynamicRegisterMethod drm = new DynamicRegisterMethod(); Log.e(tag, drm.dynamicRegisterMethod());
执行结果:
可以看到通过动态注册方法的方式,也是成功的调用了 native 方法;
六、加入链接库
在程序开发过程中,会频繁的用到调试,方式有很多种,下面要讲的这一种是通过log打印信息来打印程序运行时的一些状态数值;
修改Android.mk文件,添加一句代码
include $(CLEAR_VARS) LOCAL_LDLIBS += -llog //LDLIBS:连接libs,后面跟的参数为需要链接的libs,-llog表示Android中的Log库; include $(BUILD_SHARED_LIBRARY)
加入了log库之后,即可通过c/c++文件直接打印log信息;
在c/c++中调用log打印输出信息:
#include <android/log.h> __android_log_print(ANDROID_LOG_ERROR, "hello", "livingstone"); __android_log_print(ANDROID_LOG_DEBUG, "hello", "livingstone %d" ,23);