一,Java 調用 C
1, 首先我們創建一個文件名字叫做,JNI。其實你不創建也行。看自己
public class JNI {
//加載本地C語言文件庫。庫名字爲你寫的C語言文件名
static {
System.loadLibrary("Hello");
}
//todo: java 調用 C =======
public native String stringFromJNI();
//相加
public native int numberTest(int a,int b);
//字符串拼接 java 和 C
public native String strOrStr(String str);
// 在C中數組中的每一個元素+10 然後返回到 java;
public native int [] getArray(int array[]);
}
2, 再次來到Cpp文件裏
#include <jni.h>
#include <string>
#include<android/log.h>
#define LOG_TAG "nativeprint"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__
// java String 轉成C char
char *jstringTostring(JNIEnv *env, jstring jstr);
#pragma clang diagnostic push
#pragma ide diagnostic ignored "err_typecheck_member_reference_arrow"
extern "C" JNIEXPORT jstring JNICALL
Java_com_cwj_ndkc_JNI_stringFromJNI(JNIEnv *env, jobject) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_cwj_ndkc_JNI_numberTest(JNIEnv *env, jobject thiz, jint a, jint b) {
// TODO: implement numberTest()
int result = a + b;
return result;
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_cwj_ndkc_JNI_strOrStr(JNIEnv *env, jobject thiz, jstring str) {
// TODO: implement strOrStr()
char *javas = jstringTostring(env, str);
char *cc = "我是C語言中的char*";
strcat(javas, cc);
// return (*env)->NewStringUTF(env,javas);
return env->NewStringUTF((char *) javas);
}
//char* 轉成String
jstring stoJstring(JNIEnv *env, const char *pat) {
jclass strClass = env->FindClass("java/lang/String");
jmethodID ctorID = env->GetMethodID(strClass, "<init>", "([BLjava/lang/String;)V");
jbyteArray bytes = env->NewByteArray(strlen(pat));
env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte *) pat);
jstring encoding = env->NewStringUTF("utf-8");
return (jstring) env->NewObject(strClass, ctorID, bytes, encoding);
}
// java String 轉成C char
char *jstringTostring(JNIEnv *env, jstring jstr) {
char *rtn = NULL;
jclass clsstring = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("utf-8");
jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);
jsize alen = env->GetArrayLength(barr);
jbyte *ba = env->GetByteArrayElements(barr, JNI_FALSE);
if (alen > 0) {
rtn = (char *) malloc(alen + 1);
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
env->ReleaseByteArrayElements(barr, ba, 0);
return rtn;
}
extern "C"
JNIEXPORT jintArray JNICALL
Java_com_cwj_ndkc_JNI_getArray(JNIEnv *env, jobject thiz, jintArray array) {
// TODO: implement getArray()
//1, 得到數組的長度
int lenght = env->GetArrayLength(array);
//2,得到數組的元素
jint *intArray = env->GetIntArrayElements(array,0);
//3,遍歷數組順便加10
int i;
for (i = 0; i < lenght; ++i) {
*intArray+=10;
}
return array;
}
注意: 觀察,Java Native方法,再看C++裏面的實現。會發現,
C++的方法名字:是Java+JavaNative接口包名+接口名的方式生成的,對,這個不用自己寫。直接根據AS提示,自動創建。
3,在Activity中調用。先看看目錄
case R.id.btn_java:
//java調用C
jni.strOrStr("參數");
二,C++調用Java
1,首先要有一個java 方法吧,然後C調用java了 ,最後,還是要回到Activity中去調用。 這裏只是把java方法和native接口寫在了一個類中。
//todo: C 調用java=======================================================
public int addJava(int x,int y){
Log.e(TAG, "addJava() x=" + x + " y=" + y);
return x+y;
}
public native void callAdd();
//todo:C c調用靜態java方法
public static void javaString(String jstr){
Log.e(TAG, "javaString() " +jstr);
}
public native void javaStringCall();
2,Cpp代碼
//=============================================todo:===================== C調用java
extern "C"
JNIEXPORT void JNICALL
Java_com_cwj_ndkc_JNI_callAdd(JNIEnv *env, jobject thiz) {
// TODO: implement callAdd()
//要用到反射
//1,得到字節碼
jclass jclass1=env->FindClass("com/cwj/ndkc/JNI");
//2,得到方法
jmethodID jmethodId=env->GetMethodID(jclass1,"addJava","(II)I");
//3,實例化該類
jobject jobject=env->AllocObject(jclass1);
//4,調用方法,得到結果
jint jint1= env->CallIntMethod(jobject,jmethodId,99,1);
}
//調用靜態java方法
extern "C"
JNIEXPORT void JNICALL
Java_com_cwj_ndkc_JNI_javaStringCall(JNIEnv *env, jobject thiz) {
// TODO: implement javaStringCall()
//1,得到字節碼
jclass jclass1=env->FindClass("com/cwj/ndkc/JNI");
//2.得到方法 字節碼,方法名,方法簽名
jmethodID jmethodId=env->GetStaticMethodID(jclass1,"javaString","(Ljava/lang/String;)V");
//3,調用方法
jstring jst=env->NewStringUTF("這個是java靜態方法參數");
env -> CallStaticVoidMethod(jclass1,jmethodId,jst);
printf("日誌:=======");
__android_log_print(ANDROID_LOG_INFO,"日誌", "aa");
}
3,最後調用方式是一樣的。
case R.id.btn_c:
//C調用java
jni.callAdd();//C
jni.javaStringCall();//C
注意:學這個之前先把C++ 看一遍。其實語法差不多,最大的區別是,C++的指針,和結構體,這倆要多看幾遍
三,C調用Activity中的方法,更新UI
Activity ==================
public native void showToastCall();
public void showToast(){
Toast.makeText(this, "C++調用我了", Toast.LENGTH_SHORT).show();
}
Cpp==================
extern "C"
JNIEXPORT void JNICALL
/**
*
* @param env
* @param thiz 這個代表的Activity ,不需要實例化
*/
Java_com_cwj_ndkc_MainActivity_showToastCall(JNIEnv *env, jobject thiz) {
// TODO: implement showToastCall()
//要用到反射
//1,得到字節碼
jclass jclass1=env->FindClass("com/cwj/ndkc/MainActivity");
//2,得到方法
jmethodID jmethodId=env->GetMethodID(jclass1,"showToast","()V");
//3,實例化該類
// jobject jobject=env->AllocObject(jclass1);
//4,調用方法,得到結果
env->CallVoidMethod(thiz,jmethodId);
}
Activity調用=================================
MainActivity.this.showToastCall();
四,日誌的配置
這個放到你的Cpp文件中頂部。
#include<android/log.h>
#define LOG_TAG "nativeprint"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__
build.gradle
defaultConfig {
applicationId "com.cwj.ndkc"
minSdkVersion 24
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags " "
}
ndk {
// Specifies the ABI configurations of your native
// libraries Gradle should build and package with your APK.
// abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',
// 'arm64-v8a'
ldLibs "log"
}
}
使用: __android_log_print(ANDROID_LOG_INFO,"日誌", "aa");