基于Android Studio NDK开发

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 <jni.h>
#include <string>
#include <android/log.h>
#include <pthread.h>

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;i<length;i++){
        arr[i]=arr[i]+100;
//      __android_log_print(ANDROID_LOG_ERROR,"JNI","java argumsnets..%d",arr[i]);
        LOGE("java argumsnets..%d",arr[i]);
    }

    // TODO
    /***
     *  参数3:
     *  0 : 刷新java数组,并且释放c/c++数组
     *  1 :  只是刷新java数组
     *  2 :  只释放,在c 中修改了中,java中数据没有改变
     */

    env->ReleaseIntArrayElements(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,"<init>","(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,"<init>","(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,"<init>","(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<Person *>(args);
      jobject instance = static_cast<jobject>(args);

     // struct  Person* p= (struct Person*)(args);
      // LOGE("person.age...%d",p->age);
//    jobject instance = static_cast<jobject>(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 全局应用:可以跨方法/线程
*** 弱全局引用
 强应用,可以跨栈使用
 和强引用比较,使用的时候可能会被系统回收弱引用
必须要手动释放
发布了99 篇原创文章 · 获赞 101 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/dreams_deng/article/details/105030792