《Android开发艺术探索》第2章读书笔记

Android IPC简介
IPC是Inter-Process Communication的缩写,含义为进程间或者跨进程通信,是指两个进程之间进行数据交换的过程。
进程和线程
线程是cpu调度的最小单元,同时线程是一种有限的系统资源。
进程一般指一个执行单元,在PC和移动设备上值一个程序或者一个应用。一个进程可以包含多个线程。
因而线程和进程是包含与被包含的关系。最简单情况一个进程中可以只有一个线程,即主线程。

Android中多进程模式
在Android中,通过给四大组件在AndroidMenifest指定android:process属性,可以开启多进程模式。
比如:

        <activity  android:name=".MainActivity" android:launchMode="standard" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity  android:name=".SecondActivity" android:process=":remote" />
        <activity  android:name=".ThirdActivity" android:process="com.ryg.chapter_2.remote" />
当前应用包名为com.ryg.chapter_2;入口MainActvity启动时,它运行在默认进程中,进程名是com.ryg.chapter_2;
当SecondActivity启动时,系统会为它创建一个进程,进程名是com.ryg.chapter_2:remote,
当ThirdActivity启动时,系统也会为它创建一个进程,进程名是com.ryg.chapter_2.remote;
这样就开启了多进程了,通过shell来查看一个包中当前所在的进程信息:adb shell ps或者adb shell ps|com.ryg.chapter_2.
进程名以":"开头的进程属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中。
进程中不以“:”开头的进程属于当前应用的全局进程,其他应用的组件通过ShareUID方式可以和它跑在同一个进程中。
多进程模式的运行机制
Android系统为每个应用分配了一个独立的虚拟机,或者说是为每个进程都分配了一个独立的虚拟机,
不同的的虚拟机在内存分配上有不同的地址空间,这就导致在不同的虚拟机中访问同一个类的对象会产生多份副本。
比如创建一个类 
    public class A{
        public static int sUserId=1;
    }
那么在这上面三个进程中都会存在一个A类,并且这个三个类是互不干扰的。在一个进程中修改sUserId的值,只会影响当前进程,
对其他进程不会造成任何影响。比如在MainActivity中是的sUserId=2,但是在SecondActivity中sUserId任然是1。它们互不影响。
所有运行在不同进程的四大组件,只要它们之间需要通过内存来共享数据,都会共享失败,这也是多进程所带来的主要影响。
多进程会造成如下几个方面的问题:
1.静态成员和单例模式完全失效;
比如类A在MainActivity所在进程有一份副本,在SecondActivity所在进程也有一份副本,完全不同的两个A类,它们之间没有关系
所以静态变量也不可能唯一,单例也谈不上,两个进程之间的不同对象。
2.线程同步机制完全失效
因为是在两个进程中,锁的对象完全不一样.
3.SharedPreferences的可靠性下降
sSharedPreferences不支持两个进程同时去执行写操作,否则会造成一定几率数据丢失。
SharedPreferences的底层是通过读写XML文件来实现,并发写显然会出问题。
4.Application会多次创建
系统在创建新的进程的同时分配独立的虚拟机,所以这个过程其实就是启动一个应用的过程。那么自然就会创建新的Application;

IPC基础概念介绍
Serializable接口
是java所提供的一个序列化方式,它是一个空接口,为对象提供标准的序列化和反序列化操作。
想让一对象实现序列化只要这个类实现Serializable接口并声明一个serialVersionUID,比如下面
public class User implements Serializable {
    private static final long serialVersionUID = -4784411584622654878L;
    private int userId;
    private String userName;

    public User(int userId, String userName) {
        this.userId = userId;
        this.userName = userName;
    }

    @Override
    public String toString() {
        return "User [userId=" + userId + "userName=" + userName + "+ ]";
    }
}

实际上这个serialVersionUID也不是必须的。我们不声明这个serialVersionUID同样也可以实现序列化。但这将会对反序列化过程产生影响。
这个serialVersionUID是用来辅助序列化和反序列化的,原则上序列化后的数据中的serialVersionUID只有和当前类的serialVersionUID相同才能够正常的被反序列化。
serialVersionUID的详细工作机制是这样的:
序列化的时候系统会把当前类的serialVersionUID写入序列化的文件中(也可能是其他的中介),
当反序列化的时候系统会去检测文件中的serialVersionUID,看它是否和当前类的serialVersionUID一致,
如果一致就说明序列化的类的版本和当前类的版本是相同的,这个时候可以反序列化。
如果不一致说明当前类和序列化的类相比发生了某些变化,比如成员变量数量、类型可能发生了变化。这时候无法正常反序列化。

public class Main {

    public static void main(String[] args) {
        User user = new User(1, "安卓");
        try {
            // 序列化
            serialObject(user);
            // 反序列化
        // reSerialObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 序列化过程
    private static void serialObject(User user) throws IOException,
            FileNotFoundException {
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(
                "cache.txt"));
        out.writeObject(user);
        out.close();
    }

    // 反序列化过程
    private static void reSerialObject() throws IOException,
            FileNotFoundException, ClassNotFoundException {
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(
                "cache.txt"));
        User mUser = (User) in.readObject();
        in.close();
        System.out.println(mUser.toString());
    }
}

上面是序列化和反序列的过程。这时如果不指定serialVersionUID,而又手动修改某个类的变量的话,在此反序列化可能就会失败。
(可以自己手动先单独序列化,在添加删除User类的变量,在注释序列化代码,然后单独执行反序列化看看)
Serializable有两点需要注意的:
1.静态成员变量属于类不属于对象,所以不会参与序列化过程
2.用transient关键字标记的成员变量不参与序列化
Parcelable接口
也是一个接口,只要实现这个接口,一个类的对象就可以实现序列化并可以通过Intent和Binder传递。
下面是用法:

public class User implements Parcelable {
    private int userId;
    private String userName;
    private boolean isMale;
    private Book book;// Book类也实现了Parcelable

    // 内容描述
    @Override
    public int describeContents() {
        return 0;
    }

    // 序列化功能
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(userId);
        dest.writeString(userName);
        dest.writeInt(isMale ? 0 : 1);
        dest.writeParcelable(book, 0);
    }

    // 反序列化功能
    public static final Parcelable.Creator<User> CREATOR = new Creator<User>() {

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

        @Override
        public User createFromParcel(Parcel source) {
            return new User(source);
        }
    };

    private User(Parcel in) {
        userId = in.readInt();
        userName = in.readString();
        isMale = in.readInt() == 0;
        // book是另一个可序列化对象,所以它的序列化过程需要传递当前线程的上下文类加载器
        book = in.readParcelable(Thread.currentThread().getContextClassLoader());
        }
}

序列化过程需实现的功能有序列化,反序列化和内容描述。
wireToParcel完成序列化功能,通过Parcel的一系列方法write方法完成
CREATOR完成反序列化功能,内部表明了如何创建序列化的对象和数组,通过Parcel的一系列read方法完成反序列化。
(注:Parcel内部包装了可序列化的数据,可以在Binder中自由传输)
decribeContents方法完成内容描述功能 。几乎所有情况都返回的是0,仅当当前对象存在文件描述时返回1.

Parcelable和Serializalbe的比较:
Serializable是java中的接口,使用简单,开销很大,需要大量IO操作。
Parcelable是Android中的接口,使用麻烦,开销小点,效率很高,主要用在内存序列化上。
做Android开发首选Paracelable.
如果是将对象序列化到存储设备中或者将对象序列化后通过网络传输,两种都是可以的,但Parcelable稍显复杂,这个时候就选择使用Serializable.

Binder
直观来说Binder是Android中的一个类,它实现了IBinder;
从IPC角度来说,Binder是Android中的一种跨进程通信方式,Binder还可以理解为一种虚拟的物理设备,它的设备驱动是/dec/binder;
从Android Framework角度来说,Binder是ServiceManager连接各种Manager(ActvityManager.WindowManager,等等)和相应ManagerService的桥梁;
从Android应用层来说,Binder是客户端和服务端进行通信的媒介;
下面使用AIDL(Android进程间通信接口定义语言)生成的Binder接口类来分析Binder的工作机制:
AIDL代码文件如下,这里省略了Book类代码了:

package com.ryg.chapter_2.aidl;
import com.ryg.chapter_2.aidl.Book;//Book类在aidl中的声明
interface IBookManager{
 List<Book> getBookList();
 void addBook(in Book book);
}

下面就是系统帮我们生成的java类,已经注释了:

package com.ryg.chapter_2.aidl;

//系统生成的这个Binder接口类,所有在Binder中传输的接口都需要继承IInterface接口
public interface IBookManager extends android.os.IInterface {
    // 这个类就是一个Binder
    public static abstract class Stub extends android.os.Binder implements com.ryg.chapter_2.aidl.IBookManager {
        // Binder的唯一标识,一般用当前Binder接口类名表示
        private static final java.lang.String DESCRIPTOR = "com.ryg.chapter_2.aidl.IBookManager";

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        // 用于将服务端的Binder对象转换成客户端端所需的AIDL接口类型的对象,这种转换是区分进程的
        // 如果客户端和服务端位于同一进程,那么此方法就返回会服务端的Stub对象本身
        // 反之不是一个进程,此方法返回系统封装后的Stub.Proxy对象
        public static com.ryg.chapter_2.aidl.IBookManager asInterface(
                android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.ryg.chapter_2.aidl.IBookManager))) {
                return ((com.ryg.chapter_2.aidl.IBookManager) iin);
            }
            return new com.ryg.chapter_2.aidl.IBookManager.Stub.Proxy(obj);
        }

        // 此方法用于返回当前的Binder对象
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        // 此方法运行在服务端的线程池中,当客户端发起跨进程请求时,远程请求通过系统底层后交由此方法处理
        // code:服务端通过code可以确定客户端所请求得目标方法是什么,
        // data:如果目标方法有参数的话,会从data中取出目标方法所需的参数
        // reply:如果目标方法有返回值的话,就向reply中写入返回值
        // 如果此方法返回false,那么客户算请求失败,可以利用这个特性来做权限验证。不希望任意进程都能调用我们服务
        @Override
        public boolean onTransact(int code, android.os.Parcel data,
                android.os.Parcel reply, int flags)
                throws android.os.RemoteException {
            switch (code) {
            case INTERFACE_TRANSACTION: {
                reply.writeString(DESCRIPTOR);
                return true;
            }
            case TRANSACTION_getBookList: {
                data.enforceInterface(DESCRIPTOR);
                java.util.List<com.ryg.chapter_2.aidl.Book> _result = this
                        .getBookList();
                reply.writeNoException();
                reply.writeTypedList(_result);
                return true;
            }
            case TRANSACTION_addBook: {
                data.enforceInterface(DESCRIPTOR);
                com.ryg.chapter_2.aidl.Book _arg0;
                if ((0 != data.readInt())) {
                    _arg0 = com.ryg.chapter_2.aidl.Book.CREATOR
                            .createFromParcel(data);
                } else {
                    _arg0 = null;
                }
                this.addBook(_arg0);
                reply.writeNoException();
                return true;
            }
            }
            return super.onTransact(code, data, reply, flags);
        }

        // Stub类中的Proxy代理类
        private static class Proxy implements com.ryg.chapter_2.aidl.IBookManager {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            // 这个方法运行在客户端,
            @Override
            public java.util.List<com.ryg.chapter_2.aidl.Book> getBookList()
                    throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.ryg.chapter_2.aidl.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data,
                            _reply, 0);
                    _reply.readException();
                    _result = _reply
                            .createTypedArrayList(com.ryg.chapter_2.aidl.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            // 这个方法运行在客户端
            @Override
            public void addBook(com.ryg.chapter_2.aidl.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);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        // 这个id表示方法getBookList
        static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        // 这个id标识方法addBook
        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    }

你可能感兴趣的:(读书笔记,Android开发,ipc,多进程)