一:C/C++访问java基本类型数组
1,作为参数传入
1)通过GetIntArrayElements接收数组指针
看下MainActivity类的代码
package com.example.acer.test_18_02_21;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
int[] a=new int[10];
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
for(int i=0;i<10;i++){
a[i]=i;
}
temp(a);
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
public native int[] temp(int[] a);
}
看下native-lib代码
#include <jni.h>
#include <string>
#include <android/log.h>
extern "C"
JNIEXPORT jstring
JNICALL
Java_com_example_acer_test_118_102_121_MainActivity_stringFromJNI(
JNIEnv *env,
jobject jobj) {
std::string hello = "Hello 我乃张飞是也from C++";
return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT jintArray JNICALL
Java_com_example_acer_test_118_102_121_MainActivity_temp(JNIEnv *env, jobject instance,
jintArray jintArray1) {
//指针a与数组jintArray1进行关联
jint *a = env->GetIntArrayElements(jintArray1, NULL);
//获取数组的长度
jsize jiasize=env->GetArrayLength(jintArray1);
for(int i=0;i<jiasize;i++){
//输出类型 target
__android_log_print(ANDROID_LOG_ERROR,"JNI_tempp","%d",a[i]);
}
// TODO
//释放 jintArray1与a的关联
env->ReleaseIntArrayElements(jintArray1, a, 0);
return jintArray1;
}
看下native-lib中的打印输出
02-23 14:06:22.235 3622-3622/com.example.acer.test_18_02_21 E/JNI_tempp: 0
02-23 14:06:22.235 3622-3622/com.example.acer.test_18_02_21 E/JNI_tempp: 1
02-23 14:06:22.235 3622-3622/com.example.acer.test_18_02_21 E/JNI_tempp: 2
02-23 14:06:22.235 3622-3622/com.example.acer.test_18_02_21 E/JNI_tempp: 3
02-23 14:06:22.235 3622-3622/com.example.acer.test_18_02_21 E/JNI_tempp: 4
02-23 14:06:22.235 3622-3622/com.example.acer.test_18_02_21 E/JNI_tempp: 5
02-23 14:06:22.235 3622-3622/com.example.acer.test_18_02_21 E/JNI_tempp: 6
02-23 14:06:22.235 3622-3622/com.example.acer.test_18_02_21 E/JNI_tempp: 7
02-23 14:06:22.235 3622-3622/com.example.acer.test_18_02_21 E/JNI_tempp: 8
02-23 14:06:22.235 3622-3622/com.example.acer.test_18_02_21 E/JNI_tempp: 9
2)通过GetIntArrayRegion复制数组
MainActivity类不用动,看下native-lib文件
#include <jni.h>
#include <string>
#include <android/log.h>
extern "C"
JNIEXPORT jstring
JNICALL
Java_com_example_acer_test_118_102_121_MainActivity_stringFromJNI(
JNIEnv *env,
jobject jobj) {
std::string hello = "Hello 我乃张飞是也from C++";
return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT jintArray JNICALL
Java_com_example_acer_test_118_102_121_MainActivity_temp(JNIEnv *env, jobject instance,
jintArray jintArray1) {
//获取数组的长度
jsize jiasize=env->GetArrayLength(jintArray1);
//C中的数组需要固定的长度
jintArray b[jiasize];
//将Java数组复制到b数组中
//四个参数1,java中的数组;2,数组开始复制的位置;3,数组的长度;4,jni环境下的数组指针,数组名直接代表数组的指针
env->GetIntArrayRegion(jintArray1, 0, jiasize, (jint *) b);
for(int i=0;i<jiasize;i++){
//输出类型 target
__android_log_print(ANDROID_LOG_ERROR,"JNI_tempp","%d",b[i]);
}
return jintArray1;
}
或者这样写
#include <jni.h>
#include <string>
#include <android/log.h>
extern "C"
JNIEXPORT jstring
JNICALL
Java_com_example_acer_test_118_102_121_MainActivity_stringFromJNI(
JNIEnv *env,
jobject jobj) {
std::string hello = "Hello 我乃张飞是也from C++";
return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT jintArray JNICALL
Java_com_example_acer_test_118_102_121_MainActivity_temp(JNIEnv *env, jobject instance,
jintArray jintArray1) {
//获取数组的长度
jsize jiasize=env->GetArrayLength(jintArray1);
//C中的数组需要固定的长度
jint b[jiasize];
//将Java数组复制到b数组中
//四个参数1,java中的数组;2,数组开始复制的位置;3,数组的长度;4,jni环境下的数组指针,数组名直接代表数组的指针
env->GetIntArrayRegion(jintArray1, 0, jiasize, b);
for(int i=0;i<jiasize;i++){
//输出类型 target
__android_log_print(ANDROID_LOG_ERROR,"JNI_tempp","%d",b[i]);
}
return jintArray1;
}
ps:接收数组指针时要用指针类型接收,复制数组时要用数组复制
2,未作为参数传入
1)通过GetArrayElements接收数组指针
MainActivity类,
package com.example.acer.test_18_02_21;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
int[] a=new int[10];
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
for(int i=0;i<10;i++){
a[i]=i;
}
TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
temp(a);
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
public native int[] temp(int[] a);
}
我们看下native-lib文件
#include <jni.h>
#include <string>
#include <android/log.h>
extern "C"
JNIEXPORT jstring
JNICALL
Java_com_example_acer_test_118_102_121_MainActivity_stringFromJNI(
JNIEnv *env,
jobject jobj) {
jclass cls = env->GetObjectClass(jobj);
//注意第三个参数,是数组签名
jfieldID jid = env->GetFieldID(cls, "a", "[I");
jintArray jiarr = (jintArray) env->GetObjectField(jobj, jid);
//指针a与数组jintArray1进行关联
jint *a = env->GetIntArrayElements(jiarr, NULL);
//获取数组的长度
jsize jiasize = env->GetArrayLength(jiarr);
for (int i = 0; i < jiasize; i++) {
//输出类型 target
__android_log_print(ANDROID_LOG_ERROR, "JNI_tempp", "%d", a[i]);
}
//释放 jintArray1与a的关联
env->ReleaseIntArrayElements(jiarr, a, 0);
std::string hello = "Hello 我乃张飞是也from C++";
return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT jintArray JNICALL
Java_com_example_acer_test_118_102_121_MainActivity_temp(JNIEnv *env, jobject instance,
jintArray jintArray1) {
return jintArray1;
}
看下打印输出结果
02-23 16:00:26.941 2932-2932/com.example.acer.test_18_02_21 E/JNI_tempp: 0
02-23 16:00:26.945 2932-2932/com.example.acer.test_18_02_21 E/JNI_tempp: 1
02-23 16:00:26.945 2932-2932/com.example.acer.test_18_02_21 E/JNI_tempp: 2
02-23 16:00:26.949 2932-2932/com.example.acer.test_18_02_21 E/JNI_tempp: 3
02-23 16:00:26.949 2932-2932/com.example.acer.test_18_02_21 E/JNI_tempp: 4
02-23 16:00:26.949 2932-2932/com.example.acer.test_18_02_21 E/JNI_tempp: 5
02-23 16:00:26.949 2932-2932/com.example.acer.test_18_02_21 E/JNI_tempp: 6
02-23 16:00:26.949 2932-2932/com.example.acer.test_18_02_21 E/JNI_tempp: 7
02-23 16:00:26.949 2932-2932/com.example.acer.test_18_02_21 E/JNI_tempp: 8
02-23 16:00:26.949 2932-2932/com.example.acer.test_18_02_21 E/JNI_tempp: 9
2)通过GetArrayRegion复制数组
我们看下native-lib文件
#include <jni.h>
#include <string>
#include <android/log.h>
extern "C"
JNIEXPORT jstring
JNICALL
Java_com_example_acer_test_118_102_121_MainActivity_stringFromJNI(
JNIEnv *env,
jobject jobj) {
jclass cls = env->GetObjectClass(jobj);
//注意第三个参数,是数组签名
jfieldID jid = env->GetFieldID(cls, "a", "[I");
jintArray jiarr = (jintArray) env->GetObjectField(jobj, jid);
//获取数组的长度
jsize jiasize = env->GetArrayLength(jiarr);
//创建数组
jint b[jiasize];
env->GetIntArrayRegion(jiarr,0,jiasize,b);
for (int i = 0; i < jiasize; i++) {
//输出类型 target
__android_log_print(ANDROID_LOG_ERROR, "JNI_tempp", "%d", b[i]);
}
std::string hello = "Hello 我乃张飞是也from C++";
return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT jintArray JNICALL
Java_com_example_acer_test_118_102_121_MainActivity_temp(JNIEnv *env, jobject instance,
jintArray jintArray1) {
return jintArray1;
}
看下输出的结果
02-23 16:21:14.970 3539-3539/com.example.acer.test_18_02_21 E/JNI_tempp: 0
02-23 16:21:14.974 3539-3539/com.example.acer.test_18_02_21 E/JNI_tempp: 1
02-23 16:21:14.974 3539-3539/com.example.acer.test_18_02_21 E/JNI_tempp: 2
02-23 16:21:14.974 3539-3539/com.example.acer.test_18_02_21 E/JNI_tempp: 3
02-23 16:21:14.974 3539-3539/com.example.acer.test_18_02_21 E/JNI_tempp: 4
02-23 16:21:14.974 3539-3539/com.example.acer.test_18_02_21 E/JNI_tempp: 5
02-23 16:21:14.974 3539-3539/com.example.acer.test_18_02_21 E/JNI_tempp: 6
02-23 16:21:14.974 3539-3539/com.example.acer.test_18_02_21 E/JNI_tempp: 7
02-23 16:21:14.974 3539-3539/com.example.acer.test_18_02_21 E/JNI_tempp: 8
02-23 16:21:14.974 3539-3539/com.example.acer.test_18_02_21 E/JNI_tempp: 9
我们总结下为作为参数传入的步骤:
获取类
获取属性ID
通过通过GetObjectField方法获取数组(强转至jarray)
通过GetArrayElements接收数组指针
或者通过GetArrayRegion复制数组
二:C/C++访问java对象数组
1,作为参数传入
我们新建一个Son类
package com.example.acer.test_18_02_21;
/**
* Created by acer on 2018/2/22.
*/
public class Son {
public Son(){
}
public void drive() {
System.out.println("我正在学开车");
}
}
看下ManiActivity类
package com.example.acer.test_18_02_21;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
Son[] son=new Son[3];
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
son[0]=new Son();
son[1]=new Son();
son[2]=new Son();
callson(son);
TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
public native void callson(Son[] son);
}
看下native-lib文件
#include <jni.h>
#include <string>
#include <android/log.h>
extern "C"
JNIEXPORT jstring
JNICALL
Java_com_example_acer_test_118_102_121_MainActivity_stringFromJNI(
JNIEnv *env,
jobject jobj) {
// jclass cls = env->GetObjectClass(jobj);
// //注意第三个参数,是数组签名
// jfieldID jid = env->GetFieldID(cls, "a", "[I");
// jintArray jiarr = (jintArray) env->GetObjectField(jobj, jid);
// //获取数组的长度
// jsize jiasize = env->GetArrayLength(jiarr);
// //创建数组
// jint b[jiasize];
// env->GetIntArrayRegion(jiarr,0,jiasize,b);
// for (int i = 0; i < jiasize; i++) {
// //输出类型 target
// __android_log_print(ANDROID_LOG_ERROR, "JNI_tempp", "%d", b[i]);
// }
std::string hello = "Hello 我乃张飞是也from C++";
return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_acer_test_118_102_121_MainActivity_callson(JNIEnv *env, jobject instance,
jobjectArray son) {
// TODO
//0表示第一个元素
jobject son1=env->GetObjectArrayElement(son ,0);
jclass son_class = env->GetObjectClass(son1);
jmethodID sonid = env->GetMethodID(son_class, "drive", "()V");
//拿到对象后执行对象的方法
env->CallVoidMethod(son1, sonid);
}
看下打印输出结果
02-23 17:02:39.490 4665-4665/? I/System.out: 我正在学开车
2,不作为 参数传入
Son类
package com.example.acer.test_18_02_21;
/**
* Created by acer on 2018/2/22.
*/
public class Son {
public Son(){
}
public void drive() {
System.out.println("我正在学开车");
}
}
MainActivity类
package com.example.acer.test_18_02_21;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
Son[] son=new Son[3];
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
son[0]=new Son();
son[1]=new Son();
son[2]=new Son();
TextView tv = (TextView) findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
}
native-lib文件
#include <jni.h>
#include <string>
#include <android/log.h>
extern "C"
JNIEXPORT jstring
JNICALL
Java_com_example_acer_test_118_102_121_MainActivity_stringFromJNI(
JNIEnv *env,
jobject jobj) {
jclass cls = env->GetObjectClass(jobj);
//数组的签名:[
jfieldID jsonid = env->GetFieldID(cls,"son","[Lcom/example/acer/test_18_02_21/Son;");
jobjectArray son = (jobjectArray) env->GetObjectField(jobj, jsonid);
jobject son1=env->GetObjectArrayElement(son, 0);
jclass son_class = env->GetObjectClass(son1);
jmethodID jmid = env->GetMethodID(son_class, "drive", "()V");
env->CallVoidMethod(son1, jmid);
std::string hello = "Hello 我乃马超是也from C++";
return env->NewStringUTF(hello.c_str());
}
运行结果
02-23 19:14:40.759 6675-6675/? I/System.out: 我正在学开车
PS数组操作函数总结
jsize GetArrayLength(JNIEnv *env, jarray array)
返回数组元素个数(数组长度)
jobject GetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index)
返回对象数组元素中的对象
void SetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index, jobject value)
将对象数组元素更改为新对象
//注意,以下两个方法成对出现
<type>* Get<type>ArrayElements(JNIEnv *env, jarray array, jboolean *isCopy)
获取基本类型数组中首元素的指针,isCopy不为NULL时,如果要复制数组就设置为JNI_TURE,不复制时设置为JNI_FALSE
//和上一个方法成对出现
void Release<type>ArrayElements(JNIEnv *env, jarray array, <type> elems[], jint mode)
通知虚拟机通过Get<type>ArrayElements拿到的指针不再需要了,进行释放缓冲区,mode为0(更新数组后释放),JNI_COMMIT(更新后不释放),JNI_ABORT(不更新就释放)
void Get<type>ArrayRegion(JNIEnv *env, jarray array, jint start, jint length, <type> elems[])
复制java的基本类型数组到C/C++中
void Set <type>ArrayRegion(JNIEnv *env, jarray array, jint start, jint length, <type> elems[])
复制C/C++中的基本类型数组到java中
jobjectArray NewObjectArray(JNIEnv *env, jsize length,jclass elementClass, jobject initialElement);
新建一个对象数组,最后一个参数为元素初始化的对象
j<Type>Array New <Type> Array(JNIEnv *env, jsize length);
新建一个基本类型的数组
二JNI引用
1-局部引用
jobject NewLocalRef(JNIEnv *env, jobject ref);
新建一个局部引用
局部引用还包括各种JNI的接口创建(findclass,newobject,getobjectclass,newarray等)
局部引用在native方法调用的持续时间内有效。在native方法返回后,它们会自动释放。每个局部引用都需要一部分Java虚拟机资源。程序员需要确保native方法不会过分分配局部引用。虽然局部引用在native方法返回到Java之后会自动释放,但局部引用的过多分配可能会导致在执行native方法期间虚拟机内存就不足了。
void DeleteLocalRef(JNIEnv *env, jobject localRef);
用此方法可以主动释放局部引用的内存部分
2-全局引用
jobject NewGlobalRef(JNIEnv *env, jobject obj);
创建一个全局引用的对象
void DeleteGlobalRef(JNIEnv *env, jobject globalRef);
释放一个全局引用
3-局部引用和全局引用的主要区别
局部引用不可以C/C++的线程之间传递,函数之间也不能,会阻止GC回收对象
全局引用可以在C/C++的多个线程之间传递,如果不去手动释放,将会一直存在于内存之中,也会阻止GC去回收对象
4-引用使用中的辅助方法
jint EnsureLocalCapacity(JNIEnv *env, jint capacity);判断
在进入本地方法之前,VM自动确保至少可以创建16个本地引用。成功返回0; 否则返回一个负数并抛出一个 OutOfMemoryError。
为了向后兼容,VM会分配超出保证容量的局部引用。(作为调试支持,VM可能会向用户发出警告:局部引用太多,在JDK中,程序员可以提供 -verbose:jni命令行选项来打开这些消息。)如果没有更多的本地引用可以创建造超出保证的能力就会返回FatalError。
5-弱全局引用
一种运行期间可以被内存回收的全局引用
jweak NewWeakGlobalRef(JNIEnv *env, jobject obj);
创建一个弱全局引用
void DeleteWeakGlobalRef(JNIEnv *env, jweak obj);
释放弱全局引用