<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                [TOC] ## Serializable(謹慎使用序列化) 序列化和持久化很相似,有些人甚至混為一談,其實還是有區別的,**序列化是為了解決對象的傳輸問題,傳輸可以在線程之間、進程之間、內存外存之間、主機之間進行**。在安卓中絕大部分場景是通過Binder來進行對象的傳輸. ### serialVersionUID 如果沒有指定UID,那么JDK會自動幫我們生成一個UID.手動指定這個UID可以減少運行時的開銷,如果我們不加,系統后根據通過一套復雜的運算,自動賦值,該值與類名,接口名,成員名都有關. ### 增加了潛在的風險 引入了隱藏的構造函數 ### 測試的代價大 每個版本都得保證序列化和反序列化的成功 ### 保護字段不被序列化 1. transient關鍵字修飾的變量,不會被序列化;Transient 關鍵字的作用是控制變量的序列化,在變量聲明前加上該關鍵字,可以阻止該變量被序列化到文件中,在被反序列化后,transient 變量的值被設為初始值,如 int 型的是 0,對象型的是 null。 2. static關鍵字修飾的也不會被序列化, 因為static是類的特征,和對象無關。 3. 放在父類中,如果父類實現了序列化,子類默認實現序列化。 ### 原理 ![](https://img.kancloud.cn/6b/a4/6ba4a5d08cc26eec260570795eca819c_740x286.png) ## Parcelable ### 源碼解析 首先我們在一個實體對象在實現parcelable的時候,這個時候,我們會重寫writeToParcel方法,其中執行 dest.writeInt(this.offLineBtn); writeLong 等等類型的數據,實際是執行native方法,在這里我們就不分析各種數據類型的存取了,我們現在拿一個代表int來分析下,看下jni方法: ~~~ static void android_os_Parcel_writeInt(JNIEnv* env, jclass clazz, jint nativePtr, jint val) { Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr); const status_t err = parcel->writeInt32(val); if (err != NO_ERROR) { signalExceptionForError(env, clazz, err); } } ~~~ 在這里我們要特別注意兩個參數,一個是之前傳上去的指針以及需要保存的int數據,這兩個值分別是: (jint nativePtr,jint val) 首先是根據這個指針,這里說一下,指針實際上就是一個整型地址值,所以這里使用強轉將int值轉化為parcel類型的指針是可行的,然后使用這個指針來操作native的parcel對象,即: const status\_t err = parcel->writeInt32(val); writeInt32是調用了parcel中的方法,parcel的實現類是在Framework/native/libsbinderParcel.cpp,我們看下writeInt32方法: ~~~ status_t Parcel::writeInt32(int32_t val) { return writeAligned(val); } ~~~ ~~~ status_t Parcel::writeAligned(T val) { COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(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; ~~~ 分析上面的之前,首先要知道mData、mDataPos、mDataCapacity三個變量的意義,mData指向parcel緩存的首地址,mDataCapacity表示parcel緩存容量(大小),mDataPos指向parcel緩存中空閑區域的首地址**,整個parcel緩存是一塊連續的內存。** **物理地址 = 有效地址+偏移地址,首先會判斷先寫入的int數據的字節數是否超過了data的容量,如果沒有超過,會執行數據的寫入,**reinterpret\_cast是c++的一種再解釋,強制轉換,上面首先會將mData+mDataPos得到物理地址,轉成指向T類型的指針(T類型就是你傳進來的變量的類型),然后將val賦值給指針指向的內容。然后修改偏移地址,finishWrite(sizeof(val)): ~~~ status_t Parcel::finishWrite(size_t len) { if (len > INT32_MAX) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; } //printf("Finish write of %d\n", len); mDataPos += len; ALOGV("finishWrite Setting data pos of %p to %zu", this, mDataPos); if (mDataPos > mDataSize) { mDataSize = mDataPos; ALOGV("finishWrite Setting data size of %p to %zu", this, mDataSize); } //printf("New pos=%d, size=%d\n", mDataPos, mDataSize); return NO_ERROR; } ~~~ 上面主要是將修改偏移地址,將偏移地址加上新增加的數據的字節數。 如果增加的數據大于容量的話,那么首先擴展parcel的緩存空間,growData(sizeof(val)): ~~~ status_t Parcel::growData(size_t len) { if (len > INT32_MAX) { // don't accept size_t values which may have come from an // inadvertent conversion from a negative int. return BAD_VALUE; } size_t newSize = ((mDataSize+len)*3)/2; return (newSize <= mDataSize) ? (status_t) NO_MEMORY : continueWrite(newSize); } ~~~ 擴展成功,就繼續goto restart\_write,在writeAligned方法中有restart\_write,執行restart\_write后面code,寫入數據。 通過上面的解釋相信大家已經明白int類型的數據寫入parcel緩存了,既然知道存數據,那我們也要明白取數據了,在取數據的時候,我們會通過this.age = in.readInt();來取得int類型數據 ~~~ static jint android_os_Parcel_readInt(jlong nativePtr) { Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr); if (parcel != NULL) { return parcel->readInt32(); } return 0; } ~~~ 調用的parcel的readInt32方法: ~~~ int32_t Parcel::readInt32() const { return readAligned<int32_t>(); } T Parcel::readAligned() const { T result; if (readAligned(&result) != NO_ERROR) { result = 0; } return result; } status_t Parcel::readAligned(T *pArg) const { COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T)); if ((mDataPos+sizeof(T)) <= mDataSize) { const void* data = mData+mDataPos; mDataPos += sizeof(T); *pArg = *reinterpret_cast<const T*>(data); return NO_ERROR; } else { return NOT_ENOUGH_DATA; } ~~~ 讀取數據的時候,首先我們會從parcel的起始地址+parcel偏移地址,得到讀取的數據的地址,然后取出數據,然后將parcel的偏移地址+取出的數據的字節數,這樣指針就可以指向下一個數據,這樣說太抽象了,舉個例子: 比如我們現在有一個對象,里面是 ~~~ stu{ int age = 32; double score = 99; } ~~~ 我們在寫數據的時候,會在一塊parcel的內存地址中,寫32,99,然后讀取的時候,會從起始地址+讀取的字節數,來一一讀取,首先讀取parcel起始地址指向的數據,取出32,然后將指針地址偏移int字節數,指針指向99的地址,然后讀取99,然后取出數據,這也就是parcelable在實現的時候為什么需要存和讀取的順序需要一致的原因。 ### 參考資料 [Parcelable最強解析](https://segmentfault.com/a/1190000012522154#articleHeader1) ## Serializable 和Parcelable 的區別 ### 作用 Serializa ble的作用是為了保存對象的屬性到本地文件、數據庫、網絡流、RMI(Remote Method Invocation)以方便數據傳輸,當然這種傳輸可以是程序內的也可以是兩個程序間的。使用了反射技術,并且期間產生臨時對象 Android的Parcelable的設計初衷是因為Serializable效率過慢,為了在程序內不同組件間以及不同Android程序間(AIDL)高效的傳輸數據而設計,這些數據僅在內存中存在,Parcelable是通過IBinder通信的消息的載體。 ### 效率及選擇 Parcelable的性能比Serializable好,在內存開銷方面較小,所以在內存間數據傳輸時推薦使用Parcelable,如activity間傳輸數據,而Serializable可將數據持久化方便保存,所以在需要保存或網絡傳輸數據時選擇Serializable,因為android不同版本Parcelable可能不同,所以不推薦使用Parcelable進行數據持久化 ### 高級功能上 Serializable序列化不保存靜態變量,可以使用transient關鍵字對部分字段不進行序列化,也可以覆蓋writeObject、readObject方法以實現序列化過程自定義 如果用transient聲明一個實例變量,當對象存儲時,它的值不需要維持。換句話說,用transient關鍵字標記的成員變量不參與序列化過程。 ###編程實現 對于Serializable,類只需要實現Serializable接口,并提供一個序列化版本id(serialVersionUID)即可。 而Parcelable則需要實現writeToParcel、describeContents函數以及靜態的CREATOR變量(AS有相關插件 一鍵生成所需方法),實際上就是將如何打包和解包的工作自己來定義,而序列化的這些操作完全由底層實現。 ## Bundle ### 源碼解析 Bundle位于android.os包中,是一個final類,這就注定了Bundle不能被繼承。Bundle繼承自BaseBundle并實現了Cloneable和Parcelable兩個接口,因此對Bundle源碼的分析會結合著對BaseBundle源碼進行分析。**由于實現了Cloneable和Parcelable接口**,因此以下幾個重載是必不可少的: * public Object clone() * public int describeContents() * public void writeToParcel(Parcel parcel, int flags) * public void readFromParcel(Parcel parcel) * public static final Parcelable.Creator CREATOR = new Parcelable.Creator() 以上代碼無需過多解釋。 Bundle的功能是用來保存數據,那么必然提供了一系列Bundle的put與get方法族數據的方法,這些方法太多了,幾乎能夠存取任何類型的數據; ### 重點 Bundle之所以能以鍵值對的方式存儲數據,實質上是因為它內部維護了一個ArrayMap,具體定義是在其父類BaseBundle中: ~~~ ArrayMap<String, ObjectmMap = null; ~~~ : ~~~ void putBoolean(String key, boolean value) { unparcel(); mMap.put(key, value); } ~~~ 這里的mMap就是ArrayMap了,存儲數據就是把鍵值對保存到ArrayMap里。 布爾類型數據的讀取源碼如下: ~~~ boolean getBoolean(String key) { unparcel(); if (DEBUG) Log.d(TAG, "Getting boolean in "+ Integer.toHexString(System.identityHashCode(this))); return getBoolean(key, false); } ~~~ getBoolean(String key, boolean defaultValue)的具體實現如下: ~~~ boolean getBoolean(String key, boolean defaultValue) { unparcel(); Object o = mMap.get(key); if (o == null) { return defaultValue; } try { return (Boolean) o; } catch (ClassCastException e) { typeWarning(key, o, "Boolean", defaultValue, e); return defaultValue; } } ~~~ 數據讀取的邏輯也很簡單,就是通過key從ArrayMap里讀出保存的數據,并轉換成對應的類型返回,當沒找到數據或發生類型轉換異常時返回缺省值。 注意到這里出現了一個方法:unparcel(),它的具體源碼如下: ~~~ synchronized void unparcel() { if (mParcelledData == null) { return; } if (mParcelledData == EMPTY_PARCEL) { if (mMap == null) { mMap = new ArrayMap<String, Object>(1); } else { mMap.erase(); } mParcelledData = null; return; } int N = mParcelledData.readInt();////通過parel可以看出這個方法通過調用native方法返回當前data的dataposition if (N < 0) { return; } if (mMap == null) { mMap = new ArrayMap<String, Object>(N); } else { mMap.erase(); mMap.ensureCapacity(N); } //讀取數據并存放到mMap中 mParcelledData.readArrayMapInternal(mMap, N, mClassLoader); //回收解包完的數據 mParcelledData.recycle(); mParcelledData = null; } ~~~ ### 參考資料 [Bundle源碼解析](https://blog.csdn.net/qq_33288248/article/details/70143275) [序列化原理](https://blog.csdn.net/u011315960/article/details/89963230)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看