1. nativePtr作用

  • Java层:nativePtr long类型的 保存 C++ 对象指针的地址
  • C++层:nativePtr —> C++ 对象
1.1 理解源码中nativePtr

在openCV中有个Mat.javaMat.cpp(Java_org_opencv_core_Mat_n_1Mat__DDI)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// java代码
public class Mat {

public final long nativeObj;

public Mat(long addr) {
if (addr == 0)
throw new UnsupportedOperationException("Native object address is NULL");
nativeObj = addr;
}

// javadoc: Mat::Mat()
public Mat() {
nativeObj = n_Mat();
}

// C++: Mat::Mat()
// 调入到native初始化
private static native long n_Mat();

// C++: Mat::Mat(int rows, int cols, int type)
private static native long n_Mat(int rows, int cols, int type);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 函数的声明
JNIEXPORT jlong JNICALL Java_org_opencv_core_Mat_n_1Mat__DDI
(JNIEnv* env, jclass, jdouble size_width, jdouble size_height, jint type);

// 函数的实现
JNIEXPORT jlong JNICALL Java_org_opencv_core_Mat_n_1Mat__DDI
(JNIEnv* env, jclass, jdouble size_width, jdouble size_height, jint type)
{
static const char method_name[] = "Mat::n_1Mat__DDI()";
try {
LOGD("%s", method_name);
Size size((int)size_width, (int)size_height);

// new Mat.cpp 实例 强行 转换
//【重点这一句,new Mat(); C++对象,把此对象的首地址返回给Java==nativeObj】
return (jlong) new Mat( size, type );

} catch(const std::exception &e) {
throwJavaException(env, &e, method_name);
} catch (...) {
throwJavaException(env, 0, method_name);
}

return 0;
}

结论:Java层拿到nativeObj就是 Native层C++对象的指针,也就是对象的首地址,下次想去使用Native层的功能时,就可以使用首地址寻找C++对象,并让他干活即可。

2. Parcel源码分析

2.1 Parcel初始化流程

java端初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package android.os;

public final class Parcel {

/**
* 用于初始化 Parcel对象的函数
* Retrieve a new Parcel object from the pool.
*/
public static Parcel obtain() {
final Parcel[] pool = sOwnedPool;
synchronized (pool) {
Parcel p;
for (int i=0; i<POOL_SIZE; i++) {
p = pool[i];
if (p != null) {
pool[i] = null;
if (DEBUG_RECYCLE) {
p.mStack = new RuntimeException();
}
p.mReadWriteHelper = ReadWriteHelper.DEFAULT;
return p;
}
}
}
// 注意:这里的 new Parcel(0); 操作,会进入下面代码
return new Parcel(0);
}
}
1
2
3
4
5
6
7
8
9
private Parcel(long nativePtr) {
if (DEBUG_RECYCLE) {
mStack = new RuntimeException();
}
//Log.i(TAG, "Initializing obj=0x" + Integer.toHexString(obj), mStack

// 注意:这里的 init函数,会继续你下面代码
init(nativePtr);
}
1
2
3
4
5
6
7
8
9
10
11
private void init(long nativePtr) { // mNativePtr == Parcel.cpp 对象指针地址
if (nativePtr != 0) {
mNativePtr = nativePtr;
mOwnsNativeParcelObject = false;
} else {
mNativePtr = nativeCreate(); // 注意:这里的nativeCreate();会返回C++的头指针内存地址
mOwnsNativeParcelObject = true;
}
}

private static native long nativeCreate(); // 注意:从这里进入Native层代码

native代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static const JNINativeMethod gParcelMethods[] = {
//动态注册各种方法
......
{"openFileDescriptor", "(Ljava/lang/String;I)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_openFileDescriptor},
{"dupFileDescriptor", "(Ljava/io/FileDescriptor;)Ljava/io/FileDescriptor;", (void*)android_os_Parcel_dupFileDescriptor},
{"closeFileDescriptor", "(Ljava/io/FileDescriptor;)V", (void*)android_os_Parcel_closeFileDescriptor},

// 在这里就找到了,【nativeCreate】
{"nativeCreate", "()J", (void*)android_os_Parcel_create},
{"nativeFreeBuffer", "(J)J", (void*)android_os_Parcel_freeBuffer},
{"nativeDestroy", "(J)V", (void*)android_os_Parcel_destroy},

......
};
1
2
3
4
5
6
7
8
// 正在的实现方法
static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz){
// 实例化C++ 对象,此对象 就是一个 内存地址指针
Parcel* parcel = new Parcel();

// 返回:内存地址指针,并且用reinterpret_cast 转换jlong类型,这代码一看就知道比OpenCV写得好
return reinterpret_cast<jlong>(parcel);
}
2.2 写数据

Parcel.java

1
2
3
4
5
6
7
8
9
10
11
12
/**
* Write an integer value into the parcel at the current dataPosition(),
* growing dataCapacity() if needed.
*/
public final void writeInt(int val) {

// 注意:这里会调用native层,下面就会分析这个代码
nativeWriteInt(mNativePtr, val);
}

@FastNative // 思考:行参一的作用是可以通过此内存地址去寻找native层的C++对象
private static native void nativeWriteInt(long nativePtr, int val); // 下面进入native

android_os_Parcel.cpp 的 nativeWriteInt:

1
2
3
4
5
6
7
8
9
10
11
{"nativeWriteByteArray",      "(J[BII)V", (void*)android_os_Parcel_writeByteArray},
{"nativeWriteBlob", "(J[BII)V", (void*)android_os_Parcel_writeBlob},
// @FastNative

// 还是采用动态注册的方式,搜索函数实现 android_os_Parcel_writeInt 即可
{"nativeWriteInt", "(JI)V", (void*)android_os_Parcel_writeInt},

// @FastNative
{"nativeWriteLong", "(JJ)V", (void*)android_os_Parcel_writeLong},
// @FastNative
{"nativeWriteFloat", "(JF)V", (void*)android_os_Parcel_writeFloat},

实现函数android_os_Parcel_writeInt

1
2
3
4
5
6
7
8
9
10
11
12
13
static void android_os_Parcel_writeInt(JNIEnv* env, jclass clazz, jlong nativePtr, jint val) {

// 通过在Java层保存的,C++对象首地址,来查找到C++对象 Parcel* parcel
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
// 把内容写入进去,这里是调用到哪里去? 看下面代码...
const status_t err = parcel->writeInt32(val);
if (err != NO_ERROR) {
// 抛出异常
signalExceptionForError(env, clazz, err);
}
}
}

Parcel.cpp 的 writeInt32:

1
2
3
status_t Parcel::writeInt32(int32_t val){
return writeAligned(val);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
template<class T> // 相当于Java的泛型
status_t Parcel::writeAligned(T val) {
COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T));

// mDataPos:内存首地址的当前挪动位置
// mDataCapacity:共享内存的总大小
// mData:共享内存的首地址

if ((mDataPos+sizeof(val)) <= mDataCapacity) {

// 就相当于,指针++ 挪动好位置 然后存放刚刚挪动的位置
// * mData共享内存的首地址 = val赋值给左边;
restart_write: *reinterpret_cast<T*>(mData+mDataPos) = val;

// 下面分析 finsihWrite函数
return finishWrite(sizeof(val));
}

status_t err = growData(sizeof(val));
if (err == NO_ERROR) goto restart_write;
return err;
}

finsihWrite函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
status_t Parcel::finishWrite(size_t len) {
if (len > INT32_MAX) {
// don't accept size_t values which may have come from an
// inadvertent conversion from a negative int.
return BAD_VALUE;
}

//printf("Finish write of %d\n", len);
mDataPos += len; // 【上面函数存值后,把指针位置进行挪动一次,方便后续的值 存放】

ALOGV("finishWrite Setting data pos of %p to %zu", this, mDataPos);
if (mDataPos > mDataSize) {
mDataSize = mDataPos;
ALOGV("finishWrite Setting data size of %p to %zu", this, mDataSize);
}
//printf("New pos=%d, size=%d\n", mDataPos, mDataSize);
return NO_ERROR;
}

读的流程是一样的,按照顺序一个类型一个类型读出来。最核心的思想就是共享内存写数据和读数据。

2.3 CMakeList配置导入全部.cpp
1
2
3
4
5
6
file(GLOB allSource *.c *.cpp)
add_library(
native-lib
SHARED
${allSource}
)

3. 自定义Parcelable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class Student implements Parcelable {

protected Student(Parcel in) {
in.readInt(); // 顺序不能乱
in.readString();
}

@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(10); // 顺序不能乱
dest.writeString("AAA");
}

@Override
public int describeContents() {
return 0;
}

public static final Creator<Student> CREATOR = new Creator<Student>() {
@Override
public Student createFromParcel(Parcel in) {
return new Student(in);
}

@Override
public Student[] newArray(int size) {
return new Student[size];
}
};
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// ========================= 源码分析
Parcel parcel = Parcel.obtain();

parcel.writeInt(50);
parcel.writeInt(100);

// mDataPos = 8

// 为什么要修改成0,将内存地址还原
parcel.setDataPosition(0);

int r = parcel.readInt();
Log.d("Jack", "onCreate: 系统的:" + r);
r = parcel.readInt();
Log.d("Jack", "onCreate: 系统的:" + r);
}
}

4.Parcelable为什么比Serializable高效

Serializable IO流完成的(存储到磁盘), Parcelable C++ 对象指针 共享内存 指针挪动报错数据。