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 全局应用:可以跨方法/线程
*** 弱全局引用
强应用,可以跨栈使用
和强引用比较,使用的时候可能会被系统回收弱引用
必须要手动释放
源码下载:https://download.csdn.net/download/dreams_deng/12262916