一,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
#include
#include
#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, "", "([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
#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");