Day13-進程間通信

進程

進程有自己的內存地址, 一個進程中的1000地址可能在另一個進程中是10000, java的引用本質上還是內存地址, 如果要傳遞一個類的實例, 還需要傳遞方法等等, 方法是獨立與類對象存在的, 所以到另一個進程中去引用同一個方法就錯了, 還是因為獨立內存地址的原因.
Android中Activity之間并不能保證兩個Activity在同一個進程中, 比如一個APP調用系統(tǒng)打電話功能, 就是兩個進程, 所以需要進程間通信

序列化

  • 序列化一個實例對象編碼成字節(jié)流, 存入物理內存/用于傳輸
  • 反序列化從字節(jié)流對象中再次重新構建對象實例。

和JSON XML不同的是 JSON XML 是字符描述型對象, 它們是通用的, 不依賴于任何語言平臺

SerializableLink

JVM 虛擬機中的對象, 其內部的狀態(tài)只存在于內存中, JVM 停止后這些數(shù)據(jù)就丟失了, 所以考慮到持久化 , 通常是保存在文件系統(tǒng)或者數(shù)據(jù)庫中, 比如 對象映射關系(Object-relational mapping), 對象序列化機制(Object serialization)是Java提供的一種對象持久化方式, 將JVM中的對象和字節(jié)數(shù)組流之間進行轉換

過程簡述: Java 的 ObjectOutputStream 類用來持久化一個對象, 通過 writeObject 方法把這個類的對象寫到一個文件, 再通過 ObjectInputStream 的readObject 方法把這個對象讀出來.

  • 具備 serialVersionUID, 用來標識當前序列化對象的版本, 如果需要本地存儲, 建議每一個實現(xiàn) Serializable 的類都指定 serialVersionUID. 如果沒有指定, JVM 會根據(jù)類的信息自動生成一個 UID, 我們可以通過 JDK 的 serialver 命令來查看一個 .class 的 UID
  • transient描述的域和類的靜態(tài)變量不會被序列化 (static修飾的變量會改變, 但那是因為它放在靜態(tài)區(qū), 而不是因為序列化)Link
  • transient 只能描述變量, 不能描述類和方法(局部變量無法被修飾--類的方法中定義的變量)
  • 如果一個實現(xiàn)了Serializable的類繼承自另一個類, 那么這個類必須實現(xiàn)Serializable或者提供一個無參構造函數(shù)
  • 反序列化并不是通過構造器創(chuàng)建的,
  • 因為序列化的過程是可見的, 所以EffectiveJava的作者在第77節(jié)中希望使用靜態(tài)內部類防止被攻擊
過程
  • 序列化
    1. 是否替換即將寫入流的對象, writeReplace, 比如使用靜態(tài)內部類代理
    2. 將對象寫成流, writeObject,
      • ObjectOutputStream.defaultWriteObject()默認的序列化過程
  • 反序列化
    1. 將流讀成對象, readObject
      • 如果序列化時自定義了序列化過程, 這里也需要自定義反序列化過程
    2. 是否替換從流中讀出來的對象, readResolve
  • 示例bean
    import java.io.InvalidObjectException;
    import java.io.ObjectInputStream;
    import java.io.Serializable;
    
    class Persion implements Serializable {
    
        public String desc;
        public String name;
    
        public Persion(String desc, String name) {
            this.desc = desc;
            this.name = name;
        }
    
        static class SerializableProxy implements Serializable{
            private String desc;
            private String name;
    
            private SerializableProxy(Persion s) {
                this.desc = s.desc;
                this.name = s.name;
            }
    
            /**
             * 在這里恢復外圍類
             * 注意看這里!!!最大的好處就是我們最后得到的外圍類是通過構造器構建的!
             */
            private Object readResolve() {
                return new Persion(desc,name);
            }
    
        }
    
        /**
         * 外圍類直接替換成靜態(tài)內部代理類作為真正的序列化對象
         * @return
         */
        private Object writeReplace() {
            return new SerializableProxy(this);
        }
    
        /**
         * 這里主要是為了防止攻擊,任何以Persion聲明的對象字節(jié)流都是流氓!!
         * 因為我在writeReplace中已經(jīng)把序列化的實例指向了SerializableProxy
         * @param stream
         * @throws InvalidObjectException
         */
        private void readObject(ObjectInputStream stream) throws InvalidObjectException {
            throw new InvalidObjectException("proxy requied!");
        }
    }
    
查看 serialVersionUID 的方法
  1. .dat方法查詢 Link 不推薦
  2. serialver 命令 Link
    cd 到 java 文件目錄, java文件直接 javac 類名.class編譯, 并 serialver 類名, android 需要將文件刪掉包名, 并沒有父類或父類也跟著復制過來

Parcelable

Parcel

是一系列java通過C++調用內存的操作, 將序列化的數(shù)據(jù)寫入共享內存, 實現(xiàn)跨進程通信

  1. 根據(jù)這句宏定義得出,Link
#define PAD_SIZE(s) (((s)+3)&~3

內存存放機制和C++的結構體的內存對齊一樣, 即讀取最小字節(jié)位32bit(4字節(jié)), 如果高于4字節(jié), 以實際數(shù)據(jù)類型存放, 但得為4的倍數(shù)

  • 占用 32 bit,(<= 32 bit), 例如: boolean, char, int
  • 實際占用字節(jié)(> 32 bit), 例如: long, float, String, 數(shù)組等

由此可以知道, 當我們寫入/讀取一個數(shù)據(jù)時, 偏移量至少為4Byte, 偏移量公式
f(x) = 4 * x (x=0, 1, ...)

  1. writeXXX 和 readXXX 導致的偏移量是共用的, 我們在writeInt(23)后, 此時的dataposition = 4, 讀取的時候, 我們需要將偏移量置為0, 再從0開始讀取4個字節(jié), 所以需要先 setDataPosition(0), 再readInt().
  2. 如果預分配的空間不夠時newSize = ((mDataSize+len) * 3)/2;會一次多分配50%北启;
  3. 對于不同數(shù)據(jù)的存儲不一樣
    • 對于普通數(shù)據(jù), 使用的是 mData 內存地址,
    • 對于IBinder 或者 FileDescriptor, 使用的是 mObjects 內存地址, 通過 flatten_binder() 和 unflatten_binder()實現(xiàn), 目的是反序列化時讀出的對象就是愿對象而不是 new 出來的新對象

    測試BinderData 但是目前已經(jīng)不能找不到 BinderData 這個類

有兩個成員
uint8_t* mData; //用來存儲序列化流數(shù)據(jù)诀黍,可以把它理解成共享內存
size_t* mObjects;  //用來存儲IBinder和FileDescriptor

引用和IBinder的序列化方式不一樣

過程
  • 序列化
    • 基本類型直接 writeString / writeInt 然后調用 nativeWriteString / nativeWriteInt 用C++操作
    • 包含的子類用 writeToParcel 把類名還是用 nativeWriteString 傳給C++操作
  • 反序列化
    • CREATOR
      • createFromParcel 中從流中new出對象和
      return new Pojo(in);
      

Serializable 完整傳遞過程

結論: Intent 傳遞的 Serializable 數(shù)據(jù)最后還是由 Parcel 傳遞給 C++ 操作, 但是 比普通的 Parcel 數(shù)據(jù)多了調用 Stream 的 I/O 操作
傳遞是靠這兩句

intent.putExtra("myserializabledata", persion);
startActivity(intent);

先看第一句

  1. Intent.putExtra
public Intent putExtra(String name, Serializable value) {
        if (mExtras == null) {
            mExtras = new Bundle();
        }
        //調用了 Bundle.putSerializable
        mExtras.putSerializable(name, value);
        return this;
    }
  1. -> Bundle.putSerializable
@Override
public void putSerializable(@Nullable String key, @Nullable Serializable value) {
    //調用了BaseBundle.putSerializable
    super.putSerializable(key, value);
}
  1. -> BaseBundle.putSerializable
void putSerializable(@Nullable String key, @Nullable Serializable value) {
    unparcel();
    //數(shù)據(jù)存在 BaseBundle.mMap
    mMap.put(key, value);
}

最后將 Serializable 存在了 BaseBundle 的 mMap.

再看第二句

  1. startActivity 可以理解為最后由 ActivityManager 執(zhí)行, 而ActivityManager在源碼中各版本不一定一致, 先看4.4.4的
public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, String profileFile,
            ParcelFileDescriptor profileFd, Bundle options) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);

        ...

        if (options != null) {
            data.writeInt(1);
            //這里調用 Bundle.writeToParcel
            options.writeToParcel(data, 0);
        } else {
            data.writeInt(0);
        }

        ...

        return result;
    }
  1. -> Bundle.writeToParcel
@Override
 public void writeToParcel(Parcel parcel, int flags) {
     final boolean oldAllowFds = parcel.pushAllowFds((mFlags & FLAG_ALLOW_FDS) != 0);
     try {
       //調用 BaseBundle.writeToParcelInner
         super.writeToParcelInner(parcel, flags);
     } finally {
         parcel.restoreAllowFds(oldAllowFds);
     }
 }
  1. -> BaseBundle.writeToParcelInner(parcel, flags)
void writeToParcelInner(Parcel parcel, int flags) {
        // Keep implementation in sync with writeToParcel() in
        // frameworks/native/libs/binder/PersistableBundle.cpp.
        final Parcel parcelledData;
        synchronized (this) {
            parcelledData = mParcelledData;
        }
        if (parcelledData != null) {
            if (isEmptyParcel()) {
                parcel.writeInt(0);
            } else {
                int length = parcelledData.dataSize();
                parcel.writeInt(length);
                parcel.writeInt(BUNDLE_MAGIC);
                parcel.appendFrom(parcelledData, 0, length);
            }
        } else {
            // Special case for empty bundles.
            if (mMap == null || mMap.size() <= 0) {
                parcel.writeInt(0);
                return;
            }
            int lengthPos = parcel.dataPosition();
            parcel.writeInt(-1); // dummy, will hold length
            parcel.writeInt(BUNDLE_MAGIC);

            int startPos = parcel.dataPosition();
            // 調用 Parcel.writeArrayMapInternal
            parcel.writeArrayMapInternal(mMap);
            int endPos = parcel.dataPosition();

            // Backpatch length
            parcel.setDataPosition(lengthPos);
            int length = endPos - startPos;
            parcel.writeInt(length);
            parcel.setDataPosition(endPos);
        }
    }
  1. -> Parcel.writeArrayMapInternal(mMap)
void writeArrayMapInternal(ArrayMap<String, Object> val) {
    if (val == null) {
        writeInt(-1);
        return;
    }
    // Keep the format of this Parcel in sync with writeToParcelInner() in
    // frameworks/native/libs/binder/PersistableBundle.cpp.
    final int N = val.size();
    writeInt(N);
    if (DEBUG_ARRAY_MAP) {
        RuntimeException here =  new RuntimeException("here");
        here.fillInStackTrace();
        Log.d(TAG, "Writing " + N + " ArrayMap entries", here);
    }
    int startPos;
    for (int i=0; i<N; i++) {
        if (DEBUG_ARRAY_MAP) startPos = dataPosition();
        writeString(val.keyAt(i));
        //開始操作 map 中的 value
        writeValue(val.valueAt(i));
        if (DEBUG_ARRAY_MAP) Log.d(TAG, "  Write #" + i + " "
                + (dataPosition()-startPos) + " bytes: key=0x"
                + Integer.toHexString(val.keyAt(i) != null ? val.keyAt(i).hashCode() : 0)
                + " " + val.keyAt(i));
    }
}

這里操作了第一步生成的 mMAp

  1. -> parcel.writeValue
public final void writeValue(Object v) {
       if (v == null) {
           writeInt(VAL_NULL);
       } else if (v instanceof String) {
           writeInt(VAL_STRING);
           writeString((String) v);
       } else if (v instanceof Integer) {
           writeInt(VAL_INTEGER);
           writeInt((Integer) v);
       } else if (v instanceof Map) {
           writeInt(VAL_MAP);
           writeMap((Map) v);
       } else if (v instanceof Bundle) {
           // Must be before Parcelable
           writeInt(VAL_BUNDLE);
           writeBundle((Bundle) v);
       } else if (v instanceof PersistableBundle) {
           writeInt(VAL_PERSISTABLEBUNDLE);
           writePersistableBundle((PersistableBundle) v);
       } else if (v instanceof Parcelable) {
           // IMPOTANT: cases for classes that implement Parcelable must
           // come before the Parcelable case, so that their specific VAL_*
           // types will be written.
           writeInt(VAL_PARCELABLE);
           writeParcelable((Parcelable) v, 0);
       } else {
           Class<?> clazz = v.getClass();
           if (clazz.isArray() && clazz.getComponentType() == Object.class) {
               // Only pure Object[] are written here, Other arrays of non-primitive types are
               // handled by serialization as this does not record the component type.
               writeInt(VAL_OBJECTARRAY);
               writeArray((Object[]) v);
           } else if (v instanceof Serializable) {
               // Must be last
               writeInt(VAL_SERIALIZABLE);
               //對 Serializable 操作
               writeSerializable((Serializable) v);
           } else {
               throw new RuntimeException("Parcel: unable to marshal value " + v);
           }
       }
   }  

在writeValue的最后,處理了 Serializable

  1. -> Parcel.writeSerializable()
public final void writeSerializable(Serializable s) {
        if (s == null) {
            writeString(null);
            return;
        }
        String name = s.getClass().getName();
        writeString(name);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(s);
            oos.close();
            //將Serializable I/O操作后轉換成字節(jié)流
            writeByteArray(baos.toByteArray());
        } catch (IOException ioe) {
            throw new RuntimeException("Parcelable encountered " +
                "IOException writing serializable object (name = " + name +
                ")", ioe);
        }
    }
  1. -> Parcel.writeByteArray()
public final void writeByteArray(byte[] b) {
     //對流判斷
     writeByteArray(b, 0, (b != null) ? b.length : 0);
 }
  1. -> Parcel.nativeWriteByteArray()
public final void writeByteArray(byte[] b, int offset, int len) {
   if (b == null) {
       writeInt(-1);
       return;
   }
   Arrays.checkOffsetAndCount(b.length, offset, len);
   //調用了 native 方法
   nativeWriteByteArray(mNativePtr, b, offset, len);
}

Intent

Intent 的 bundle 使用Binder機制進行數(shù)據(jù)傳遞, 能使用Binder的緩沖區(qū)有大小限制, 有些手機是2M
一個進程默認有16個 Binder線程, 所以一個線程所能占用的緩沖區(qū)更小了(大約一個線程128KB), 所以當出現(xiàn)The Binder transaction failed because it was too large, 說明數(shù)據(jù)太大.
因此Intent傳遞List和Bitmap對象是存在風險的

參考

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末萤厅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蕴茴,更是在濱河造成了極大的恐慌锹漱,老刑警劉巖腋逆,帶你破解...
    沈念sama閱讀 218,204評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異监嗜,居然都是意外死亡谐檀,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評論 3 395
  • 文/潘曉璐 我一進店門秤茅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來稚补,“玉大人,你說我怎么就攤上這事框喳】文唬” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評論 0 354
  • 文/不壞的土叔 我叫張陵五垮,是天一觀的道長乍惊。 經(jīng)常有香客問我,道長放仗,這世上最難降的妖魔是什么润绎? 我笑而不...
    開封第一講書人閱讀 58,657評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮诞挨,結果婚禮上莉撇,老公的妹妹穿的比我還像新娘。我一直安慰自己惶傻,他們只是感情好棍郎,可當我...
    茶點故事閱讀 67,689評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著银室,像睡著了一般涂佃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蜈敢,一...
    開封第一講書人閱讀 51,554評論 1 305
  • 那天辜荠,我揣著相機與錄音,去河邊找鬼抓狭。 笑死伯病,一個胖子當著我的面吹牛,可吹牛的內容都是我干的否过。 我是一名探鬼主播午笛,決...
    沈念sama閱讀 40,302評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼膨蛮,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了季研?” 一聲冷哼從身側響起敞葛,我...
    開封第一講書人閱讀 39,216評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎与涡,沒想到半個月后惹谐,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡驼卖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,851評論 3 336
  • 正文 我和宋清朗相戀三年氨肌,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片酌畜。...
    茶點故事閱讀 39,977評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡怎囚,死狀恐怖,靈堂內的尸體忽然破棺而出桥胞,到底是詐尸還是另有隱情恳守,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評論 5 347
  • 正文 年R本政府宣布贩虾,位于F島的核電站催烘,受9級特大地震影響,放射性物質發(fā)生泄漏缎罢。R本人自食惡果不足惜伊群,卻給世界環(huán)境...
    茶點故事閱讀 41,306評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望策精。 院中可真熱鬧舰始,春花似錦、人聲如沸咽袜。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽酬蹋。三九已至及老,卻和暖如春抽莱,著一層夾襖步出監(jiān)牢的瞬間范抓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評論 1 270
  • 我被黑心中介騙來泰國打工食铐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留匕垫,地道東北人。 一個月前我還...
    沈念sama閱讀 48,138評論 3 370
  • 正文 我出身青樓虐呻,卻偏偏與公主長得像象泵,于是被迫代替她去往敵國和親寞秃。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,927評論 2 355

推薦閱讀更多精彩內容