做NDK項目,不僅會用到java層通過jni調用C++層函數的情況,也會有需要C++可以調用java層函數的需求,所以我簡單整理了C++反調java函數的流程,直接進入正文:
一、創建java類,聲明native函數、用於可供C++調用的函數:
NDKUtils.java
package com.cwang.ndkdemo;
import android.util.Log;
/**
* @author: cwang
* @time: 2020/5/15 15:51
* @Description:
*/
public class NDKUtils {
private final String Tag = "NDKUtils";
// 加載SO
static {
System.loadLibrary("NDKUtils");
}
/**
* 構造函數
*/
public NDKUtils() {
super();
}
/**
* 該初始化函數裏完成對JavaVM的初始化
*/
public native void init();
/**
* 供C++調用的函數,java層傳輸一個字符串
* @param str
*/
public void strFromC(String str) {
Log.i(Tag, "這是來自C++層的字符串 = " + str);
}
}
二、生成NDKUtils.java對應jni的.h和.cpp
NDKUtils.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_cwang_ndkdemo_NDKUtils */
#ifndef _Included_com_cwang_ndkdemo_NDKUtils
#define _Included_com_cwang_ndkdemo_NDKUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_cwang_ndkdemo_NDKUtils
* Method: init
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_cwang_ndkdemo_NDKUtils_init
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif
NDKUtils.cpp
#include "NDKUtils.h"
#include "LogUtil.h"
JavaVM *gJavaVM = NULL;
jobject G_obj = NULL;
bool CStrFromC(const char *CStr);
/*
* Class: com_cwang_ndkdemo_NDKUtils
* Method: init
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_cwang_ndkdemo_NDKUtils_init
(JNIEnv * env, jclass obj) {
if (gJavaVM == NULL) env->GetJavaVM(&gJavaVM);
G_obj = env->NewGlobalRef(obj);
if (CStrFromC("我是一個來自C++層的字符串")) {
LOGI("CStrFromC Success");
}
}
/**
* 調用java層函數向java層傳輸一個字符串
* @return 是否調用成功
*/
bool CStrFromC(const char *CStr) {
//獲取obj中對象的class對象
if(gJavaVM == NULL) return false;
LOGD("CStrFromC");
JNIEnv *G_env = NULL;
bool isAttached = false;
int status = gJavaVM->GetEnv((void **) (&G_env), JNI_VERSION_1_4);
if (status < 0) {
gJavaVM->AttachCurrentThread(&G_env,NULL);
isAttached = true;
}
if (!G_env) return false;
if (!G_obj) return false;
//到java代碼的 class文件
jclass clazz = G_env->GetObjectClass(G_obj);
{
jthrowable exc = G_env->ExceptionOccurred();
if (exc) {
G_env->ExceptionClear();
G_env->DeleteLocalRef(clazz);
return false;
}
}
if (clazz == 0) {
G_env->DeleteLocalRef(clazz);
if (isAttached)
gJavaVM->DetachCurrentThread();
return false;
}
// 尋找class裏面的方法
jmethodID id_StrFromC = G_env->GetMethodID(clazz, "strFromC", "(Ljava/lang/String;)V");
{
jthrowable exc = G_env->ExceptionOccurred();
if (exc) {
G_env->ExceptionClear();
G_env->DeleteLocalRef(clazz);
if (isAttached)
gJavaVM->DetachCurrentThread();
return false;
}
}
if (id_StrFromC == 0) {
G_env->DeleteLocalRef(clazz);
if (isAttached)
gJavaVM->DetachCurrentThread();
return false;
};
jstring utf8CStr = G_env->NewStringUTF(CStr);
//調用strFromC方法
G_env->CallVoidMethod(G_obj, id_StrFromC, utf8CStr);
G_env->DeleteLocalRef(clazz);
G_env->DeleteLocalRef(utf8CStr);
return true;
}
三、NDKUtils類的初始化
MainActivity.java
package com.cwang.ndkdemo;
import android.app.Activity;
import android.os.Bundle;
/**
* @author cwang
* @time: 2020/5/15 15:51
* @Description:NDK Jni
*/
public class MainActivity extends Activity {
private NDKUtils ndkUtils;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ndkUtils = new NDKUtils();
ndkUtils.init();
}
}
四、當然不要忘記CMakeLists.txt裏的配置
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
# 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.
NDKUtils
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
NDKUtils.cpp
JniStrUtils.cpp)
# 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.
NDKUtils
# Links the target library to the log library
# included in the NDK.
${log-lib})