Parcel相关


学习android源代码,首先需要打好基础。binder是android的基础,而Parcel又是binder的基础。

java中的serialization

java provides a mechanism, called object serialization where an object can be represented as a sequence of bytes that includes the object's data as well as information about the object's type and the types of data stored in the object.
After a serialized object has been written into a file, it can be read from the file and deserialized that is, the type information and bytes that represent the object and its data can be used to recreate the object in memory.

java提供了一种对象serialization的机制,可以把对象序列化,存在本地或者传给远程,然后可以通过反序列化将这个对象重建,也就是可以进程间或者网络传递类的对象。
而android提供了类似的机制,既Parcel,基于内存存储,结合binder使用,下面记录几点需要注意的。

Java代码中Parcel的使用

parcel如何使用?

下面是google提供的简单例子,很容易理解。

To create a class that supports the Parcelable protocol, you must do the following:
1. Make your class implement the Parcelable interface;
2. Implement writeToParcel, which takes the current state of the object and writes it to a Parcel;
3. Add a static field called CREATOR to your class which is an object implementing the Parcelable.Creator interface;
4. Finally, create an .aidl file that declares your parcelable class (as shown for the Rect.aidl file, below).

For example, here is a Rect.aidl file to create a Rect class that's parcelable:

package android.graphics;

// Declare Rect so AIDL can find it and knows that it implements
// the parcelable protocol.
//声明定义了一个类Rect,实现了Parcelable 接口
parcelable Rect;
And here is an example of how the Rect class implements the Parcelable protocol.

import android.os.Parcel;
import android.os.Parcelable;

public final class Rect implements Parcelable {
    public int left;
    public int top;
    public int right;
    public int bottom;

    public static final Parcelable.Creator<Rect> CREATOR = new
Parcelable.Creator<Rect>() {
    ////从Parcel中重建对象
        public Rect createFromParcel(Parcel in) {
            return new Rect(in);
        }

        public Rect[] newArray(int size) {
            return new Rect[size];
        }
    };

    public Rect() {
    }

    private Rect(Parcel in) {
        readFromParcel(in);
    }
    //将对象写入Parcel
    public void writeToParcel(Parcel out) {
        out.writeInt(left);
        out.writeInt(top);
        out.writeInt(right);
        out.writeInt(bottom);
    }
    //从Parcel中重建对象
    public void readFromParcel(Parcel in) {
        left = in.readInt();
        top = in.readInt();
        right = in.readInt();
        bottom = in.readInt();
    }
}

这里需要注意一点,Parcelable接口中并没有readFromParcel()这个函数,从Parcel中重建对象我们需要createFromParcel()接口就足够了,但是我们从android源代码中能看到好多类似的实现,都是在对象的构造函数中调用readFromParcel(),然后在createFromParcel()中new一个新的对象。这里主要是AIDL工具的需要,算是hard code吧。

AIDL中的in/out/inout

在AIDL的接口函数中经常能看到in,out,inout,oneway这些字眼,有什么用?

An in parameter is only transported from the caller to the callee. 
An out parameter is transported from the callee to the caller. 
And an inout parameter is transported both ways.
You would use an inout paramter when you pass an object to the callee and the callee changes it.

in 修饰的参数仅仅会从调用者传递给被调用者,既仅从client传递给server;
out 修饰的参数会从被调用者传回给调用者,即从server创建然后传给client;
inout 修饰的参数,client传给server一个对象,server修改这个对象,然后又传回给clent;

Another intresting point is that it changes the values only if its not a oneway AIDL 
The oneway keyword modifies the behavior of remote calls. When used, a remote call does not block; it simply sends the transaction data and immediately returns. 

此外还需要注意,如果这个函数被oneway 修饰,则out,inout这两种修饰符也就没啥意义了,函数会马上返回。

In AIDL, the "out" tag specifies an output-only parameter. In other words, it's a parameter that contains no interesting data on input, but will be filled with data during the method.

上面这句话说的很清楚,out修饰符,client不用太关心具体内容,server会对这个对象进行填充。
这点可以用创建Surface时的例子,在viewRoot的构造函数中,使用Surface的无参构造函数,创建一个Surface,

private final Surface mSurface = new Surface();
    //看到了吧这个surface,后面会被readFromParcel()填充,那么肯定是用了进程间通讯,AILD
    //接口中的参数描述符为out
    /**
     * Create an empty surface, which will later be filled in by
     * readFromParcel().
     * {@hide}
     */
    public Surface() {
        if (DEBUG_RELEASE) {
            mCreationStack = new Exception();
        }
        mCanvas = new CompatibleCanvas();
    }

在IWindowSession.aidl中,找到了实现的接口,很明显这个Surface是被out修饰的,将会在windowmanagerservice中进行生成。

    int relayout(IWindow window, in WindowManager.LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewVisibility,
            boolean insetsPending, out Rect outFrame, out Rect outContentInsets,
            out Rect outVisibleInsets, out Configuration outConfig,
            out Surface outSurface);

根据上面提供的例子,我们可以猜到Surface 类肯定实现了Parcelable接口,同时还需要实现接口中的方法这样Surface类的对象就可以在进程间传递啦。

public class Surface implements Parcelable{

    //入参为一个Parcel 
        private Surface(Parcel source) throws OutOfResourcesException {
        //init中肯定会调用readFromParcel(),用来从Parcel中重建Surface对象
        init(source);
    }

    //native函数来实现的
    public native   void readFromParcel(Parcel source);
    public native   void writeToParcel(Parcel dest, int flags);

    public static final Parcelable.Creator<Surface> CREATOR
            = new Parcelable.Creator<Surface>()
    {
        public Surface createFromParcel(Parcel source) {
            try {
                return new Surface(source);
            } catch (Exception e) {
                Log.e(LOG_TAG, "Exception creating surface from parcel", e);
            }
            return null;
        }

        public Surface[] newArray(int size) {
            return new Surface[size];
        }
    };
}

当然还少不了Surface.aidl文件,

//Surface.aidl
package android.view;

parcelable Surface;

因此,如果android源代码中有哪个类实现了Parcelable接口,那么我们在AIDL文件中查看接口的时候就需要注意in out等描述符。

C++代码中Parcel的使用

最近在看Graphic相关的代码,发现了C++中使用Parcel的方法,C++当然不能使用别人java中的Parcelable接口(这里说的Parcel是c++的Parcel类,Java层的Parcel类也是基于这个c++的native Parcel类实现),但是可以继承Flattenable模板,具体的使用方法如下,类GraphicBuffer继承了Flattenable<GraphicBuffer>模板,实现了4个Flattenable 协议相关的函数

class GraphicBuffer
    : public ANativeObjectBase< ANativeWindowBuffer, GraphicBuffer, RefBase >,
      public Flattenable<GraphicBuffer>
{
    // Flattenable protocol
    size_t getFlattenedSize() const;
    size_t getFdCount() const;
    status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
    status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
}

从下面的代码中能够看到对对象的flatten和unflatten的过程,

size_t GraphicBuffer::getFlattenedSize() const {
    return (8 + (handle ? handle->numInts : 0))*sizeof(int);
}

size_t GraphicBuffer::getFdCount() const {
    return handle ? handle->numFds : 0;
}

status_t GraphicBuffer::flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const {
    size_t sizeNeeded = GraphicBuffer::getFlattenedSize();
    if (size < sizeNeeded) return NO_MEMORY;

    size_t fdCountNeeded = GraphicBuffer::getFdCount();
    if (count < fdCountNeeded) return NO_MEMORY;

    int* buf = static_cast<int*>(buffer);
    buf[0] = 'GBFR';
    buf[1] = width;
    buf[2] = height;
    buf[3] = stride;
    buf[4] = format;
    buf[5] = usage;
    buf[6] = 0;
    buf[7] = 0;

    if (handle) {
        buf[6] = handle->numFds;
        buf[7] = handle->numInts;
        native_handle_t const* const h = handle;
        memcpy(fds,     h->data,             h->numFds*sizeof(int));
        memcpy(&buf[8], h->data + h->numFds, h->numInts*sizeof(int));
    }

    buffer = reinterpret_cast<void*>(static_cast<int*>(buffer) + sizeNeeded);
    size -= sizeNeeded;
    fds += handle->numFds;
    count -= handle->numFds;

    return NO_ERROR;
}

status_t GraphicBuffer::unflatten(
        void const*& buffer, size_t& size, int const*& fds, size_t& count) {
    if (size < 8*sizeof(int)) return NO_MEMORY;

    int const* buf = static_cast<int const*>(buffer);
    if (buf[0] != 'GBFR') return BAD_TYPE;

    const size_t numFds  = buf[6];
    const size_t numInts = buf[7];

    const size_t sizeNeeded = (8 + numInts) * sizeof(int);
    if (size < sizeNeeded) return NO_MEMORY;

    size_t fdCountNeeded = 0;
    if (count < fdCountNeeded) return NO_MEMORY;

    if (handle) {
        // free previous handle if any
        free_handle();
    }

    if (numFds || numInts) {
        width  = buf[1];
        height = buf[2];
        stride = buf[3];
        format = buf[4];
        usage  = buf[5];
        native_handle* h = native_handle_create(numFds, numInts);
        memcpy(h->data,          fds,     numFds*sizeof(int));
        memcpy(h->data + numFds, &buf[8], numInts*sizeof(int));
        handle = h;
    } else {
        width = height = stride = format = usage = 0;
        handle = NULL;
    }

    mOwner = ownHandle;

    if (handle != 0) {
        status_t err = mBufferMapper.registerBuffer(handle);
        if (err != NO_ERROR) {
            ALOGE("unflatten: registerBuffer failed: %s (%d)",
                    strerror(-err), err);
            return err;
        }
    }

    buffer = reinterpret_cast<void const*>(static_cast<int const*>(buffer) + sizeNeeded);
    size -= sizeNeeded;
    fds += numFds;
    count -= numFds;

    return NO_ERROR;
}

在java中,Parcel对象直接在Parcelable的接口实现中就生成了,但是在上面的C++中目前还没看到和Parcel的联系。
那么上面的GraphicBuffer在继承了Flattenable模板后是如何使用的呢?
从下面两个函数的注释中,就能看出来这是Serialization相关的东东,也就是和Parcel相关的,注意下面parcel->write和parcel->read的接口输入都为GraphicBuffer,而GraphicBuffer继承了Flattenable模板,

// ----------------------------------------------------------------------------
// Serialization
// ----------------------------------------------------------------------------

static void android_view_GraphiceBuffer_write(JNIEnv* env, jobject clazz,
        GraphicBufferWrapper* wrapper, jobject dest) {
    Parcel* parcel = parcelForJavaObject(env, dest);
    if (parcel) {
       // sp<GraphicBuffer> buffer
       // sp 的* 操作既是 *mptr ,也就是所指向的真实对象
        parcel->write(*wrapper->buffer);
    }
}

static GraphicBufferWrapper* android_view_GraphiceBuffer_read(JNIEnv* env, jobject clazz,
        jobject in) {

    Parcel* parcel = parcelForJavaObject(env, in);
    if (parcel) {
        sp<GraphicBuffer> buffer = new GraphicBuffer();
        parcel->read(*buffer);
        return new GraphicBufferWrapper(buffer);
    }

    return NULL;
}

那么我们需要去Parcel类的实现中,找找有没有Flattenable模板的write()和read()实现?果然有!
先看Flatten相关的,

template<typename T>
status_t Parcel::write(const Flattenable<T>& val) {
    const FlattenableHelper<T> helper(val);
    return write(helper);
}

status_t Parcel::write(const FlattenableHelperInterface& val)
{
    status_t err;

    // size if needed
    //调用getFlattenedSize()接口,该接口需要实现
    const size_t len = val.getFlattenedSize();
    //调用getFdCount()接口,该接口需要实现
    const size_t fd_count = val.getFdCount();

    err = this->writeInt32(len);
    if (err) return err;

    err = this->writeInt32(fd_count);
    if (err) return err;

    // payload
    void* const buf = this->writeInplace(PAD_SIZE(len));
    if (buf == NULL)
        return BAD_VALUE;

    int* fds = NULL;
    if (fd_count) {
        fds = new int[fd_count];
    }

    //调用flatten()接口,该接口需要实现
    err = val.flatten(buf, len, fds, fd_count);
    for (size_t i=0 ; i<fd_count && err==NO_ERROR ; i++) {
        err = this->writeDupFileDescriptor( fds[i] );
    }

    if (fd_count) {
        delete [] fds;
    }

    return err;
}

再看关于读的,也既是Unflatten相关的,

status_t Parcel::read(FlattenableHelperInterface& val) const
{
    // size
    const size_t len = this->readInt32();
    const size_t fd_count = this->readInt32();

    // payload
    void const* const buf = this->readInplace(PAD_SIZE(len));
    if (buf == NULL)
        return BAD_VALUE;

    int* fds = NULL;
    if (fd_count) {
        fds = new int[fd_count];
    }

    status_t err = NO_ERROR;
    for (size_t i=0 ; i<fd_count && err==NO_ERROR ; i++) {
        fds[i] = dup(this->readFileDescriptor());
        if (fds[i] < 0) err = BAD_VALUE;
    }

    if (err == NO_ERROR) {
    ////调用unflatten()接口,该接口需要实现
        err = val.unflatten(buf, len, fds, fd_count);
    }

    if (fd_count) {
        delete [] fds;
    }

    return err;
}

这样GraphicBuffer这个c++对象使用Parcel就可以在进程间进行传递了。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值