IPC机制(一)——IPC基础概念

2019-10-05  本文已影响0人  李die喋

Android中的多进程

IPC学过操作系统的都应该知道,就是进程间通信或者跨进程通信。Android是基于Linux内核的移动操作系统,它的进程间通信方式并不是完全继承于Linux,它有自己的进程间通信方式。

多进程通信的主要方式

开启多进程模式

Android中使用多进程只有一种方法,就是给四大组件在AndroidMenifest中指定android:process属性,除此之外没有其他办法。但有一种非常规的办法就是通过JNI在native层去fork一个新的进程。fork在linux系统上就可以尝试。

<activity>
    <android:process=":remote"/>
    或者
    <android:process="com.lxy.ipc.remote"/>
</activity>

两种方式的区别:

以“:”开头的是一种简写的方法,完整的进程名称是com.lxy.ipc:remote。也就是在当前进程名前加上了当前的包名。且以“:”开头的进程属于当前进程的私有进程,其它应用组件不可以和它跑在同一进程中。

第二中命名方式是完整的命名方式,不会附加包名信息,属于全局进程,其它应用可以通过sharedUserID和它跑在同一进程中。

//第一个程序的menifest文件
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.lxy.client"
    android:versionCode="1"
    android:versionName="1.0"
    android:sharedUserId="com.lxy.share">
    
//第二个程序的menifest文件
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.lxy.service"
    android:versionCode="1"
    android:versionName="1.0"
    android:sharedUserId="com.lxy.share">   

多进程模式的运行机制

在学操作系统的时候我们知道每一个进程的都有自己的进程控制块(PCB),android在多进程模式中,不同进程的组件有自己独立的虚拟机、Application、内存空间

举个例子来说:

UserManager类中有一个静态变量

public static int userId = 1;

按理来说静态变量应该是被所有地方共享的,在一个进程中进行加一操作,在其他进程打印,发现结果为1。这说明多进程不是添加了一个android:process属性这么简单。每一个进程都有一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这会导致在不同的虚拟机中访问同一个类对象(静态变量属于类对象)会产生多份副本

不同进程中的组件几乎都需要共享数据。使用多进程会造成如下几方面的问题:

分配不同的虚拟机,产生多个对象副本

不在同一块内存中,所以锁对象和锁全局类都达不到效果

SP不支持两个进程同时执行写操作,否则会导致数据一定几率的丢失。SP的底层是通过读写XML来文件实现的,并发写/读都可能出现问题。

创建新的进程就是要分配独立的虚拟机,所以就是启动一个新的应用的过程,重新启动就会创建新的Application。

Serializable

Serializable是java提供的一个序列化接口,是一个空接口。

public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;
    、、、
}

序列化和反序列化过程:

//序列化
User user = new User("ldd",20);
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cache.txt"));
out.writeObject(user);
out.close();

//反序列化
ObjectInputStream in = new ObjectInputStream(new FileInputStream("cache.txt"));
User newUser = (User) in.readObject();
in.close();

序列化就是:将对象转化为字节序列的过程

反序列化就是:将字节序列恢复为java对象

在User类中有一个变量serialVersionUID是用来辅助序列化的过程。原则上序列化后的数据中的serialVersionUID只有和当前类的serialVersionUID相同才能够被反序列化。当我们不指定它的值时,可以让编译器根据当前类的结构去生成它的值,但是这样容易出现错误。比如,当前对象序列化后,更改了类中的变量,再反序列化的时候就会出现错误。因为更改类后会重新生成一个新值。所以一般serialVersionUID的值手动设置,类改变了反序列化也不会出错。

注意:

Parcelable接口

实现这个接口,一个类的对象就可以实现序列化并可以通过Intent和Binder传递。

public class User implements Parcelable {
        private String name;
        private int age;
        private Book book;

        public User(String name,int age){
            this.name = name;
            this.age = age;
        }

        protected User(Parcel in) {
            name = in.readString();
            age = in.readInt();
            book = in.readParcelable(Thread.currentThread().getContextClassLoader());
        }

        public static final Creator<User> CREATOR = new Creator<User>() {
            @Override
            public User createFromParcel(Parcel in) {
                return new User(in);
            }

            @Override
            public User[] newArray(int size) {
                return new User[size];
            }
        };

        @Override
        public int describeContents() {
            return 0;
        }
        
        /**
        *Parcel内部包装了可序列化的数据
        *将当前对象写入序列化结构中
        *flags == 1:表示当前对象需要作为返回值返回
        * flags == 0:几乎所有情况都为0
        */
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(name);
            dest.writeInt(age);
            dest.writeParcelable(book,0);
        }
}

序列化过程实现的工能:

writeToParcel(Parcel out,int flags)

由CREATOR完成,内部创建了序列化对象和数组。

describeContents

返回当前对象的内容描述符(FileDescriptor)。若有返回1,若无返回0。一般都返回0。

Serializable和Parcelable比较

Serializable Parcelable
使用简单但开销大 使用复杂但效率高
需要大量I/O操作 用在内存序列化上(存储设备、网络传输)
上一篇下一篇

猜你喜欢

热点阅读