Android JNI 调用

简介: Android JNI 调用

1.Android Studio创建native项目

 

对项目进行解释

cmake_minimum_required(VERSION 3.10.2)
# Declares and names the project.
project("jnitest")
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
        # 设置生成.so 的文件名,也是你在 java 代码里调用的名字,填一个就好了,记住!!
        jnitest
        # Sets the library as a shared library.
        # 设置库的类型 一种静态文件 STATIC .a 一种动态文件 SHARED .so
        SHARED
        # Provides a relative path to your source file(s).
        # 需要编译的c/c++ 文件,这里填相对路径
        native-lib.cpp
        test.c)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
        log-lib
        # Specifies the name of the NDK library that
        # you want CMake to locate.
        log)
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
        # 指定链接的目标库
        jnitest
        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

2.创建完成后,就是标准的Native 项目,MainActivity里有 java 调 c 的例子

/**
     * A native method that is implemented by the 'jni' native library, which is packaged with this
     * application.
     */
    public native String stringFromJNI();
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_jni_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

3.创建自定义log

#ifndef JNITEST_LOG_H
#define JNITEST_LOG_H
#define LOG_TAG "jniTest"
//#endif //JNITEST_LOG_H
//#ifndef LOGGING_H
//#define LOGGING_H
#include <android/log.h>
//定义TAG之后,我们可以在LogCat通过TAG过滤出NDK打印的日志
// 定义debug信息
#define LOGD(TAG, ...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
// 定义info信息
#define LOGI(TAG, ...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__)
// 定义warn信息
#define LOGW(TAG, ...) __android_log_print(ANDROID_LOG_WARN,TAG,__VA_ARGS__)
// 定义error信息
#define LOGE(TAG, ...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)
#endif //LOGGING_H

使用   LOGI(LOG_TAG,"CallIntMethod %d",ret);


4.c 调java

总体流程

1.获取jclass

2.获取jmethodid

3.获取jclass对象

4.对象调用方法

 

详细步骤

获取你需要访问的Java对象的类

如果被Native调用的Java类是静态类:FindClass通过传java中完整的类名来查找java的class

jclass jclazz = (*env).FindClass("com/example/jnitest/Test");

如果是非静态类:GetObjectClass通过传入jni中的一个java的引用来获取该引用的类型

env->GetObjectClass(thiz)

他们之间的区别是,前者要求你必须知道完整的类名,后者要求在Jni有一个类的引用

获取调用方法的MethodID

GetMethodID 得到一个实例的方法的ID

GetStaticMethodID 得到一个静态方法的ID

获取对象的属性

GetFieldID 得到一个实例的域的ID

GetStaticFieldID 得到一个静态的域的ID

JNI通过ID识别域和方法,一个域或方法的ID是任何处理域和方法的函数的必须参数

环境

jni里面调用java方法的环境分为2种:

第一种:在env所在线程调用java方法,这种情况不需要做特殊处理,直接按照步骤执行即可

第二种:在pthread子线程调用java方法,这种情况下就需要做处理了

在jni中,子线程中是不能直接调用JNIEnv对象的,也不能直接调用env线程中的jobject对象。因为jni中,JNIEnv是和线程相关的,每一个native方法所在线程就有一个当前线程相关的JNIEnv对象,而pthread线程中是不能调用native方法所在线程的JENnv对象的。

解决办法是:利用JavaVM虚拟机

JavaVM是和进程相关的,一个进程里面的JavaVM都是同一个,所以在pthread线程中就可以通过JavaVM来获取(AttachCurrentThread)当前线程的JNIEnv指针,然后就可以使用JNIEnv指针操作数据了。

还有在pthread线程中调用jobject对象时,首先需要把native线程里面的jobject创建全局引用(env->NewGlobalRef(jobj)),其返还的jobject对象就可以在程序中使用了,具体的调用函数的代码和函数的返回值相关,对应规则如下:

static JavaVM *mJavaVm = NULL;
static jclass mClass = NULL;
static bool isInitialed = false;
static inline JNIEnv *JNI_OnLoad(bool *needsDetach) {
    *needsDetach = false;
    JNIEnv *env = NULL;
    int status = mJavaVm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_4);
    if (status < 0) {
        JavaVMAttachArgs args = {JNI_VERSION_1_4, NULL, NULL};
        int result = mJavaVm->AttachCurrentThread(&env, (void *) &args);
        if (result != JNI_OK) {
            TX_DBG("thread attach failed: %#x", result);
            return NULL;
        }
        *needsDetach = true;
    }
    return env;
}
static inline void detachJNI() {
    int result = mJavaVm->DetachCurrentThread();
    if (result != JNI_OK) {
        TX_DBG("thread detach failed: %#x", result);
    }
}
static void skTr069JniConfig(JNIEnv *env) {
    jclass clazz;
    env->GetJavaVM(&mJavaVm);
    if (NULL == (clazz = env->FindClass(JNI_REG_CLASS))) {
        TX_ERR("call FindClass(%s) failed", JNI_REG_CLASS);
        return;
    }
    mClass = reinterpret_cast<jclass> (env->NewGlobalRef(clazz));
    for (int ii = 0; ii < ARRAY_SIZE(sFuncScript); ii++) {
        if (NULL == (sJavaFunction[ii] = env->GetStaticMethodID(mClass, sFuncScript[ii].name, sFuncScript[ii].type))) {
            TX_ERR("call GetStaticMethodID %s(%s) failed", sFuncScript[ii].name, sFuncScript[ii].type);
            return;
        }
    }
//env->DeleteGlobalRef(mClass);
}

 总结:

1.在JNI_OnLoad中,保存JavaVM*,这是跨线程的,持久有效的,而JNIEnv*则是当前线程有效的。一旦启动线程,用AttachCurrentThread方法获得env。

2.通过JavaVM*和JNIEnv可以查找到jclass。

3.把jclass转成全局引用,使其跨线程。

4.然后就可以正常地调用你想调用的方法了。

5.用完后,别忘了delete掉创建的全局引用和调用DetachCurrentThread方法。

 

在java中创建要被调用的java方法和native方法

public native int callAge();
    public int getAge(){
        Log.e(TAG, "getAge: 我被C语言调用了");
        return 20;
    }
public class Test {
    public native String callName();
    public String getName(){
        Log.e("TAG", "getAge: 我被C语言调用了");
        return "wang";
    }
}

在native里通过 反射的方式调用java方法

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_jnitest_MainActivity_callAge(JNIEnv *env, jobject thiz) {
    // TODO: implement callAge()
    //1、获取字节码
    jclass jclazz = (*env).FindClass("com/example/jnitest/MainActivity");
    //2、获取方法
    jmethodID jmethodId = (*env).GetMethodID(jclazz,"getAge","()I");
    //3、实例化对象
    jobject jobject1 = (*env).AllocObject(jclazz);
    //4、调用方法
    //jstring jstr = (*env).NewStringUTF("这句话来自C");
    int ret = (*env).CallIntMethod(jobject1,jmethodId);
    LOGI(LOG_TAG,"CallIntMethod %d",ret);
    return ret;
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_example_jnitest_Test_callName(JNIEnv *env, jobject thiz) {
    // TODO: implement callName()
    const char *pStr = NULL;
    //1、获取字节码
    jclass jclazz = (*env).FindClass("com/example/jnitest/Test");
    //2、获取方法
    jmethodID jmethodId = (*env).GetMethodID(jclazz,"getName","()Ljava/lang/String;");
    //3、实例化对象
    jobject jobject1 = (*env).AllocObject(jclazz);
    //4、调用方法
    //jstring jstr = (*env).NewStringUTF("这句话来自C");
    auto ret = (jstring)(*env).CallObjectMethod(jobject1,jmethodId);
    if (ret) pStr = env->GetStringUTFChars(ret, NULL);
    LOGI(LOG_TAG,"CallIntMethod %s",pStr);
    env->ReleaseStringUTFChars(ret, pStr);
    //env->GetObjectClass(thiz)
    return ret;
}


目录
相关文章
|
7月前
|
Android开发
Android JNI与CAN通信遇到的问题总结
Android JNI与CAN通信遇到的问题总结
271 1
|
7月前
|
Android开发
Android JNI 报错(signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr )
Android JNI 报错(signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr )
1052 1
|
4月前
|
Java Android开发 C++
Android Studio JNI 使用模板:c/cpp源文件的集成编译,快速上手
本文提供了一个Android Studio中JNI使用的模板,包括创建C/C++源文件、编辑CMakeLists.txt、编写JNI接口代码、配置build.gradle以及编译生成.so库的详细步骤,以帮助开发者快速上手Android平台的JNI开发和编译过程。
321 1
|
7月前
|
Java 开发工具 Android开发
OpenCV(一):Android studio jni配置OpenCV(亲测有效,保姆级)
OpenCV(一):Android studio jni配置OpenCV(亲测有效,保姆级)
835 0
|
Java Android开发
Android JNI开发从0到1,java调C,C调Java,保姆级教程详解
Android JNI开发从0到1,java调C,C调Java,保姆级教程详解
91 1
|
Java Android开发 C++
Android中的JNI开发,你了解多少?
Android中的JNI开发,你了解多少?
99 0
|
7月前
|
传感器 Java 开发工具
[NDK/JNI系列03] Android Studio集成NDK开发环境
[NDK/JNI系列03] Android Studio集成NDK开发环境
72 0
|
Java Android开发
[Android JNI] --- JNI基础(下)
[Android JNI] --- JNI基础(下)
85 0
|
7月前
|
Android开发
[Android jni] Bitmap与Mat对象的相互转换
[Android jni] Bitmap与Mat对象的相互转换
229 0
|
7月前
|
Java 开发工具 Android开发
[Android]JNI的基础知识
[Android]JNI的基础知识
118 0
[Android]JNI的基础知识