Parcel分析

Parcelandroid中不同于Java Serialize新的序列化机制。

Java Serialize机制作用是能将数据对象存入字节流中,在需要时重新生成对象。主要应用是利用外部存储设备保存对象状态,以及通过网络传输对象等。

android系统定位内存受限设备,对性能要求更高,而且系统中采用了binder ipc机制,就需要求性能更出色的对象传输方式。Parcel定位就是轻量级高效的对象序列化和反序列化机制。

源码位于

Framework/native/include/binder/parcel.h

Framework/native/libs\binder\Parcel.cpp

 

我们以MediaPlayerSeriviceManageraddService为例,解析Parcel


virtual status_t addService(const String16& name, const sp<IBinder>& service,
            bool allowIsolated)
    {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        data.writeStrongBinder(service);
        data.writeInt32(allowIsolated ? 1 : 0);
        status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
        return err == NO_ERROR ? reply.readExceptionCode() : err;
}

从代码知,实例化了两个Parcel栈上对象datareplydata主要是传送给servicemanager程序,而reply是从servicemanager返回的结果。

Data写入的值有InterfaceToken :“android.os.IServiceManager” String16:media.player

StrongBinder:MediaPlayerService, Int32: 0


我们跟踪入Parcel,看他形成了什么样的数据格式


status_t Parcel::writeInterfaceToken(const String16& interface)
{
    writeInt32(IPCThreadState::self()->getStrictModePolicy() |
               STRICT_MODE_PENALTY_GATHER);
    // currently the interface identification token is just its name as a string
    return writeString16(interface);
}
writeInterfaceToken写入int32的IPCThreadState的strictmode policy,还有string16的android.os.IServiceManager字符串
status_t Parcel::writeInt32(int32_t val)
{
    return writeAligned(val);
}

template<class T>
status_t Parcel::writeAligned(T val) {
    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T));

    if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
        *reinterpret_cast<T*>(mData+mDataPos) = val;
        return finishWrite(sizeof(val));
    }

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

从代码可以看出writeInt32实际上调用的是writeAligned泛型函数

writeInterfaceTokenparcel第一次被调用的函数,

COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T));

是断言,判断读写时是否为4字节对齐#define PAD_SIZE(s) (((s)+3)&~3)

从代码可以看出,写的数据主要往mData里面,下面为创建Parcel对象时的初始值


void Parcel::initState()
{
    mError = NO_ERROR;
    mData = 0;
    mDataSize = 0;
    mDataCapacity = 0;
    mDataPos = 0;
    ALOGV("initState Setting data size of %p to %d\n", this, mDataSize);
    ALOGV("initState Setting data pos of %p to %d\n", this, mDataPos);
    mObjects = NULL;
    mObjectsSize = 0;
    mObjectsCapacity = 0;
    mNextObjectHint = 0;
    mHasFds = false;
    mFdsKnown = true;
    mAllowFds = true;
    mOwner = NULL;
}<p>再回到<span style="font-family:Times New Roman;">writeAligned</span><span style="font-family:宋体;">函数,此时被</span><span style="font-family:Times New Roman;">goto</span><span style="font-family:宋体;">语句指到了赋值语句,把值赋给了</span><span style="font-family:Times New Roman;">mData</span><span style="font-family:宋体;">,最后调用</span><span style="font-family:Times New Roman;">finishWrite</span></p>

很明显条件是不符合的,所以得去growData函数分配更多的内存空间


status_t Parcel::growData(size_t len)
{
    size_t newSize = ((mDataSize+len)*3)/2; //newSize = ((0+4)*3)/2 = 6
    return (newSize <= mDataSize)
            ? (status_t) NO_MEMORY
            : continueWrite(newSize);
}

可以看到内存的申请是通过continueWrite

status_t Parcel::continueWrite(size_t desired)
{
    // If shrinking, first adjust for any objects that appear
    // after the new data size.
    size_t objectsSize = mObjectsSize;
    if (desired < mDataSize) {
        if (desired == 0) {
            objectsSize = 0;
        } else {
            while (objectsSize > 0) {
                if (mObjects[objectsSize-1] < desired)
                    break;
                objectsSize--;
            }
        }
    }
    
    if (mOwner) {
        // If the size is going to zero, just release the owner's data.
        if (desired == 0) {
            freeData();
            return NO_ERROR;
        }

        // If there is a different owner, we need to take
        // posession.
        uint8_t* data = (uint8_t*)malloc(desired);
        if (!data) {
            mError = NO_MEMORY;
            return NO_MEMORY;
        }
        size_t* objects = NULL;
        
        if (objectsSize) {
            objects = (size_t*)malloc(objectsSize*sizeof(size_t));
            if (!objects) {
                mError = NO_MEMORY;
                return NO_MEMORY;
            }

            // Little hack to only acquire references on objects
            // we will be keeping.
            size_t oldObjectsSize = mObjectsSize;
            mObjectsSize = objectsSize;
            acquireObjects();
            mObjectsSize = oldObjectsSize;
        }
        
        if (mData) {
            memcpy(data, mData, mDataSize < desired ? mDataSize : desired);
        }
        if (objects && mObjects) {
            memcpy(objects, mObjects, objectsSize*sizeof(size_t));
        }
        //ALOGI("Freeing data ref of %p (pid=%d)\n", this, getpid());
        mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie);
        mOwner = NULL;

        mData = data;
        mObjects = objects;
        mDataSize = (mDataSize < desired) ? mDataSize : desired;
        ALOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
        mDataCapacity = desired;
        mObjectsSize = mObjectsCapacity = objectsSize;
        mNextObjectHint = 0;

    } else if (mData) {
        if (objectsSize < mObjectsSize) {
            // Need to release refs on any objects we are dropping.
            const sp<ProcessState> proc(ProcessState::self());
            for (size_t i=objectsSize; i<mObjectsSize; i++) {
                const flat_binder_object* flat
                    = reinterpret_cast<flat_binder_object*>(mData+mObjects[i]);
                if (flat->type == BINDER_TYPE_FD) {
                    // will need to rescan because we may have lopped off the only FDs
                    mFdsKnown = false;
                }
                release_object(proc, *flat, this);
            }
            size_t* objects =
                (size_t*)realloc(mObjects, objectsSize*sizeof(size_t));
            if (objects) {
                mObjects = objects;
            }
            mObjectsSize = objectsSize;
            mNextObjectHint = 0;
        }

        // We own the data, so we can just do a realloc().
        if (desired > mDataCapacity) {
            uint8_t* data = (uint8_t*)realloc(mData, desired);
            if (data) {
                mData = data;
                mDataCapacity = desired;
            } else if (desired > mDataCapacity) {
                mError = NO_MEMORY;
                return NO_MEMORY;
            }
        } else {
            if (mDataSize > desired) {
                mDataSize = desired;
                ALOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
            }
            if (mDataPos > desired) {
                mDataPos = desired;
                ALOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos);
            }
        }
        
    } else {
        // This is the first data.  Easy!
        uint8_t* data = (uint8_t*)malloc(desired);
        if (!data) {
            mError = NO_MEMORY;
            return NO_MEMORY;
        }
        
        if(!(mDataCapacity == 0 && mObjects == NULL
             && mObjectsCapacity == 0)) {
            ALOGE("continueWrite: %d/%p/%d/%d", mDataCapacity, mObjects, mObjectsCapacity, desired);
        }
        
        mData = data;
        mDataSize = mDataPos = 0;
        ALOGV("continueWrite Setting data size of %p to %d\n", this, mDataSize);
        ALOGV("continueWrite Setting data pos of %p to %d\n", this, mDataPos);
        mDataCapacity = desired;
    }

    return NO_ERROR;
}

从代码知道mData存储着普通数据,mObjects则记录着内存块IBinder类型的数据以及FileDescriptor,而后者是通过flatten_binder()和unflatten_binder()实现的。

而我们writeInt32mData这步,如果mData之前有分配内存走realloc,没分配走malloc,此时mDataCapacity6mData为分配出来的内存,mDataSize = mDataPos = 0;


template<class T>
status_t Parcel::writeAligned(T val) {
    COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T));

    if ((mDataPos+sizeof(val)) <= mDataCapacity) {
restart_write:
        *reinterpret_cast<T*>(mData+mDataPos) = val;
        return finishWrite(sizeof(val));
    }

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

再回到writeAligned函数,此时被goto语句指到了赋值语句,把值赋给了mData,最后调用finishWrite

status_t Parcel::finishWrite(size_t len)
{
    //printf("Finish write of %d\n", len);
    mDataPos += len;
    ALOGV("finishWrite Setting data pos of %p to %d\n", this, mDataPos);
    if (mDataPos > mDataSize) {
        mDataSize = mDataPos;
        ALOGV("finishWrite Setting data size of %p to %d\n", this, mDataSize);
    }
    //printf("New pos=%d, size=%d\n", mDataPos, mDataSize);
    return NO_ERROR;
}


mDataPos = 4, mDataSize = 4

 

到这里就完成了writeInt32的操作

接着我们看writeInterfaceTokenwriteString16

status_t Parcel::writeString16(const char16_t* str, size_t len)
{ // str = “android.os.IServiceManager”
    if (str == NULL) return writeInt32(-1);
    
    status_t err = writeInt32(len);
    if (err == NO_ERROR) {
        len *= sizeof(char16_t); // 104
        uint8_t* data = (uint8_t*)writeInplace(len+sizeof(char16_t)); // 108
        if (data) {
            memcpy(data, str, len);
            *reinterpret_cast<char16_t*>(data+len) = 0;
            return NO_ERROR;
        }
        err = mError;
    }
    return err;
}

从代码可以看到String是先在前面写了他str的个数大小,然后

void* Parcel::writeInplace(size_t len)
{
    const size_t padded = PAD_SIZE(len); // 56 (54内存对齐)

    // sanity check for integer overflow
    if (mDataPos+padded < mDataPos) {  //8+56  < 12 ?
        return NULL;
    }

    if ((mDataPos+padded) <= mDataCapacity) {  // 8+56  < 12 ?
restart_write:
        //printf("Writing %ld bytes, padded to %ld\n", len, padded);
        uint8_t* const data = mData+mDataPos; // 指针指向为赋值的地方

        // Need to pad at end?
        if (padded != len) {  // 56 != 54 
#if BYTE_ORDER == BIG_ENDIAN
            static const uint32_t mask[4] = {
                0x00000000, 0xffffff00, 0xffff0000, 0xff000000
            };
#endif
#if BYTE_ORDER == LITTLE_ENDIAN
            static const uint32_t mask[4] = {
                0x00000000, 0x00ffffff, 0x0000ffff, 0x000000ff
            };
#endif
            //printf("Applying pad mask: %p to %p\n", (void*)mask[padded-len],
            //    *reinterpret_cast<void**>(data+padded-4));

 // data + 108 - 4  最后4位字节对齐后数据因大小端需保护是否正常
            *reinterpret_cast<uint32_t*>(data+padded-4) &= mask[padded-len];
        }

        finishWrite(padded); // 把大小赋予mDataPos = 56 +8和mDataSize = 56 +8        return data;
    }

    status_t err = growData(padded); // 申请了(mDataSize(8) + 56  )*3/2的内存(96)
    if (err == NO_ERROR) goto restart_write;
    return NULL;
}

如果数据不够则申请内存,够的话,要考虑对齐后大小端问题,然后通过memcpystr赋值给data,同时在最后加上结束符

writeInterfaceToken函数就到这,现在ParcelmData是这样的



virtual status_t addService(const String16& name, const sp<IBinder>& service,
            bool allowIsolated)
    {
        Parcel data, reply;
        data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
        data.writeString16(name);
        data.writeStrongBinder(service);
        data.writeInt32(allowIsolated ? 1 : 0);
        status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);
        return err == NO_ERROR ? reply.readExceptionCode() : err;
}


 

接着是data.writeString16(name); // name = “media.player”

可得出如下结构



接着是data.writeStrongBinder(service);这里的serviceMediaplayerService,它是继承于IBinder的,所以写的不止是mData,还有mObject,跟踪下代码


status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{
    return flatten_binder(ProcessState::self(), val, this);
}

status_t flatten_binder(const sp<ProcessState>& proc,
    const sp<IBinder>& binder, Parcel* out)
{
    flat_binder_object obj;
    
    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; //接收其他进程发过来的文件形式binder
    if (binder != NULL) {
        IBinder *local = binder->localBinder(); // mediaplayer service这项不为空
        if (!local) {
// 此处为client端使用
            BpBinder *proxy = binder->remoteBinder();
            if (proxy == NULL) {
                ALOGE("null proxy");
            }
            const int32_t handle = proxy ? proxy->handle() : 0;
            obj.type = BINDER_TYPE_HANDLE;
            obj.handle = handle;
            obj.cookie = NULL;
        } else {
            obj.type = BINDER_TYPE_BINDER;
            obj.binder = local->getWeakRefs();
            obj.cookie = local;
        }
    } else {
        obj.type = BINDER_TYPE_BINDER;
        obj.binder = NULL;
        obj.cookie = NULL;
    }
    
    return finish_flatten_binder(binder, obj, out);
}

可以看出,这里涉及到flat_binder_object


从代码可以看出,mediaplayer service flat_binder_object 值如下

obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS; // 8个字节

obj.type = BINDER_TYPE_BINDER; // 8个字节

obj.binder = local->getWeakRefs(); // 8个字节

obj.cookie = local; // 4 个字节


inline static status_t finish_flatten_binder(
    const sp<IBinder>& binder, const flat_binder_object& flat, Parcel* out)
{
    return out->writeObject(flat, false);
}

status_t Parcel::writeObject(const flat_binder_object& val, bool nullMetaData)
{
    // flat_binder_object本身是28字节已经对齐
    const bool enoughData = (mDataPos+sizeof(val)) <= mDataCapacity;  // 96+28 <= 96  
    const bool enoughObjects = mObjectsSize < mObjectsCapacity; // 0 < 0
    if (enoughData && enoughObjects) { // false && false
restart_write:
        *reinterpret_cast<flat_binder_object*>(mData+mDataPos) = val;
        
        // Need to write meta-data?
        if (nullMetaData || val.binder != NULL) {
            mObjects[mObjectsSize] = mDataPos; //mObject[0] = 96
            acquire_object(ProcessState::self(), val, this); // 增加引用值
            mObjectsSize++; //mObjectsSize = 1
        }
        
        // remember if it's a file descriptor
        if (val.type == BINDER_TYPE_FD) {
            if (!mAllowFds) {
                return FDS_NOT_ALLOWED;
            }
            mHasFds = mFdsKnown = true;
        }

        return finishWrite(sizeof(flat_binder_object)); // 28  mDataPos = 96+28 ....
    }

    if (!enoughData) { // false
        const status_t err = growData(sizeof(val)); // ((96+28)*3)/2 =186
        if (err != NO_ERROR) return err;
    }
    if (!enoughObjects) { // false
        size_t newSize = ((mObjectsSize+2)*3)/2; // (0+2)*3/2 = 3
        size_t* objects = (size_t*)realloc(mObjects, newSize*sizeof(size_t)); // 3*4 = 12
        if (objects == NULL) return NO_MEMORY;
        mObjects = objects;
        mObjectsCapacity = newSize; // mObjectsCapacity = 12
    }
    
    goto restart_write;
}


void acquire_object(const sp<ProcessState>& proc,
    const flat_binder_object& obj, const void* who)
{
    switch (obj.type) {
        case BINDER_TYPE_BINDER:
            if (obj.binder) {
                LOG_REFS("Parcel %p acquiring reference on local %p", who, obj.cookie);
                static_cast<IBinder*>(obj.cookie)->incStrong(who);
            }
            return;
        case BINDER_TYPE_WEAK_BINDER:
            if (obj.binder)
                static_cast<RefBase::weakref_type*>(obj.binder)->incWeak(who);
            return;
        case BINDER_TYPE_HANDLE: {
            const sp<IBinder> b = proc->getStrongProxyForHandle(obj.handle);
            if (b != NULL) {
                LOG_REFS("Parcel %p acquiring reference on remote %p", who, b.get());
                b->incStrong(who);
            }
            return;
        }
        case BINDER_TYPE_WEAK_HANDLE: {
            const wp<IBinder> b = proc->getWeakProxyForHandle(obj.handle);
            if (b != NULL) b.get_refs()->incWeak(who);
            return;
        }
        case BINDER_TYPE_FD: {
            // intentionally blank -- nothing to do to acquire this, but we do
            // recognize it as a legitimate object type.
            return;
        }
    }

    ALOGD("Invalid object type 0x%08lx", obj.type);
}

通过writeObject函数把flat_binder_object 值 写入了mData,并mObject记录了flat_binder_object的起始位置

mData:


mObject:


最后data.writeInt32(allowIsolated ? 1 : 0);



最后通过remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply);

BpBinder(0)Parcel data发送到ServiceManager


我们来看看读出数据,还是用addService的数据,看看怎么读取!

读取数据我们只好到servicemanager里面看下

 

Data的解析就在这binder_txn databinder_io *msg)里面


int svcmgr_handler(struct binder_state *bs,
                   struct binder_txn *txn,
                   struct binder_io *msg, /** 是data */
                   struct binder_io *reply)
{
    struct svcinfo *si;
    uint16_t *s;
    unsigned len;
    void *ptr;
    uint32_t strict_policy;
    int allow_isolated;

//    ALOGI("target=%p code=%d pid=%d uid=%d\n",
//         txn->target, txn->code, txn->sender_pid, txn->sender_euid);

    if (txn->target != svcmgr_handle)
        return -1;

    // Equivalent to Parcel::enforceInterface(), reading the RPC
    // header with the strict mode policy mask and the interface name.
    // Note that we ignore the strict_policy and don't propagate it
    // further (since we do no outbound RPCs anyway).
    strict_policy = bio_get_uint32(msg); // 读出4个字节的strictmode
    s = bio_get_string16(msg, &len); // 读出带字符串长度,并对齐的字符串android.os.IServiceManager
    if ((len != (sizeof(svcmgr_id) / 2)) ||
        memcmp(svcmgr_id, s, sizeof(svcmgr_id))) {  // svcmgr_id为android.os.IServiceManager
        fprintf(stderr,"invalid id %s\n", str8(s));
        return -1;
    }

    switch(txn->code) {
    case SVC_MGR_GET_SERVICE:
    case SVC_MGR_CHECK_SERVICE:
        s = bio_get_string16(msg, &len);
        ptr = do_find_service(bs, s, len, txn->sender_euid);
        if (!ptr)
            break;
	// 如果找到则填充ptr入reply
        bio_put_ref(reply, ptr);
        return 0;

    case SVC_MGR_ADD_SERVICE:
        s = bio_get_string16(msg, &len); // 读出带字符串长度,并对齐的字符串media.player
        ptr = bio_get_ref(msg); // 读出flat_binder_obj(带有MediaplayerService)
        allow_isolated = bio_get_uint32(msg) ? 1 : 0; //读出allow_isolated
        if (do_add_service(bs, s, len, ptr, txn->sender_euid, allow_isolated))
            return -1;
        break;

    case SVC_MGR_LIST_SERVICES: {
        unsigned n = bio_get_uint32(msg);

        si = svclist;
        while ((n-- > 0) && si)
            si = si->next;
        if (si) {
            bio_put_string16(reply, si->name);
            return 0;
        }
        return -1;
    }
    default:
        ALOGE("unknown code %d\n", txn->code);
        return -1;
    }
	
    bio_put_uint32(reply, 0);
    return 0;
}


其中bio_get_string16bio_get_uint32这些实际上相当于Parcel里面的readString16readInt32

我们把他当做parcel的成员函数来看,从代码看刚刚好是对应的,至于为什么能指向到其他进程的地址空间,那是binder driver做的

至于Java层的Parcel后面再讨论



  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Parcel 资源泄漏是指在程序运行过程中,由于某些原因导致 Parcel 对象持有的资源没有被及时释放,从而造成资源的浪费或者不良影响的情况。 ParcelAndroid 中的一个数据传输类,它提供了将数据进行打包和解包的功能。在使用 Parcel 进行数据传输时,我们通常需要手动释放 Parcel 对象持有的资源,比如文件、输入输出流等。但有时候在代码中没有及时释放资源,就会出现 Parcel 资源泄漏的情况。 导致 Parcel 资源泄漏的原因有多种,比如在代码中没有调用 Parcel 对象的 recycle 方法、没有关闭输入输出流、没有释放文件句柄等等。当这些资源无法正常释放时,会造成内存泄漏、文件泄漏等问题。长时间积累下来,会严重影响程序的性能和稳定性。 为了避免 Parcel 资源泄漏,需要开发者在代码编写过程中时刻注意资源管理的问题。首先,应该养成良好的编码习惯,及时释放不再使用的资源,减少资源泄漏的可能性。其次,要利用好 Java 语言提供的 try-finally 或 try-with-resources 语句块进行资源的释放。最后,可以使用工具辅助检测和解决潜在的资源泄漏问题,比如使用静态代码分析工具、内存泄漏检测工具等。 总之,Parcel 资源泄漏是一种常见的程序问题,会对系统性能和可靠性产生不良影响。开发者应该时刻关注资源的管理和释放,遵循良好的编码规范,以减少 parcel 资源泄漏的可能性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值