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:代表本地方法所在的类(也就是本地方法的调用者)