Day13-进程间通信

进程

进程有自己的内存地址, 一个进程中的1000地址可能在另一个进程中是10000, java的引用本质上还是内存地址, 如果要传递一个类的实例, 还需要传递方法等等, 方法是独立与类对象存在的, 所以到另一个进程中去引用同一个方法就错了, 还是因为独立内存地址的原因.
Android中Activity之间并不能保证两个Activity在同一个进程中, 比如一个APP调用系统打电话功能, 就是两个进程, 所以需要进程间通信

序列化

  • 序列化一个实例对象编码成字节流, 存入物理内存/用于传输
  • 反序列化从字节流对象中再次重新构建对象实例。

和JSON XML不同的是 JSON XML 是字符描述型对象, 它们是通用的, 不依赖于任何语言平台

SerializableLink

JVM 虚拟机中的对象, 其内部的状态只存在于内存中, JVM 停止后这些数据就丢失了, 所以考虑到持久化 , 通常是保存在文件系统或者数据库中, 比如 对象映射关系(Object-relational mapping), 对象序列化机制(Object serialization)是Java提供的一种对象持久化方式, 将JVM中的对象和字节数组流之间进行转换

过程简述: Java 的 ObjectOutputStream 类用来持久化一个对象, 通过 writeObject 方法把这个类的对象写到一个文件, 再通过 ObjectInputStream 的readObject 方法把这个对象读出来.

  • 具备 serialVersionUID, 用来标识当前序列化对象的版本, 如果需要本地存储, 建议每一个实现 Serializable 的类都指定 serialVersionUID. 如果没有指定, JVM 会根据类的信息自动生成一个 UID, 我们可以通过 JDK 的 serialver 命令来查看一个 .class 的 UID
  • transient描述的域和类的静态变量不会被序列化 (static修饰的变量会改变, 但那是因为它放在静态区, 而不是因为序列化)Link
  • transient 只能描述变量, 不能描述类和方法(局部变量无法被修饰--类的方法中定义的变量)
  • 如果一个实现了Serializable的类继承自另一个类, 那么这个类必须实现Serializable或者提供一个无参构造函数
  • 反序列化并不是通过构造器创建的,
  • 因为序列化的过程是可见的, 所以EffectiveJava的作者在第77节中希望使用静态内部类防止被攻击
过程
  • 序列化
    1. 是否替换即将写入流的对象, writeReplace, 比如使用静态内部类代理
    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);
            }
    
        }
    
        /**
         * 外围类直接替换成静态内部代理类作为真正的序列化对象
         * @return
         */
        private Object writeReplace() {
            return new SerializableProxy(this);
        }
    
        /**
         * 这里主要是为了防止攻击,任何以Persion声明的对象字节流都是流氓!!
         * 因为我在writeReplace中已经把序列化的实例指向了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++调用内存的操作, 将序列化的数据写入共享内存, 实现跨进程通信

  1. 根据这句宏定义得出,Link
#define PAD_SIZE(s) (((s)+3)&~3

内存存放机制和C++的结构体的内存对齐一样, 即读取最小字节位32bit(4字节), 如果高于4字节, 以实际数据类型存放, 但得为4的倍数

  • 占用 32 bit,(<= 32 bit), 例如: boolean, char, int
  • 实际占用字节(> 32 bit), 例如: long, float, String, 数组等

由此可以知道, 当我们写入/读取一个数据时, 偏移量至少为4Byte, 偏移量公式
f(x) = 4 * x (x=0, 1, ...)

  1. writeXXX 和 readXXX 导致的偏移量是共用的, 我们在writeInt(23)后, 此时的dataposition = 4, 读取的时候, 我们需要将偏移量置为0, 再从0开始读取4个字节, 所以需要先 setDataPosition(0), 再readInt().
  2. 如果预分配的空间不够时newSize = ((mDataSize+len) * 3)/2;会一次多分配50%;
  3. 对于不同数据的存储不一样
    • 对于普通数据, 使用的是 mData 内存地址,
    • 对于IBinder 或者 FileDescriptor, 使用的是 mObjects 内存地址, 通过 flatten_binder() 和 unflatten_binder()实现, 目的是反序列化时读出的对象就是愿对象而不是 new 出来的新对象

    测试BinderData 但是目前已经不能找不到 BinderData 这个类

有两个成员
uint8_t* mData; //用来存储序列化流数据,可以把它理解成共享内存
size_t* mObjects;  //用来存储IBinder和FileDescriptor

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

过程
  • 序列化
    • 基本类型直接 writeString / writeInt 然后调用 nativeWriteString / nativeWriteInt 用C++操作
    • 包含的子类用 writeToParcel 把类名还是用 nativeWriteString 传给C++操作
  • 反序列化
    • CREATOR
      • createFromParcel 中从流中new出对象和
      return new Pojo(in);
      

Serializable 完整传递过程

结论: Intent 传递的 Serializable 数据最后还是由 Parcel 传递给 C++ 操作, 但是 比普通的 Parcel 数据多了调用 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();
    //数据存在 BaseBundle.mMap
    mMap.put(key, value);
}

最后将 Serializable 存在了 BaseBundle 的 mMap.

再看第二句

  1. startActivity 可以理解为最后由 ActivityManager 执行, 而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操作后转换成字节流
            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机制进行数据传递, 能使用Binder的缓冲区有大小限制, 有些手机是2M
一个进程默认有16个 Binder线程, 所以一个线程所能占用的缓冲区更小了(大约一个线程128KB), 所以当出现The Binder transaction failed because it was too large, 说明数据太大.
因此Intent传递List和Bitmap对象是存在风险的

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容