1. What are the communication modes between processes

2. What are the differences between binder and socket communication

3. Why does Android use Binder for interprocess communication in most scenarios

4. Difference between Serializable and Parcelable

5. Specific process of Parcelable serialization and deserialization

I don’t know if you have encountered the above similar questions in the interview process. I have encountered them in the interview between Tencent and OPPO. These questions may be relatively simple. For example, Serializable is more expensive and Parcelable is more efficient, but why? I’m afraid there will be a small number of people who can’t answer. Today we will take you through this knowledge, now that we have learned some JNI basics, we will implement it ourselves from the Native layer.

As mentioned in OpencV before, it can be written in pure Java code. Mat data matrix is operated by Native layer in OpencV, and Mat class is also available in the corresponding Java layer. Let’s see how Java and Native correspond.

// C++: class Mat
//javadoc: Mat
public class Mat {

    public final long nativeObj;

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

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

    //
    // C++: Mat::Mat(int rows, int cols, int type)
    //
    // javadoc: Mat::Mat(rows, cols, type)
    public Mat(int rows, int cols, int type) {// create an object at the c++ layer and return the pointer address jlong to the Java layer.type); 
        return;
    }

    // C++: Mat::Mat()
    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);
}
Copy the code
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);
        return (jlong) new Mat( size, type );
    } catch(const std::exception &e) {
        throwJavaException(env, &e, method_name);
    } catch (...) {
        throwJavaException(env, 0, method_name);
    }

    return 0;
}
Copy the code

When we call Mat Mat = new Mat(720,1280, cvtype.cv_8uc4), we will call the native layer to create the c++ Mat object, and then call the pointer address back to the Java layer. In fact, we can also guess that c++ operates the object of Mat in the native layer. We give the pointer to the object created to the Java layer, so that when we operate in the native layer again, we can find the corresponding Mat object of c++ according to the pointer address, and then we can carry out some series of operations.

Why are we talking about OpencV? In fact, the focus of this time is, in the process of JNI development, how to transfer the object of native layer to the object of Java layer for saving and operation. Let’s take a look at an example of the serialization process for Android shared memory. Once you look at the source code for Parcel, all of the above problems should be solved. If you do not have the source code for the frameworker layer, please download it first. Do not download the castrated version, which should have the source code for the native layer.

Public final class Parcel {private long mNativePtr; public final class Parcel {private long mNativePtr; // used by native code private Parcel(long nativePtr) {if (DEBUG_RECYCLE) {
            mStack = new RuntimeException();
        }
        //Log.i(TAG, "Initializing obj=0x" + Integer.toHexString(obj), mStack);
        init(nativePtr);
    }

    private void init(long nativePtr) {
        if(nativePtr ! = 0) { mNativePtr = nativePtr; mOwnsNativeParcelObject =false;
        } else {
            mNativePtr = nativeCreate();
            mOwnsNativeParcelObject = true;
        }
    }

    /**
     * Write an integer value into the parcel at the current dataPosition(),
     * growing dataCapacity() if needed.
     */
    public final void writeInt(int val) {
        nativeWriteInt(mNativePtr, val);
    }

    /**
     * Read an integer value from the parcel at the current dataPosition().
     */
    public final int readInt() {
        returnnativeReadInt(mNativePtr); } // create Parcel. CPP returns jLong private static Native long nativeCreate(); Private static native void nativeWriteInt(long nativePtr, int val); Private static native int nativeReadInt(long nativePtr); private static native int nativeReadInt(long nativePtr); }Copy the code
// create Parcel, Static jlong android_os_Parcel_create(JNIEnv* env, jclass clazz) {Parcel* Parcel = new Parcel();returnreinterpret_cast<jlong>(parcel); } // get c++ layer objects from pointer addresses, Static void android_os_Parcel_writeInt(JNIEnv* env, jclass clazz, jlong nativePtr, jint val) { Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);if(parcel ! = NULL) { const status_t err = parcel->writeInt32(val);if(err ! = NO_ERROR) { signalExceptionForError(env, clazz, err); }}}Copy the code
status_t Parcel::writeInt32(int32_t val)
{
    returnwriteAligned(val); } template<class T> status_t Parcel::writeAligned(T val) { COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T))  == sizeof(T)); // Check whether the size is larger or notif((mDataPos+sizeof(val)) <= mDataCapacity) {restart_write: // Write data to memory *reinterpret_cast<T*>(mData+mDataPos) = val; // Returns write successreturnfinishWrite(sizeof(val)); } return error status_t err = growData(sizeof(val));if (err == NO_ERROR) goto restart_write;
    return err;
}

int32_t Parcel::readInt32() const
{
    return readAligned<int32_t>();
}

template<class T>
T Parcel::readAligned() const {
    T result;
    if (readAligned(&result) ! = NO_ERROR) { result = 0; }return result;
}

template<class T>
status_t Parcel::readAligned(T *pArg) const { COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T)); // There is no more thanif((mDataPos+sizeof(T)) <= mDataSize) {const void* data = mData+mDataPos; MDataPos += sizeof(T); PArg = *reinterpret_cast<const T*>(data);return NO_ERROR;
    } else {
        returnNOT_ENOUGH_DATA; }}Copy the code

When it comes to shared memory, or binder drivers, a Parcel is simply a Parcel of memory in the native layer and writing data to it in a certain order. When I need to get the data, I’ll just get it in the order I wrote it. You have to write in the same order as you pick, or you’re bound to make a mistake. So why not do it in the Java layer? Can these effects be achieved at the Java layer? This also proves that C and C ++ are more flexible, because you can manipulate data on a memory address and do whatever you want. How to answer the above question should not need me to say, understand the principle so you can do some expansion in the answer.

Since it is the basic part of JNI, we had better do it ourselves. Common open source libraries such as Retrofit, OkHttp, and RxJava are better understood by tapping into the core of the library itself.

  var parcel = Parcel.obtain()

  parcel.writeInt(12)
  parcel.writeInt(24)

  var number1 = parcel.readInt()
  var number2 = parcel.readInt()

  Log.e("TAG"."number1 = $number1 , number2 = $number2")
Copy the code
class Parcel{
    private var mNativePtr: Long = 0 // used by native code
    init {
        System.loadLibrary("native-lib") mNativePtr = nativeCreate(); } private external fun nativeCreate(): Long; Fun writeInt(value: int) {nativeWriteInt(mNativePtr, value); } funreadInt(): Any {
        returnNativeReadInt (mNativePtr)} private External Fun nativeWriteInt(mNativePtr: Long, value: Int); private external fun nativeReadInt(nativePtr: Long): Int }Copy the code

Video video address: https://pan.baidu.com/s/1H2d3mxxZJn7brGEYKNrCHw password: i81c