C调用Java中的方法
其实就是从java中调用c,从C中调用java的方法,可以看做是一个回调
Java中没有参数的方法
1.在java中写一个本地方法
public native void methodInC();
2.使用命令提示符进入到项目下的bin/classes目录下,使用javah 类的全路径 命令得到本地方法的头文件
3.在项目上点右键–>Android Tools–>Add Native Support,给我们的C代码起一个名字,点确定,会自动生成一个jni的目录和c代码(一般生成的是.cpp,把后缀改为.c)
4.自动生成的Android.mk文件中的编译源文件是.cpp,改为c(如果第三步没有改,则不需要改)
#指定编译的文件夹 指定当前文件目录
LOCAL_PATH := $(call my-dir)
#编译器会定义很多的临时变量,中间变量,最好清空所有的中间变量。
include $(CLEAR_VARS)
#编译出来模块的名称(编译后的名字,也就是存放于libs目录下的名字,不带扩展名)
LOCAL_MODULE := hello
# 编译的源代码的名称(要编译的c代码的名称,带扩展名)
LOCAL_SRC_FILES := hello.c
#编译一个动态库
include $(BUILD_SHARED_LIBRARY)
5.如果需要在除arm处理器的其它机型上使用,需要在jni目录下创建一个Application.mk文件(如果只是在arm下使用,可以跳过此步骤),需要哪一个处理器,就配置哪一个,不要配置太多,因为生成的文件会很大
APP_ABI := arm x86 mips
6.把ndk安装的目录下的android-ndk-r9b\platforms\android-16\arch-arm\usr\include/jni.h复制到项目的jni目录下(平台就现在版本没有任何区别,哪一个版本下的都可以)
7.写C代码
#include <jni.h>
#include "stdio.h"
#include "com_itheima_calljava_DataProvider.h"
/**
* 用C代码调用java代码中的方法,去发送一条短信
*/
JNIEXPORT void JNICALL Java_com_itheima_calljava_DataProvider_methodInC
(JNIEnv* env, jobject obj){
//第一步:找到要调用的方法的class字节码文件
jclass clazz = (*env)->FindClass(env,"com/itheima/calljava/DataProvider");
//第二步:找到这个类中的方法.参数依次为:JVM虚拟机环境、class对象、方法名、方法签名(返回值+方法的形参)
jmethodID mid = (*env)->GetMethodID(env,clazz,"methodInjava","()V");
//第三步:调用这个方法
(*env)->CallVoidMethod(env,obj,mid);
}
8.在要调用本地方法的类中使用静态代码块的方式加载生成的c代码库
static{
//这个名字是在Android.mk中的LOCAL_MODULE的值,不带前缀,不带扩展名
System.loadLibrary("hello");
}
9.只要在java中调用本地方法methodInC即可
注: Java中的被C调用的方法
/**
* 让C调用此方法,发送一条短信
*/
public void methodInjava(){
Log.i(TAG,"-------------------------");
//创建短信管理对象
SmsManager smsManager = SmsManager.getDefault();
//发送一条文本短信
smsManager.sendTextMessage("5556", null, "hello 5556", null, null);
}
调取静态方法(C中的代码,其它没区别)
/**
* 用C代码去调用java中的静态方法
*/
JNIEXPORT void JNICALL Java_com_itheima_calljava_DataProvider_methodInC03
(JNIEnv * env, jobject obj){
//1.查找以class字节码文件
jclass clazz = (*env)->FindClass(env,"com/itheima/calljava/DataProvider");
//2.查找到方法
jmethodID mid = (*env)->GetStaticMethodID(env,clazz,"staticMethodJava","()V");
//3.调用该静态方法
(*env)->CallStaticVoidMethod(env,clazz,mid);
}
调取带参数的方法(传入String,)
/**
* 用C代码去调用java代码中的方法,拼接一个从C传入的字符串
*/
JNIEXPORT void JNICALL Java_com_itheima_calljava_DataProvider_methodInC02
(JNIEnv * env, jobject obj) {
//1.查找到Class
jclass clazz = (*env)->FindClass(env,"com/itheima/calljava/DataProvider");
//2.查找到方法
jmethodID mid = (*env)->GetMethodID(env,clazz,"methodInjava02","(Ljava/lang/String;)Ljava/lang/String;");
//3.调用方法
(*env)->CallObjectMethod(env,obj,mid,(*env)->NewStringUTF(env,"小志"));
}
调取java中的本地方法和被C调用的方法不在同一个类中
/**
* 用C代码调用java代码中的方法,去发送一条短信(注意:这个实现的是MainActivity中的本地方法)
*/
JNIEXPORT void JNICALL Java_com_itheima_calljava_MainActivity_callMethodInC04
(JNIEnv * env, jobject obj){
//第一步:找到要调用的方法的class字节码文件
jclass clazz = (*env)->FindClass(env,"com/itheima/calljava/DataProvider");
//第二步:找到这个类中的方法.参数依次为:JVM虚拟机环境、class对象、方法名、方法签名(返回值+方法的形参)
jmethodID mid = (*env)->GetMethodID(env,clazz,"methodInjava","()V");
//由于要调用的方法不是MainActivity(当前对象)中的方法,也就不能使用当前的obj,要创建该方法所在类中的对象
//使用该类clazz字节码文并 使用默认的构造方法创建该对象(没有参数的构造)
jobject jobj = (*env)->AllocObject(env,clazz);
//第三步:调用这个方法
(*env)->CallVoidMethod(env,jobj,mid);
}
注意事项
- 一般在写本地方法时,都会单独的放在一个单独的类中,方便管理,不容易出错。
- 注意FindClass这个方法的使用,传入的类的全路径是用/分隔的
- 方法签名的获取(在命令提示符下)
- 进入到项目的bin/classes目录下
- 使用javap -s 类的全路径
- JNIEnv:代表当前的jvm虚拟机环境
- jobject:代表本地方法所在的类(也就是本地方法的调用者)