AndroidのParcelとParcelable

11585 ワード

まとめ:
  • Parcelクラスは1つのコンテナであり、様々なタイプのデータを入れることができ、C/CPPの下位層で伝送することができる.
  • ParcelはBinderドライバで異なるプロセスのためにデータインタラクションを実現できるが、なぜこのような役割を果たすことができるのか.システム設計が可能である.異なるプロセスはJava層では互いに独立しており、異なるメモリ空間を持っているが、下位層にはbinderドライバ(binder.cpp)がそれらを統一して相互接続を実現しているが、Parcelは3層コード(Java-JNI-Native、もちろんBinderも)を実現しているため、javaでC層を操作することができる.したがって,Parcelを用いてデータをロードし,ツールクラスbinderドライバ(binder.cpp)で処理した結果,Native層を介してJava層にコールバックするという操作の組み合わせが最適である.

  •  
     
    Parcelに比べて、私たちはParcelableによく接触しています.2つの単語は似ていますが、違いはありますか?
  • 同じ点:両方ともAndroid専用のシステムクラス.
  • Parcelは、プロセス間通信、データ転送のためのエンティティークラスです.
  • Parcelableは、Androidの高性能シーケンス化に使用されるインターフェース(詳細はアクセスしてください)
  • public final class Parcel {.....}
    public interface Parcelable {.....}

    Parcelableとは何かを知ってから、Parcelクラスを知ってみましょう.
     
    AIDLによって自動的に生成されるファイルには、次のコードの例があります.
    //     
    @Override
    public void addBook(com.test.aidl_demo.Book book) throws android.os.RemoteException {
          android.os.Parcel _data = android.os.Parcel.obtain();
          android.os.Parcel _reply = android.os.Parcel.obtain();
          try {
              _data.writeInterfaceToken(DESCRIPTOR);
              if ((book != null)) {
                     _data.writeInt(1);
                     book.writeToParcel(_data, 0); // Book      ,   Parcelable  
              } else {
                      _data.writeInt(0);
              }
              mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
              _reply.readException();
           } finally {
              _reply.recycle();
              _data.recycle();
           }
    }

     
    Parcelクラス
    Parcelは、もともと「包む、包む」という意味で、Androidでは、主にシーケンス化されたデータを格納し、Binderを介してプロセス間でデータを転送するコンテナです.転送データ型は次のとおりです.
  • 元データ型(writeInt()やwriteFloat()など、様々な対応方法で書き込む)
  • Parcelableオブジェクト(例:writeParcelable()、writeParcelableCreator()等)
  • IBinderオブジェクトの参照(例:writeStrongBinder())
  • 検索コードで発見(Andoridソースをダウンロードする必要がある)、AndroidはJava-Jni-CにParcelクラスがあり、
  • Java層:/frameworks/base/core/java/android/os/parcel.java
  • JNI  レイヤ:/frameworks/base/core/jni/android_os_Parcel.cpp
  • C++層:/frameworks/native/libs/binder/Parcel.cpp
  • Javaレイヤコードはパッケージエージェントにすぎず、C++レイヤで実装されていることがわかります.
     
    呼び出しプロシージャ
  • 取得Parcel :通過obtain()静的方法
  • データの格納と読み出し:通過writeXXX()和  readXXX()実現、
  • データのシーケンス化と逆シーケンス化:marshall()と  unmarshall() ,
  • 回収資源:通過recycle()
  •  
     
    1、Parcelオブジェクトの取得方法
     Parcel.java :
    
        //        Parcel ,         ,    Parcel     ,       
        // Parcel         
        private static final int POOL_SIZE = 6;
        private static final Parcel[] sOwnedPool = new Parcel[POOL_SIZE];
    
    
        public static Parcel obtain() {
            final Parcel[] pool = sOwnedPool;
            synchronized (pool) {
                Parcel p;
                for (int i=0; i(parcel); 
    }
    
    
    
     Native (Parcel.cpp):
    
    
    Parcel::Parcel()
    {
        initState();
    }
    
    //          
    void Parcel::initState()
    {
        mError = NO_ERROR;  //     
        mData = 0;          //     1,         ,           
        mDataSize = 0;      //      
        mDataCapacity = 0;  //    (       )  ,        
        mDataPos = 0;       //               
    
        ALOGV("initState Setting data size of %p to %zu", this, mDataSize);
        ALOGV("initState Setting data pos of %p to %zu", this, mDataPos);
    
        mObjects = NULL;    //     2,    Binder    
        mObjectsSize = 0;
        mObjectsCapacity = 0;
        mNextObjectHint = 0;
        mHasFds = false;
        mFdsKnown = true;
        mAllowFds = true;
        mOwner = NULL;
    }
    
    Parcel            , mData mObjects,mData         ,mObjects  Binder    。
    mData  parcel      ,
    mDataCapacity  parcel    (  ),
    mDataPos  parcel           ,  parcel          。
    
         =     +    ,
    reinterpret_cast c++      ,    
    

     
    2、データの書き込み
             ---       
    
    Java (Parcel.java)
    
        public final void writeInt(int val) {
            nativeWriteInt(mNativePtr, val);
        }
    
        private static native void nativeWriteInt(long nativePtr, int val);
    
    
    JNI  :
    
    static const JNINativeMethod gParcelMethods[] = {
        ......
        {"nativeWriteInt",            "(JI)V", (void*)android_os_Parcel_writeInt},
        {"nativeWriteLong",           "(JJ)V", (void*)android_os_Parcel_writeLong},
        ......    
    };
    
    
    
    static void android_os_Parcel_writeInt(JNIEnv* env, jclass clazz, jlong nativePtr, jint val) {
        Parcel* parcel = reinterpret_cast(nativePtr); //           
        const status_t err = parcel->writeInt32(val);
        if (err != NO_ERROR) {
            signalExceptionForError(env, clazz, err);
        }
    }
    
    
    
     Native   :
    status_t Parcel::writeInt32(int32_t val) // 32  4   
    {
        return writeAligned(val); //     
    }
    
    template
    status_t Parcel::writeAligned(T val) {
        //     
        COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T));
    
        //            ,        
        if ((mDataPos+sizeof(val)) <= mDataCapacity) { //     ,     
    
    // goto     
    restart_write:
            //       ,        ,    。
            *reinterpret_cast(mData+mDataPos) = val; //    *p = value
            return finishWrite(sizeof(val)); //     ,          
    
        }
    
        //        ,   
        status_t err = growData(sizeof(val));
        if (err == NO_ERROR) goto restart_write; //     goto  ,     
        return err;
    }
    
    
    
    status_t Parcel::finishWrite(size_t len)
    {
        mDataPos += len;
        ALOGV("finishWrite Setting data pos of %p to %zu", this, mDataPos);
        if (mDataPos > mDataSize) {
            // mDataSize:    mData        
            // mDataPos             ,          ,         。
            mDataSize = mDataPos; 
            ALOGV("finishWrite Setting data size of %p to %zu", this, mDataSize);
        }
        return NO_ERROR;
    }
    
    
    
    status_t Parcel::growData(size_t len)
    {
        size_t newSize = ((mDataSize+len)*3)/2; //      (    +      )* 1.5 
        return (newSize <= mDataSize)
                ? (status_t) NO_MEMORY
                : continueWrite(newSize);
    }

    3、データの読み出し
    Java :
    
        //         ,           Parcel   
        private long mNativePtr; // used by native code
    
        public final int readInt() {
            return nativeReadInt(mNativePtr);
        }
    
        private static native int nativeReadInt(long nativePtr);
    
    
    
    
    JNI :
    
    static jint android_os_Parcel_readInt(JNIEnv* env, jclass clazz, jlong nativePtr)
    {
        Parcel* parcel = reinterpret_cast(nativePtr);
        if (parcel != NULL) {
            return parcel->readInt32();
        }
        return 0;
    }
    
    
    
    
     Native  :
    
    int32_t Parcel::readInt32() const
    {
        return readAligned();
    }
    // int32_t:int_t         ,     type/typedef   ,
       int32_t   : typedef signed int;
       uint32_t  : typedef unsigned int;
    //       typedef   ,           。     ,            ,
    //         typedef          。
    
    
    
    template
    status_t Parcel::readAligned(T *pArg) const {
        COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE(sizeof(T)) == sizeof(T));
    
        if ((mDataPos+sizeof(T)) <= mDataSize) { //   
            const void* data = mData+mDataPos; 
            mDataPos += sizeof(T); //          
            *pArg =  *reinterpret_cast(data);
            return NO_ERROR;
        } else {
            return NOT_ENOUGH_DATA;
        }
    }
    
      :       ,
    1、      parcel     (mData) + parcel    (mDataPos),      ,
    2、      ,   parcel      +          ,
    3、              
    
    
          mData+mDataPos      ,    T     (T              ),   val          。        
    
    
    

     
    4、どのようにしてParcelオブジェクトを回収しますか?
     Java  (Parcel.java):
    
        public final void recycle() {
            if (DEBUG_RECYCLE) mStack = null;
            freeBuffer();
    
            final Parcel[] pool;
            if (mOwnsNativeParcelObject) {
                pool = sOwnedPool;
            } else {
                mNativePtr = 0;
                pool = sHolderPool;
            }
    
            synchronized (pool) {
    
                 //          ,   ,  parcel     ,      
                 //         ,         ,    
                for (int i=0; i(nativePtr);
        if (parcel != NULL) {
            parcel->freeData();
        }
    }
    
    
    
    
     Native  :
    
    void Parcel::freeData()
    {
        freeDataNoInit(); //     
        initState();      //       
    }
    
    void Parcel::freeDataNoInit()
    {
        if (mOwner) {
            //ALOGI("Freeing data ref of %p (pid=%d)", this, getpid());
            mOwner(this, mData, mDataSize, mObjects, mObjectsSize, mOwnerCookie);
        } else {
            releaseObjects();
            if (mData) free(mData);
            if (mObjects) free(mObjects);
        }
    }
    
    
    
    void Parcel::initState()
    {
        mError = NO_ERROR;
        mData = 0;
        mDataSize = 0;
        mDataCapacity = 0;
        mDataPos = 0;
        ALOGV("initState Setting data size of %p to %zu", this, mDataSize);
        ALOGV("initState Setting data pos of %p to %zu", this, mDataPos);
        mObjects = NULL;
        mObjectsSize = 0;
        mObjectsCapacity = 0;
        mNextObjectHint = 0;
        mHasFds = false;
        mFdsKnown = true;
        mAllowFds = true;
        mOwner = NULL;
    }
    
    
    
    void Parcel::releaseObjects()
    {
        const sp proc(ProcessState::self());
        size_t i = mObjectsSize;
        uint8_t* const data = mData;
        binder_size_t* const objects = mObjects;
        while (i > 0) {
            i--;
            const flat_binder_object* flat
                = reinterpret_cast(data+objects[i]);
            release_object(proc, *flat, this);
        }
    }
    
    
    void release_object(const sp& proc,
        const flat_binder_object& obj, const void* who)
    {
        switch (obj.type) {
            case BINDER_TYPE_BINDER:
                if (obj.binder) {
                    LOG_REFS("Parcel %p releasing reference on local %p", who, obj.cookie);
                    reinterpret_cast(obj.cookie)->decStrong(who);
                }
                return;
            case BINDER_TYPE_WEAK_BINDER:
                if (obj.binder)
                    reinterpret_cast<:weakref_type>(obj.binder)->decWeak(who);
                return;
            case BINDER_TYPE_HANDLE: {
                const sp b = proc->getStrongProxyForHandle(obj.handle);
                if (b != NULL) {
                    LOG_REFS("Parcel %p releasing reference on remote %p", who, b.get());
                    b->decStrong(who);
                }
                return;
            }
            case BINDER_TYPE_WEAK_HANDLE: {
                const wp b = proc->getWeakProxyForHandle(obj.handle);
                if (b != NULL) b.get_refs()->decWeak(who);
                return;
            }
            case BINDER_TYPE_FD: {
                if (obj.cookie != 0) close(obj.handle);
                return;
            }
        }
    
        ALOGE("Invalid object type 0x%08x", obj.type);
    }