The interviewer asked a question. Let’s see how the three students, 😎, 😨 and 🤔️, did


😎 thinks he knows everything, has reached the application development ceiling, the current monthly salary of 10K

Interviewer: How do you convey the big picture across the process

😎 : Very simple, save the picture to the SD card, then pass the path, and read it in another process.

Interviewer: This requires document operation and is not efficient. Is there another way?

😎 : Bitmap implements the Parcelable interface, which can be passed directly in the Intent via the Intent.putextra (String name, Parcelable Value) method.

Interviewer: What are the disadvantages of this method?

😎 : If the Bitmap is too large, it throws an exception, so I prefer the path

Interviewer: Why did you throw it abnormally?

😎 :…

Interviewer: Ok, go back and wait for the announcement


😨 in his spare time, he often plays games, watches TV dramas and stays up late. His current monthly salary is 15K

Interviewer: What’s wrong with sending a Bitmap directly with an Intent?

😨 : Bitmap is thrown TransactionTooLargeException is unusual, the reason is: the underlying judgment as long as the Binder Transaction fails, and the Intent of data is larger than 200 k will throw this exception. (See: signalExceptionForError method of android_util_binder. CPP file)

Interviewer: Why does an Intent pass have a size limit?

😨 : When an application process starts the Binder mechanism, it maps a buffer of 1 MB. All Binder transactions in progress share this buffer of 1 MB. When using Intent on IPC application cache more than 1 m – the memory of other business, can apply for failure TransactionTooLargeException process. (Well, not like last time. See: “Talk about your understanding of Binder? That’s how you pass the test. “)

Interviewer: How do you get around that?

😨 : IPC with Binder through AIDL is not subject to this restriction. The code is as follows:

Bundle bundle = new Bundle();
bundle.putBinder("binder".new IRemoteGetBitmap.Stub() {
    @Override
    public Bitmap getBitMap(a) throws RemoteException {
        returnmBitmap; }}); intent.putExtras(bundle);Copy the code

Interviewer: How does that work?

😨 : haven’t checked it out yet

Interviewer: Ok, go back and wait for the announcement


🤔️ Keep learning every day and constantly improve myself. My current monthly salary is 30K

Interviewer: why was by putBinder Bitmap won’t throw TransactionTooLargeException exception

🤔️ : Let’s see how the Bitmap is written to the Parcel in IPC.

Android - 28 Bitmap.cpp
static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, ...) {
    // Get the Native Bitmap
    auto bitmapWrapper = reinterpret_cast<BitmapWrapper*>(bitmapHandle);
    // Obtain the corresponding SkBitmap, used to obtain the Bitmap pixel information
    bitmapWrapper->getSkBitmap(&bitmap);

    int fd = bitmapWrapper->bitmap().getAshmemFd();
    if (fd >= 0 && !isMutable && p->allowFds()) {
   	 		// Bitmap with ashmemFd && Bitmap cannot be modified && Parcel can have fd
    		// Just write the FD to Parcel.
        status = p->writeDupImmutableBlobFileDescriptor(fd);
        return JNI_TRUE;
    }

    // Copy the Bitmap to a new buffer if the above conditions are not met
    android::Parcel::WritableBlob blob;
  	// Get a buffer blob by writeBlob
    status = p->writeBlob(size, mutableCopy, &blob);

    // Get the pixel information and write to the buffer
    const void* pSrc =  bitmap.getPixels();
    if (pSrc == NULL) {
        memset(blob.data(), 0, size);
    } else {
        memcpy(blob.data(), pSrc, size); }}Copy the code

Let’s take a look at how writeBlob gets the buffer (note that even though the method name is write, the actual data is written to the buffer after the method executes).

Android - 28 Parcel.cpp
// Maximum size of a blob to transfer in-place.
static const size_t BLOB_INPLACE_LIMIT = 16 * 1024;

status_t Parcel::writeBlob(size_t len, bool mutableCopy, WritableBlob* outBlob)
{
    if(! mAllowFds || len <= BLOB_INPLACE_LIMIT) {// If fd is not allowed, or the data is less than 16K
    // Allocate a Parcel buffer to hold the data
        status = writeInt32(BLOB_INPLACE);
        void* ptr = writeInplace(len);
        outBlob->init(- 1, ptr, len, false);
        return NO_ERROR;
    }

		// Create another ashmem, map a piece of memory, the subsequent data will be stored in ashmem memory
    int fd = ashmem_create_region("Parcel Blob", len);
    void* ptr = ::mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); .// Just write fd in the parcel, so that the parcel itself does not have a large buffer, even if the data volume is large
    status = writeFileDescriptor(fd, true /*takeOwnership*/);
 		outBlob->init(fd, ptr, len, mutableCopy);
    return status;
}
Copy the code

The size of a Parcel written to a Bitmap depends on the allowFds of the Pacel.

Directly through Intent and Bitmap easy TransactionTooLargeException abnormal, because the Parcel allowFds = false, the Bitmap into the buffer directly to occupy a larger memory.

When allowFds is set to false:

// Start the Activity to execute this method in Instrumentation. Java
public ActivityResult execStartActivity(... , Intent intent, ...){
  ...
  intent.prepareToLeaveProcess(who);
	ActivityManager.getService().startActivity(...,intent,...)
}

// Intent.java
public void prepareToLeaveProcess(boolean leavingPackage) {
 Allowfds = allowfds = allowfds
  setAllowFds(false); . }Copy the code

Interviewer: Too many, you know.

🤔️ : To sum up: Large bitmaps are easily thrown because the system disables the file descriptor FD mechanism when an Intent starts a component. Bitmaps cannot use shared memory and can only be copied to the buffer mapped by the Binder. As a result, the buffer exceeds the limit and an exception is triggered. With putBinder, there is no Intent disabled descriptor. AllowFds is true when bitmap is writing parcels, and shared memory can be used to transfer images efficiently.

Interviewer: Ok, let’s talk about something else.