基于Android Studio NDK开发

Rhoda ·
更新时间:2024-11-10
· 978 次阅读

1.  开发环境配置

Android Studio3.0.1
android-ndk-r13
gradle插件: classpath 'com.android.tools.build:gradle:3.0.1'
gradle:distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
  插件:
  Android NDK Support
  Cmake simple highlighter
  SDk Tools :LLDB  
 博主家里的就是这样,写c++有提示,但是公司开发环境类似,写C++木有提示??,没有提示???

2. NKD字符串操作:

   MainActivity申明:

/** * 1. 传递字符粗 * A native method that is implemented by the 'native-lib' native library, * which is packaged with this application. * 字符粗 */ public native String stringFromJNI();

native-lib.cpp 实现

#include #include #include #include extern "C" JNIEXPORT jstring /* 可变参数... 在末尾使用 __VA_ARGS__ 代替**/ #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "JNI", __VA_ARGS__) // 字符串 JNICALL Java_lanya_denganzhi_com_ndk_MainActivity_stringFromJNI( JNIEnv *env, jobject /* this */) { // char* =》jstring std::string hello = "Hello from C++"; jstring str=env->NewStringUTF(hello.c_str()); // jstring->char* const char* s= env->GetStringUTFChars(str,0); LOGE("转化的char*是:%s",s); env->ReleaseStringUTFChars(str,s); return str; } 3.数组操作 public void testArr(View view){ int a[]={1,2}; arrayEncode(a); Toast.makeText(MainActivity.this, Arrays.toString(a),Toast.LENGTH_SHORT).show(); } /** * 2.传递数组 * @param arr */ public native void arrayEncode(int[] arr); // 数组 extern "C" JNIEXPORT void JNICALL Java_lanya_denganzhi_com_ndk_MainActivity_arrayEncode(JNIEnv *env, jobject instance, jintArray arr_) { /** * 第二个参数: 指针,指向内存地址 * true 表示重新申请一块内存保存数据 * false: 表示使用java 数组的内存地址 */ jint *arr = env->GetIntArrayElements(arr_, NULL); int length = env->GetArrayLength( arr_); for(int i=0;iReleaseIntArrayElements(arr_, arr, 0); } 4.  参数传递对象,  C调用Java  [Java反射使用] package lanya.denganzhi.com.ndk; import android.util.Log; /** * Created by Administrator on 2020/2/23. */ public class Bean { private int i; public int getI() { Log.e("denganzhi","invoke-java-getI"); return i; } public void setI(int i) { Log.e("denganzhi","invoke-java-setI"); this.i = i; } public static void printInfo(String msg){ Log.e("denganzhi1",msg+""); } } package lanya.denganzhi.com.ndk; /** * Created by Administrator on 2020/2/23. */ public class Bean2 { private int i; public Bean2(int i) { this.i = i; } public Bean2() { } }

 声明Native方法:

public void testArr(View view){ Bean bean=new Bean(); passObject(bean,"test---bean"); } /** * 3. 传递对象 */ public native void passObject(Bean bean, String string);

C++调用: 

// instance: java中MainActivity bean: java中的bean // 反射运用 extern "C" JNIEXPORT void JNICALL Java_lanya_denganzhi_com_ndk_MainActivity_passObject(JNIEnv *env, jobject instance, jobject bean, jstring string_) { const char *string = env->GetStringUTFChars(string_, 0); // C 中通过放射 来获取Java的属性、调用Java的方法 // 反射调用 Java方法 jclass beanCls= env->GetObjectClass(bean); // 签名格式: (参数签名)返回值签名 // 获取method jmethodID gidI= env->GetMethodID(beanCls,"getI","()I"); jmethodID setI= env->GetMethodID(beanCls,"setI","(I)V"); // bean.setI(2000) 这里相当于 env->CallVoidMethod(bean,setI,2000); // 调用方法, 返回值是int的call. Int表示方法返回值 // 这里调用get方法 jint result= env->CallIntMethod(bean,gidI); LOGE("bean---%d",result); // 调用static方法,类的 // public static void printInfo(String msg) jmethodID printInfo= env->GetStaticMethodID(beanCls,"printInfo","(Ljava/lang/String;)V"); jstring jstr2= env->NewStringUTF("show printfl"); env->CallStaticVoidMethod(beanCls,printInfo,jstr2); // 反射获取属性 // private int i; // int i= 1000; jfieldID jfieldID1 =env->GetFieldID(beanCls,"i","I"); env->SetIntField(bean,jfieldID1,1000000); jint jint1= env->GetIntField(bean,jfieldID1); LOGE("放射获取属性---%d",jint1); // 反射构建对象 jclass bean2Class=env->FindClass("lanya/denganzhi/com/ndk/Bean2"); // 获取构造方法 , 默认构造方法的 名称和 签名 jmethodID construct =env->GetMethodID(bean2Class,"","(I)V"); // 构造方法newInstance // bean2 是 c++中的引用 new Bean2(9999) jobject bean2= env->NewObject(bean2Class,construct,9999); jfieldID jfieldID2Bean2= env->GetFieldID(bean2Class,"i","I"); jint jint3= env->GetIntField(bean2,jfieldID2Bean2); // 获取i属性 LOGE("构造出来的bean2是---%d",jint3); env->DeleteLocalRef(jstr2); env->DeleteLocalRef(bean2Class); env->DeleteLocalRef(bean2); env->ReleaseStringUTFChars(string_, string); }
5. 全局引用(强引用)  与 弱引用
强应用,可以跨栈使用 弱引用和强引用比较,使用的时候可能会被系统回收弱引用 public void testArr(View view){ invokeStronRef1(); } /** * 4. 强引用 */ public native void invokeStronRef1(); jclass beanStrongClass=NULL; extern "C" JNIEXPORT void JNICALL Java_lanya_denganzhi_com_ndk_MainActivity_invokeStronRef1(JNIEnv *env, jobject instance) { // TODO // 反射构建对象 jclass bean2Class=env->FindClass("lanya/denganzhi/com/ndk/Bean2"); // 把它变成全局引用,强引用 // beanStrongClass = (jclass) env->NewGlobalRef(bean2Class); // 弱全局引用 beanStrongClass = (jclass) env->NewWeakGlobalRef(bean2Class); // 获取构造方法 , 默认构造方法的 名称和 签名 jmethodID construct =env->GetMethodID(beanStrongClass,"","(I)V"); // 构造方法newInstance // bean2 是 c++中的引用 jobject bean2= env->NewObject(beanStrongClass,construct,9999); jfieldID jfieldID2Bean2= env->GetFieldID(beanStrongClass,"i","I"); jint jint3= env->GetIntField(bean2,jfieldID2Bean2); LOGE("invokeStronRef1---%d",jint3); }

上面方法执行以后,beanStrongClass 没有被释放,第二次调用

public void testArr2(View view){ invokeStronRef2(); } public native void invokeStronRef2(); extern "C" JNIEXPORT void JNICALL Java_lanya_denganzhi_com_ndk_MainActivity_invokeStronRef2(JNIEnv *env, jobject instance) { // 对应一个弱引用,可能会被内存回收 // 如何判断是否被内存回收 // true,表示被释放了 jboolean isEqual= env->IsSameObject(beanStrongClass,NULL); if(isEqual){ LOGE("true"); }else{ LOGE("false"); } // 没有被释放 if(beanStrongClass!=NULL && !isEqual){ jmethodID construct =env->GetMethodID(beanStrongClass,"","(I)V"); // 构造方法newInstance // bean2 是 c++中的引用 // new Bean2() jobject bean2= env->NewObject(beanStrongClass,construct,9999); jfieldID jfieldID2Bean2= env->GetFieldID(beanStrongClass,"i","I"); // 获取i 属性 jint jint3= env->GetIntField(bean2,jfieldID2Bean2); // 不使用了,释放全局应用 // env->DeleteGlobalRef(beanStrongClass); env->DeleteWeakGlobalRef(beanStrongClass); // 必须置空,否则悬空指针,内存被释放了,但是指针还在 beanStrongClass=NULL; LOGE("invokeStronRef2---%d",jint3); }else{ // 释放了 LOGE("invokeStronRef2---%s", "已经被释放了"); } // 获取构造方法 , 默认构造方法的 名称和 签名 }
7.  动态注册:

JNI_OnLoad  Java方法和 C方法注册绑定, 上面的1-6都是静态注册

// 动态注册 public void staticRegester(View view){ invokeStatic(988888); } /** * 5. 动态注册 */ public native void invokeStatic(int a); /*** * 静态调用 */ #if 0 typedef struct { char *name; /* Java里调用的函数名 */ char *signature; /* Java 方法签名 */ void *fnPtr; /* C语言实现的本地函数 */ } JNINativeMethod; #endif // 对应映射的C方法 void invokeStaticC(JNIEnv *env, jobject cls,jint i){ LOGE("invokeStaticC:%d",i); } // 结构体数组 static const JNINativeMethod methods[] = { {"invokeStatic", "(I)V", (void *)invokeStaticC}, }; JavaVM * _vm; JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { _vm=jvm; LOGE("JNI_OnLoad---start"); JNIEnv *env ; // jvm java虚拟机获取 JNIEnv // 0 成功 if (jvm->GetEnv((void**)&env , JNI_VERSION_1_6) !=JNI_OK) { return JNI_ERR; /* JNI version not supported */ } jclass cls; cls = env->FindClass("lanya/denganzhi/com/ndk/MainActivity"); if (cls == NULL) { return JNI_ERR; } /* 2. map java hello c c_hello */ /* Java方法和 C方法 映射 */ jint methodCount= sizeof(methods)/sizeof(JNINativeMethod); LOGE("methodCount:%d",methodCount); if(env->RegisterNatives(cls,methods,methodCount)<0){ return JNI_ERR; } LOGE("JNI_OnLoad---end"); return JNI_VERSION_1_6; }  8. 切换线程

在新线程中获取  JNIEnv* 只能通过  JavaVM, 不能通过结构体传递

public void switchThreadMethod(View view){ switchThread(); } /** * 6. JNIEnv *env 不能跨跨线程 没有了,如何获取 */ public native void switchThread(); // C中 新线程调用Java中的 updateUI方法 public void updateUI(){ if(Looper.myLooper()== Looper.getMainLooper()){ Toast.makeText(MainActivity.this,"更新UI1",Toast.LENGTH_SHORT).show(); }else{ runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(MainActivity.this,"更新UI2",Toast.LENGTH_SHORT).show(); } }); } } struct Person{ //jobject instance; // JNIEnv *env; int age; }; void* threadTask(void* args){ // env 切换线程,只能通过 _vm 构建 一个 env 环境 // 不能通过 结构体 传递 Env对象 JNIEnv* env; if(_vm->AttachCurrentThread(&env,0) != JNI_OK){ }; //Person* uperson = static_cast(args); jobject instance = static_cast(args); // struct Person* p= (struct Person*)(args); // LOGE("person.age...%d",p->age); // jobject instance = static_cast(p->instance); // 获取 MainActivity的 jclass对象 jclass beanCls= env->GetObjectClass(instance); // 获取methodID jmethodID updateUI= env->GetMethodID(beanCls,"updateUI","()V"); env->CallVoidMethod(instance,updateUI); // delete(instance); env->DeleteGlobalRef(instance); //分离 _vm->DetachCurrentThread(); return 0; } extern "C" JNIEXPORT void JNICALL Java_lanya_denganzhi_com_ndk_MainActivity_switchThread(JNIEnv *env, jobject instance) { // Person* person=new Person; // person->env= env; // person->instance = env->NewGlobalRef(instance); // instance 就是MainActivity的jobject // jinstance2 不能通过结构体成员传递,无效 jobject jinstance2 = env->NewGlobalRef(instance); struct Person p= { 67 }; pthread_t pid; // 启动线程, env 如何跨线程传递 pthread_create(&pid,0,threadTask,(void*)jinstance2); } 归纳: **** JNI 局域引用: 使用NewObject/FindClass/NewStringUTF 属于局部应用,出了方法,jvm回收无效 1. 方法执行完毕vm自动释放 2. 使用 NewStringUTF->ReleaseStringUTFChars FindClass/NewStringUTF->DeleteLocalRef 上面的不释放也可以,在栈中 *** JNI 全局应用:可以跨方法/线程 *** 弱全局引用 强应用,可以跨栈使用 和强引用比较,使用的时候可能会被系统回收弱引用 必须要手动释放
作者:小置同学



Android Studio studio ndk Android

需要 登录 后方可回复, 如果你还没有账号请 注册新账号