Java和C++互相调用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/pengtgimust/article/details/82835258

一、JNI简介

JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。从Java1.1开始,JNI标准成为java平台的一部分,它允许Java代码和其他语言写的代码进行交互。JNI一开始是为了本地已编译语言,尤其是C和C++而设计的,但是它并不妨碍你使用其他编程语言,只要调用约定受支持就可以了。使用java与本地已编译的代码交互,通常会丧失平台可移植性。但是,有些情况下这样做是可以接受的,甚至是必须的。例如,使用一些旧的库,与硬件、操作系统进行交互,或者为了提高程序的性能。JNI标准至少要保证本地代码能工作在任何Java 虚拟机环境。

二、JNI资源

JNINativeMethod结构体中有一个字段为signature(签名),再介绍signature格式之前需要掌握各种数据类型在Java层、Native层以及签名所采用的签名格式。

数据类型

基本数据类型

Signature格式 Java Native
B byte jbyte
C char jchar
D double jdouble
F float jfloat
I int jint
S short jshort
J long jlong
Z boolean jboolean
V void void

数组数据类型

数组简称则是在前面添加[:

Signature格式 Java Native
[B byte[] jbyteArray
[C char[] jcharArray
[D double[] jdoubleArray
[F float[] jfloatArray
[I int[] jintArray
[S short[] jshortArray
[J long[] jlongArray
[Z boolean[] jbooleanArray

复杂数据类型

对象类型简称:L+classname +;

Signature格式 Java Native
Ljava/lang/String; String jstring
L+classname +; 所有对象 jobject
[L+classname +; Object[] jobjectArray
Ljava.lang.Class; Class jclass
Ljava.lang.Throwable; Throwable jthrowable

Signature

有了前面的铺垫,那么再来通过实例说说函数签名: (输入参数…)返回值参数,这里用到的便是前面介绍的Signature格式。

Java函数 对应的签名
void foo() ()V
float foo(int i) (I)F
long foo(int[] i) ([I)J
double foo(Class c) (Ljava/lang/Class;)D
boolean foo(int[] i,String s) ([ILjava/lang/String;)Z
String foo(int i) (I)Ljava/lang/String;

三、Java调用C++

咱们这里先创建一个AS工程如下图:
在这里插入图片描述
创建JavaNativeScheduler 类

package com.pengtg.jnidemo;
import android.util.Log;
public class JavaNativeScheduler {
    public static final String TAG = "JavaNativeScheduler";
    public showToastCallback mCallback;
    public JavaNativeScheduler(showToastCallback callback){
        mCallback = callback;
    }
    public native void setFlag(int flag); //创建setFlag 的native方法
    public void updateTextFromNative(int flag){ //此方法是供C++调用的
        Log.d(TAG,"I'm called from native!!! set flag success!!!");
        mCallback.showToast(flag);
    }
    interface showToastCallback{//MainActivity实现该接口
        void showToast(int flag);
    }
}

通过命令:javah -jni com.pengtg.jnidemo.JavaNativeScheduler 生成 jni 头文件com_pengtg_jnidemo_JavaNativeScheduler.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_pengtg_jnidemo_JavaNativeScheduler */
#include "../cpp/my_preview/preview_controller.h"

#ifndef _Included_com_pengtg_jnidemo_JavaNativeScheduler
#define _Included_com_pengtg_jnidemo_JavaNativeScheduler
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_pengtg_jnidemo_JavaNativeScheduler
 * Method:    setFlag
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_com_pengtg_jnidemo_JavaNativeScheduler_setFlag
  (JNIEnv *, jobject, jint);

#ifdef __cplusplus
}
#endif
#endif

根据com_pengtg_jnidemo_JavaNativeScheduler.h头文件,我们创建com_pengtg_jnidemo_JavaNativeScheduler.cpp
并实现Java_com_pengtg_jnidemo_JavaNativeScheduler_setFlag的JNI方法

JNIEXPORT void JNICALL Java_com_pengtg_jnidemo_JavaNativeScheduler_setFlag
  (JNIEnv *env, jobject obj, jint flag){
    controller = new MyController();
    JavaVM *g_jvm = NULL;
    env->GetJavaVM(&g_jvm);
    g_obj = env->NewGlobalRef(obj);
    controller->prepareContext(g_jvm,g_obj,flag);
}

这里我们创建了MyController对象,并调用了prepareContext方法。下面我们来看一下MyController的声明和定义部分。preview_controller.h

//
// Created by pengt on 2018/8/21.
//
#ifndef JNIDEMO_PREVIEW_CONTROLLER_H
#define JNIDEMO_PREVIEW_CONTROLLER_H
#include <jni.h>
#include <pthread.h>
class MyController {
public:
    MyController();
    virtual ~MyController();
    void prepareContext(JavaVM *g_jvm, jobject obj, int flag);
    static void *threadStartCallback(void *myself);
    void updateText();
protected:
    JavaVM *g_jvm;
    jobject obj;
    int flag;
    pthread_t _thread_id;
};
#endif //JNIDEMO_PREVIEW_CONTROLLER_H

preview_controller.cpp

// An highlighted block
//
// Created by pengt on 2018/8/21.
//
#include <jni.h>
#include <stdlib.h>
#include "preview_controller.h"
#include "../log.h"
#include <pthread.h>
#define LOG_TAG "MyController"

MyController::MyController() {
    LOGI("MyController instance created");
}
MyController::~MyController() {
    LOGI("MyController instance destroyed");
}
void MyController::prepareContext(JavaVM *g_jvm, jobject obj, int flag) {
    LOGI("MyController prepareContext");
    this->g_jvm = g_jvm;
    this->obj = obj;
    this->flag = flag;//至此java调用C++将flag值赋给了C++层
    pthread_create(&_thread_id, NULL, threadStartCallback, this);//
}
void *MyController::threadStartCallback(void *myself) {
    MyController *controller = (MyController *)myself;
    controller->updateText();
    pthread_exit(0);
}

void MyController::updateText() {
    LOGI("MyController updateText");
    JNIEnv *env;
    if(g_jvm->AttachCurrentThread(&env, NULL) != JNI_OK){
        LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
        return;
        //goto error;
    }
    g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4);
    if(env == NULL){
        LOGI("get JNIEnv failed");
        return;
        //goto error;
    }
    jclass jcls = env->GetObjectClass(obj);
    if(NULL != jcls){
        jmethodID updateTextCallback = env->GetMethodID(jcls,"updateTextFromNative", "(I)V");
        if(NULL != updateTextCallback){
            env->CallVoidMethod(obj,updateTextCallback,flag);
        }
    }
//error:
    if(g_jvm->DetachCurrentThread() != JNI_OK){
        LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
    }
    pthread_exit(0);
}

接下来我们在MainActivity 中进行java的JNI调用:

package com.pengtg.jnidemo;
import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements JavaNativeScheduler.showToastCallback {
    public Context mContext;
    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mContext = this;
        // Example of a call to a native method
        TextView tv = (TextView) findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());
        JavaNativeScheduler scheduler = new JavaNativeScheduler(this);
        scheduler.setFlag(1);
    }
    @Override
    public void showToast(final int flag) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(mContext,"set flag: " + flag + " success!!!", Toast.LENGTH_LONG).show();
            }
        });
    }
}

四、C++调用Java

void MyController::prepareContext(JavaVM *g_jvm, jobject obj, int flag) {
    LOGI("MyController prepareContext");
    this->g_jvm = g_jvm; //JavaVM:是指进程虚拟机环境,每个进程有且只有一个JavaVM实例
    this->obj = obj; //JNIEnv:是指线程上下文环境,每个线程有且只有一个JNIEnv实例。
    //obj是在JNI方法Java_com_pengtg_jnidemo_JavaNativeScheduler_setFlag中赋值过来的env->NewGlobalRef(obj)
    this->flag = flag;//至此java调用C++将flag值赋给了C++层
    pthread_create(&_thread_id, NULL, threadStartCallback, this);//我们这里创建新的线程用来执行调用Java方法。
}
void *MyController::threadStartCallback(void *myself) {
    MyController *controller = (MyController *)myself;
    controller->updateText();
    pthread_exit(0);
}

void MyController::updateText() {
    LOGI("MyController updateText");
    JNIEnv *env;
    if(g_jvm->AttachCurrentThread(&env, NULL) != JNI_OK){
        LOGE("%s: AttachCurrentThread() failed", __FUNCTION__);
        return;
        //goto error;
    }
    g_jvm->GetEnv((void**) &env, JNI_VERSION_1_4);//获取上下文JNIEnv 
    if(env == NULL){
        LOGI("get JNIEnv failed");
        return;
        //goto error;
    }
    jclass jcls = env->GetObjectClass(obj);//通过赋值过来的对象获得native类
    if(NULL != jcls){
        jmethodID updateTextCallback = env->GetMethodID(jcls,"updateTextFromNative", "(I)V");//获取Java类方法updateTextFromNative
        if(NULL != updateTextCallback){
            env->CallVoidMethod(obj,updateTextCallback,flag);//调用Java方法
        }
    }
//error:
    if(g_jvm->DetachCurrentThread() != JNI_OK){
        LOGE("%s: DetachCurrentThread() failed", __FUNCTION__);
    }
    pthread_exit(0);
}

五、实例源码地址

最后方便大家一起学习交流,附上完整的工程源码。
Java和C++相互调用:JniDemo

猜你喜欢

转载自blog.csdn.net/pengtgimust/article/details/82835258