JNIEXPORT jstring JNICALL Java_jni_study_com_jnibsetpractice_Jni_sayHello
(JNIEnv *env, jobject instance) {
return (*env)->NewStringUTF(env, "Hello from C");
java里调用native方法
bt_1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String hello = jni.sayHello();
Toast.makeText(MainActivity.this,hello,Toast.LENGTH_LONG).show();
实践二:java传int参数给C处理,并且返回int
java代码,定义native函数
public class Jni {
static {
System.loadLibrary("best");
public native int add(int x,int y);
javah 生成jni样式的标准头文件
切换到src/main/java目录下执行
javah -d ../jni jni.study.com.jnibsetpractice.Jni
注意:javah 后半段跟的是 这个native方法所在的类的全路径名称
打开这个文件,发现生成了java里的这个native方法的jni方法签名
JNIEXPORT jint JNICALL Java_jni_study_com_jnibsetpractice_Jni_add
(JNIEnv *, jobject, jint, jint);
public native int add(int x,int y);
对比一下:
返回值:int
对应jint
方法名:add
对应Java_jni_study_com_jnibsetpractice_Jni_add
参数列表(int ,int )
对应(JNIEnv *, jobject, jint, jint)
这里说明一下
参数JNIEnv * :jni的环境,通过它来调用内置的很多jni方法
参数jobject:代表在java里调用这个native方法的实例对象(如果是静态方法代表的是类)
参数jint, jint:对应 int x,int y
JNIEnv是什么在JNI相关概念的理解文章里有详细解释
jint和int关系在JNI相关概念的理解文章里也有详细解释
c代码,实现java里的native方法
JNIEXPORT jint JNICALL Java_jni_study_com_jnibsetpractice_Jni_add
(JNIEnv * env, jobject instance, jint x, jint y){
return x+y;
Java调用native方法
bt_2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int add = jni.add(1, 2);
Toast.makeText(MainActivity.this, "1+2=" + add, Toast.LENGTH_LONG).show();
ok,完成了,是不是很简单,如果java传递string给C也是这么简单吗,记得吗java里的string与c里的string可是不一样哟
实践三:java传String参数给C处理,并且返回String
java代码,定义native函数
public class Jni {
static {
System.loadLibrary("best");
public native String transe_string(String str);
javah 生成jni样式的标准头文件
生成文件的方法略,直接分析这个文件
JNIEXPORT jstring JNICALL Java_jni_study_com_jnibsetpractice_Jni_transe_1string
(JNIEnv *, jobject, jstring);
public native String transe_string(String str);
对比一下:
jstring
对应String
看看jstring是什么
c代码,实现java里的native方法
java传给c一个string,javah生成了方法名后,
发现传递来的是一个jstring(因为在c里,是没有string的),
jstring其实是void*(任意类型),
我们需要调用一个方法,把jstring转为C语言的char*类型,先看下这个工具方法:
* 把一个jstring转换成一个c语言的char* 类型.
char* _JString2CStr(JNIEnv* env, jstring jstr) {
char* rtn = NULL;
jclass clsstring = (*env)->FindClass(env, "java/lang/String");
jstring strencode = (*env)->NewStringUTF(env,"GB2312");
jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid, strencode); // String .getByte("GB2312");
jsize alen = (*env)->GetArrayLength(env, barr);
jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
if(alen > 0) {
rtn = (char*)malloc(alen+1); //"\0"
memcpy(rtn, ba, alen);
rtn[alen]=0;
(*env)->ReleaseByteArrayElements(env, barr, ba,0);
return rtn;
实现native方法
JNIEXPORT jstring JNICALL Java_jni_study_com_jnibsetpractice_Jni_transe_1string
(JNIEnv *env, jobject instance, jstring jstr) {
//把一个jstring转换成一个c语言的char* 类型
char *cStr = _JString2CStr(env, jstr);
//c语言拼接字符串
char *cNewStr = strcat(cStr, "简单加密一下哈哈哈!!!");
// 把c语言里的char* 字符串转成java认识的字符串
return (*env)->NewStringUTF(env, cNewStr);
Java调用native方法
bt_3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String str = jni.transe_string("abc");
Toast.makeText(MainActivity.this, "abc ====c转换=====> " + str, Toast.LENGTH_LONG).show();
实践四:java传递给C int数组
java代码,定义native函数
public class Jni {
static {
System.loadLibrary("best");
public native int[] transeIntArray(int[] intArray);
javah 生成jni样式的标准头文件
生成jni头文件的方法 略
JNIEXPORT jintArray JNICALL Java_jni_study_com_jnibsetpractice_Jni_transeIntArray
(JNIEnv *, jobject, jintArray);
public native int[] transeIntArray(int[] intArray);
jintArray
对应int[]
c代码,实现java里的native方法
JNIEXPORT jintArray JNICALL Java_jni_study_com_jnibsetpractice_Jni_transeIntArray
(JNIEnv *env, jobject instance, jintArray jArray) {
// 得到从java传递来的int[]的长度
jsize length = (*env)->GetArrayLength(env, jArray);
// 得到数组指针
int *arrayPointer = (*env)->GetIntArrayElements(env, jArray, NULL);
// 开始遍历数组,把每个元素加100
int i;
for (i = 0; i < length; i++) {
*(arrayPointer + i) += 100;
// ★★★★将arrayPointer这个int *中值复制到jArray数组中,别忘了这一步骤★★★
(*env)->SetIntArrayRegion(env,jArray, 0, length, arrayPointer);
// 返回数组
return jArray;
Java调用native方法
bt_4.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int[] intArray = new int[]{1, 2, 3};
int[] intArrayTranse = jni.transeIntArray(intArray);
for (int i = 0; i < intArray.length; i++) {
//发现在这里遍历旧的数组,打印出来也是加过100后的数字,101,102,103
// 因为C是通过指针操作内存地址,改变了intArray的内存地址的值,
// 所以C里面不返回int[]也是可以实现目的的
Log.e("abc",intArray[i]+"old");
for (int i = 0; i < intArrayTranse.length; i++) {
Log.e("abc",intArrayTranse[i]+"new");
}//这里打印的是101,102,103
log如下
实践五:C调Java 空参数方法
前面讲的都是java调用C的方法,有时候我们需C里调用Java的方法,
例如:我们有一个操作图片的方法是C写的,在C操作图片是,需要让android弹出一个进度条,展示图片操作的进度,这时候,就需要C代码里调用java代码,来改变UI了
C调java,其实用的反射,先复习一下java的反射
public class Animal {
public String say(String s) {
Log.e("tag", s);
return s;
// 1. 获取字节码对象
Class<Animal> clazz = Animal.class;
// 2. 获取method方法的对象
try {
Method say = clazz.getDeclaredMethod("say", String.class);
// 3. 通过字节码获取Animal的实例
Animal animal = clazz.newInstance();
// 4.通过实例调用方法
say.invoke(animal, "hello!");
} catch (Exception e) {
e.printStackTrace();
ok,现在正式开始C调用java
java代码,定义native函数
public class Jni {
static {
System.loadLibrary("best");
// 这个方法要去调用C代码
public native void callBackMethodVoid();
// 我是java实现的方法,等待C里的callBackMethodVoid方法回调我
public void helloFromJava() {
Log.e("tag", "hello from java");
javah 生成jni样式的标准头文件
生成jni头文件的方法 略
c代码,实现java里的native方法
JNIEXPORT void JNICALL Java_jni_study_com_jnibsetpractice_Jni_callBackMethodVoid
(JNIEnv *env, jobject instance) {
// 1. 获取字节码对象
jclass clazz = (*env)->FindClass(env, "jni/study/com/jnibsetpractice/Jni");
// 2. 获取method方法的对象
jmethodID methodID = (*env)->GetMethodID(env, clazz, "helloFromJava", "()V");
// ★因为java方法有重载,所以只是靠方法名找不到这个唯一的函数,所以需要最后一个参数
// 注意最后一个参数"()V",这个代表了方法的参数,这里无参数用(),V代表了方法的返回值,这里是void,所以是V
// ★★★★★★★★★★★重点理解 第二个参数 instance★★★★★★★★★★★★★
// 3. 通过字节码获取Animal的实例,
// 这里不要创建这个实例,因为上面的第二个参数 jobject instance,
// 代表的就是调用该方法的对象
// 在本例中是通过 jni.callBackMethodVoid()调用Java_jni_study_com_jnibsetpractice_Jni_callBackMethodVoid的
//那么instance就是jni(Jni的对象)
// ★★★★★★★★★★★重点理解 第二个参数 instance★★★★★★★★★★★★★
// 4.通过实例调用java里的方法
(*env)->CallVoidMethod(env, instance, methodID);
Java调用native方法
bt_6.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
jni.callBackMethodVoid();
实践六:C调Java int类型参数方法
java代码,定义native函数
public class Jni {
static {
System.loadLibrary("best");
// 这个方法要去调用C代码
public native int callBackMethodInt();
// 我是java实现的方法,等待C里的callBackMethodInt方法回调我
public int addInJava(int x, int y) {
return x + y;
javah 生成jni样式的标准头文件
c代码,实现java里的native方法
JNIEXPORT jint JNICALL Java_jni_study_com_jnibsetpractice_Jni_callBackMethodInt
(JNIEnv *env, jobject instance) {
// 1. 获取字节码对象
jclass clazz = (*env)->FindClass(env, "jni/study/com/jnibsetpractice/Jni");
// 2. 获取method方法的对象
jmethodID methodID = (*env)->GetMethodID(env, clazz, "addInJava", "(II)I");
// 3. 通过字节码获取调用者的实例, 这里不需要,因为就是instance
// 4.通过实例调用方法
jint add = (*env)->CallIntMethod(env, instance, methodID, 9, 9);
return add;
Java调用native方法
bt_7.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int i = jni.callBackMethodInt();
Toast.makeText(MainActivity.this, "C——》java" + i, Toast.LENGTH_LONG).show();
实践七:C调用java string参数
java代码,定义native函数
public class Jni {
static {
System.loadLibrary("best");
// 这个方法要去调用C代码
public native void callbackMethodString();
//我是java实现的方法,等待C里的callbackMethodString方法回调我
// C 是无法直接打印log的,我们可以,通过此方法,在C里打印log
// 当然在C里通过配置也是可以打印log的,以后再说
public void printString(String s){
System.out.println(s);
Log.e("tag",s);
javah 生成jni样式的标准头文件
c代码,实现java里的native方法
JNIEXPORT void JNICALL Java_jni_study_com_jnibsetpractice_Jni_callbackMethodString
(JNIEnv *env, jobject instance){
// 1. 获取字节码对象
jclass clazz = (*env)->FindClass(env, "jni/study/com/jnibsetpractice/Jni");
// 2. 获取method方法的对象
jmethodID methodID = (*env)->GetMethodID(env, clazz, "printString", "(Ljava/lang/String;)V");
// 3. 通过字节码获取调用者的实例, 这里不需要,因为就是instance
// 4.通过实例调用方法
// ★★★4.1调用之前,记得在C里的"字符串",java不识别,要转换一下★★★
jstring result =(*env)->NewStringUTF(env,"hello from c");
// 4.2 放心去调用吧
(*env)->CallVoidMethod(env,instance,methodID,result);
Java调用native方法
bt_8.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
jni.callbackMethodString();
实践八:C调java 弹出吐司
有两种写法
我们可以把一个native方法,写到了Activity里,这样
JNIEXPORT jstring JNICALL xxxxxx (JNIEnv *env, jobject instance)
中的第二个参数instance,就是Activity,第三步不需要《3. 通过字节码获取调用者的实例》,直接使用instance就好了
应该在构造方法里传入context,让toast方法不要带上context参数,总之一句话context(上下文)的实例是不能通过反射获得的,切记!切记!切记!切记!切记!
Context mContext;
private Jni(Context context) {
mContext = context;
// 这个方法要去调用C代码
public native void callbackMethodToast();
//我是java实现的方法,等待C里的callbackMethodToast方法回调我
public void toast(String s) {
Toast.makeText(mContext, s, Toast.LENGTH_LONG).show();
方法一:写在Activity里
java代码,定义native函数
public class MainActivity extends AppCompatActivity {
// C调java的toast方法,写到activity里
public native String callbackToastInActivity();
public void toastJavaInativity(String s){
Toast.makeText(MainActivity.this, s , Toast.LENGTH_LONG).show();
javah 生成jni样式的标准头文件
略,注意是javah -d ../jni jni.study.com.jnibsetpractice.MainActivity
c代码,实现java里的native方法
JNIEXPORT jstring JNICALL Java_jni_study_com_jnibsetpractice_MainActivity_callbackToastInActivity
(JNIEnv *env, jobject instance){
// 1. 获取字节码对象
jclass clazz = (*env)->FindClass(env, "jni/study/com/jnibsetpractice/MainActivity");
// 2. 获取method方法的对象
jmethodID methodID = (*env)->GetMethodID(env, clazz, "toastJavaInativity", "(Ljava/lang/String;)V");
// 3. 通过字节码获取调用者的实例, 这里不需要,因为就是instance
// 4.通过实例调用方法
// 4.1调用之前,记得在C里的"字符串",java不识别,要转换一下
jstring result =(*env)->NewStringUTF(env,"写在Activity的toast");
// 4.2 放心去调用吧
(*env)->CallVoidMethod(env,instance,methodID,result);
return (*env)->NewStringUTF(env, "写在Activity的toast");
Java调用native方法
bt_9_1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String toast = callbackToastInActivity();
Log.e("tag",toast);
方法二:写在单独的Jni类里
java代码,定义native函数
public class Jni {
static {
System.loadLibrary("best");
Context mContext;
// 把context通过构造方法,传递进来,避免在toast()方法里传递context
public Jni(Context context) {
mContext = context;
// 这个方法要去调用C代码
public native void callbackMethodToast();
//我是java实现的方法,等待C里的callbackMethodToast方法回调我
public void toast(String s) {
//这里不要要传递context参数,context不能通过反射获得
Toast.makeText(mContext, s, Toast.LENGTH_LONG).show();
javah 生成jni样式的标准头文件
略 注意是javah -d ../jni jni.study.com.jnibsetpractice.Jni
c代码,实现java里的native方法
JNIEXPORT void JNICALL Java_jni_study_com_jnibsetpractice_Jni_callbackMethodToast
(JNIEnv *env, jobject instance){
// 1. 获取字节码对象
jclass clazz = (*env)->FindClass(env, "jni/study/com/jnibsetpractice/Jni");
// 2. 获取method方法的对象
jmethodID methodID = (*env)->GetMethodID(env, clazz, "toast", "(Ljava/lang/String;)V");
// 3. 通过字节码获取调用者的实例, 这里不需要,因为就是instance
// 4.通过实例调用方法
// 4.1调用之前,记得在C里的"字符串",java不识别,要转换一下
jstring result =(*env)->NewStringUTF(env,"写在Jni类的toast");
// 4.2 放心去调用吧,
// ★★★★★★★注意我们保证了toast方法没传入context对象,因为context对象在C里反射出来是不能用的,会空指针异常★★★★★★★★★
(*env)->CallVoidMethod(env,instance,methodID,result);
Java调用native方法
bt_9_2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
jni.callbackMethodToast();
C调用java小结
① 找到字节码对象
//jclass (FindClass)(JNIEnv, const char*);
//第二个参数 要回调的java方法所在的类的路径 "jni/study/com/jnibsetpractice/Jni"
② 通过字节码对象找到方法对象
//jmethodID (GetMethodID)(JNIEnv, jclass, const char*, const char*);
第二个参数 字节码对象 第三个参数 要反射调用的java方法名 第四个参数 要反射调用的java方法签名
javap -s 要获取方法签名的类的全类名 项目/bin/classes 运行javap
③ 通过字节码创建 java对象(可选) 如果本地方法和要回调的java方法在同一个类里可以直接用 jni传过来的java对象 调用创建的Method
jobject obj =(*env)->AllocObject(env,claz);
当回调的方法跟本地方法不在一个类里 需要通过刚创建的字节码对象手动创建一个java对象
再通过这个对象来回调java的方法
需要注意的是 如果创建的是一个activity对象 回调的方法还包含上下文 这个方法行不通!!!回报空指针异常
④ 反射调用java方法
//void (CallVoidMethod)(JNIEnv, jobject, jmethodID, ...);
第二个参数 调用java方法的对象 第三个参数 要调用的jmethodID对象 可选的参数 调用方法时接收的参数
在C里输出log的办法
在C 里输入
在Android.mk里输入
LOCAL_LDLIBS += -llog
使用log
LOGD("length = %d", length);
HELLO_C26726
android
粉丝