文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

基于Android Studio NDK开发

2022-06-06 13:30

关注

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申明:

 
    public native String stringFromJNI();

native-lib.cpp 实现


#include 
#include 
#include 
#include 
extern "C"
JNIEXPORT jstring

#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "JNI", __VA_ARGS__)
// 字符串
JNICALL
Java_lanya_denganzhi_com_ndk_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject ) {
    // 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();
    }
 
    public native void arrayEncode(int[] arr);

// 数组
extern "C"
JNIEXPORT void JNICALL
Java_lanya_denganzhi_com_ndk_MainActivity_arrayEncode(JNIEnv *env, jobject instance,
                                                      jintArray arr_) {
    
    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;

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;

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");
    }
     
    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();
    }
  
    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);
    }
	  
    public native void invokeStatic(int a);



#if 0
typedef struct {
    char *name;          
    char *signature;    
    void *fnPtr;          
} 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; 
    }
    jclass cls;
    cls = env->FindClass("lanya/denganzhi/com/ndk/MainActivity");
    if (cls == NULL) {
        return JNI_ERR;
    }
    
    
    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();
    }
	   
    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 全局应用:可以跨方法/线程
*** 弱全局引用
 强应用,可以跨栈使用
 和强引用比较,使用的时候可能会被系统回收弱引用
必须要手动释放

作者:小置同学


阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-移动开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯