Android 上的多进程情景

Android 中每个应用的进程都 fork 自 Zygote 进程, Zygote 进程在启动时自己会创建一个虚拟机,从而也就让每个应用拥有了自己的虚拟机。
当应用涉及多进程时,想当于启动了多个虚拟机,在单进程情况下的一些情景将会失效:

  • 静态变量: 由于静态变量是位于虚拟机内存的方法区,每个虚拟机彼此独立,多个进程访问的也就不会是同一个变量
  • 单利模式:单利也是基于一个虚拟机的,多个虚拟机将失效
  • 线程同步机制:同步锁基于同一进程
  • SharedPerfrence 不再可靠: SP 内部是以读写 xml 文件的方式,且只有线程锁。多进程环境下,文件同时读写将不可靠。
  • Application 类的 onCreate 会在每个进程启动时被调用: 在含有多进程的应用里,需要在 Application 类的 onCreate 里区分当前的进程,避免多个进程都执行了重复的代码。

如何开启多进程

AndroidManifest 中,给四大组件设置单独的 android:process 属性。
这种方式,有两种情况:

  1. 当前应用私有的进程,声明 process 属性带 : 号,其他应用的组件不能运行在该进程中。
  1. 不带 : 号的全局进程。其他应用可以通过 SharedUID 方式跑在该进程中。

序列化和反序列化

Java Serializable 接口

让对象支持序列化,只需实现 Serializable接口,并声明一个serialVersionUIDserialVersionUID不是必需的,但是如果不声明会对反序列化过程产生影响。序列化后的数据中的serialVersionUID只有和当前类的serialVersionUID相同时,才能被正常地反序列化。

  • 静态属性属于类,不会被序列化
  • transitent 声明的属性不会被序列化

Android Parcelable 接口

Android 提供的序列化接口,相比 Serializable 性能更好,因为它主要用于在内存上序列化和反序列化。实现方式就是类实现 Parcelable 接口,并实现 createFromParcelwriteToParcel 等方法。

Binder

  • 从 IPC 角度,Binder 是 Android 的一种跨进程通信方式
  • 从 Android Framework 角度,Binder 是 ServiceManager 连接各种 Manager 和相应 ManagerService 的桥梁
  • 从 Android 应用层,Binder 是客户端和服务端进行通信的媒介,当 bindService 时,服务端会返回一个包含了服务端业务调用的 Binder 对象,通过这个 Binder 对象,客户端就可以像调用客户端本地方法一样调用服务端的方法。
  • 普通 Service 中的 Binder 不涉及进程间通信
  • 多进程的应用会较多涉及 Binder,Binder 也主要用在 Service 上

Android 提供了 AIDL 描述语言来方便开发者创建 Binder 类,也可以自己手写实现 Binder 类。

模拟一个数据类 User,并实现 Parcelable 接口,使用跨进程的方式,从远程 Service 中获取 User

public class User implements Parcelable {    String name;    int age;    protected User(Parcel in) {        name = in.readString();        age = in.readInt();    }    public static final Creator CREATOR = new Creator() {        @Override        public User createFromParcel(Parcel in) {            // 从 Parcel 中构造 User 对象            return new User(in);        }        @Override        public User[] newArray(int size) {            return new User[size];        }    };    @Override    public int describeContents() {        return 0;    }    @Override    public void writeToParcel(Parcel dest, int flags) {        // 将属性写入 Parcel 对象中        dest.writeString(name);        dest.writeInt(age);    }}

使用 AIDL,自动生成 Binder 类

  1. 创建 User.aidl
// User.aidlpackage com.jy.app2;parcelable User;
  1. 创建 IUserManagerInterface.aidl
// IUserManagerInterface.aidlpackage com.jy.app2;import com.jy.app2.User;interface IUserManagerInterface {User getUser();void setUser(in User user);}

需要注意,User.aidl 的文件名和内部声明的 pracelable 都要和 Java 类 User一致,且 User.aidl 的包路径也要和 Java User类一致。

  1. 编译生成的 IUserManagerInterface Java 接口
package com.jy.app2;// 所有可以在 Binder 中传输的接口,都需要继承 IInterfacepublic interface IUserManagerInterface extends android.os.IInterface {    // 一个 Binder 类,当客户端和服务端在同一个进程时不会走 onTransact 过程    // 当客户端和服务端不在同一个进程时,会走 onTransact 过程,并且逻辑有内部类 Proxy 完成    public static abstract class Stub extends android.os.Binder implements com.jy.app2.IUserManagerInterface {        // Binder 的唯一标识        private static final java.lang.String DESCRIPTOR = "com.jy.app2.IUserManagerInterface";        public Stub() {            this.attachInterface(this, DESCRIPTOR);        }        /*         * 用于将服务端的 Binder 对象,转换成客户端所需的 IInterface 接口对象(使用 AIDL 生成的)。         * 这个过程是区分进程的:如果客户端和服务端在同一个进程,此方法返回服务端的 Stub 对象本身;否则         * 就返回 Stub 的内部类 Proxy 对象        */        public static com.jy.app2.IUserManagerInterface asInterface(android.os.IBinder obj) {            if ((obj == null)) {                return null;            }            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);            if (((iin != null) && (iin instanceof com.jy.app2.IUserManagerInterface))) {                return ((com.jy.app2.IUserManagerInterface) iin);            }            return new com.jy.app2.IUserManagerInterface.Stub.Proxy(obj);        }        // 此方法返回当前的 Binder 对象        @Override        public android.os.IBinder asBinder() {            return this;        }        // 此方法运行在服务端的 Binder 线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装之后,交给该方法执行        // 该方法中会服务端根据 code 参数确定应该执行的目标方法,接着从 data 中取出目标方法需要的参数(如果目标参数需要传入参数),目标方法执行完成后,将结果写入 reply 中(如果目标方法有返回值)。        // 如果该方法返回 false,代表客户端请求失败。所以可以在这里面加自己的业务,比如权限验证,当不通过时直接返回 false        @Override        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {            java.lang.String descriptor = DESCRIPTOR;            switch (code) {                case INTERFACE_TRANSACTION: {                    reply.writeString(descriptor);                    return true;                }                case TRANSACTION_getUser: {                    data.enforceInterface(descriptor);                    com.jy.app2.User _result = this.getUser();                    reply.writeNoException();                    if ((_result != null)) {                        reply.writeInt(1);                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);                    } else {                        reply.writeInt(0);                    }                    return true;                }                case TRANSACTION_setUser: {                    data.enforceInterface(descriptor);                    com.jy.app2.User _arg0;                    if ((0 != data.readInt())) {                        _arg0 = com.jy.app2.User.CREATOR.createFromParcel(data);                    } else {                        _arg0 = null;                    }                    this.setUser(_arg0);                    reply.writeNoException();                    return true;                }                default: {                    return super.onTransact(code, data, reply, flags);                }            }        }        private static class Proxy implements com.jy.app2.IUserManagerInterface {            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;            }            // 此方法运行在客户端,当客户端远程调用此方法时,先创建输入和输出 Parcel _data 和 _reply            // 然后调用 transact 发起 RPC 远程调用,同时线程挂起;然后服务端的 onTransact 被调用,直到            // RPC 结果返回,客户端线程继续运行,并从 _reply 中取出 RPC 的返回结果,最后返回结果            @Override            public com.jy.app2.User getUser() throws android.os.RemoteException {                android.os.Parcel _data = android.os.Parcel.obtain();                android.os.Parcel _reply = android.os.Parcel.obtain();                com.jy.app2.User _result;                try {                    _data.writeInterfaceToken(DESCRIPTOR);                    mRemote.transact(Stub.TRANSACTION_getUser, _data, _reply, 0);                    _reply.readException();                    if ((0 != _reply.readInt())) {                        _result = com.jy.app2.User.CREATOR.createFromParcel(_reply);                    } else {                        _result = null;                    }                } finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }            // 此方法同上面,只是多了将参数写入到 _data ,由于该方法没有返回值,所以不会从 _reply 中取结果            @Override            public void setUser(com.jy.app2.User user) 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 ((user != null)) {                        _data.writeInt(1);                        user.writeToParcel(_data, 0);                    } else {                        _data.writeInt(0);                    }                    mRemote.transact(Stub.TRANSACTION_setUser, _data, _reply, 0);                    _reply.readException();                } finally {                    _reply.recycle();                    _data.recycle();                }            }        }        //两个整形,用于标识客户端请求的方法        static final int TRANSACTION_getUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);        static final int TRANSACTION_setUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);    }    // 服务端 Binder 需要实现的方法    public com.jy.app2.User getUser() throws android.os.RemoteException;    public void setUser(com.jy.app2.User user) throws android.os.RemoteException;}

手写实现 Binder 类

先定义一个继承了IInterface的接口

public interface IUserManagerInterface extends IInterface {    public String DESCRIPTION = "com.jy.app2.IUserManagerInterface";    public void setUser(String token, User user) throws RemoteException;    public User getUser(String token) throws RemoteException;    public int Method_setUser = IBinder.FIRST_CALL_TRANSACTION;    public int Method_getUser = IBinder.FIRST_CALL_TRANSACTION + 1;}

实现接口,并继承Binder

package com.jy.app2;import android.os.*;import android.support.annotation.NonNull;import android.support.annotation.Nullable;import android.text.TextUtils;abstract class IUserManagerInterfaceImpl extends Binder implements IUserManagerInterface {    IUserManagerInterfaceImpl() {        attachInterface(this, DESCRIPTION);    }        @Override    public IBinder asBinder() {        return this;    }    // 当不是跨进程时,直接返回服务端本身的 Binder    // 当是跨进程时,返回代理对象    public static IUserManagerInterface asInterface(IBinder object) {        if (object == null) {            return null;        }        IInterface iin = object.queryLocalInterface(DESCRIPTION);        if ((iin != null) && (iin instanceof IUserManagerInterface)) {            return (IUserManagerInterface) iin;        }        return new Proxy(object);    }    @Override    protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {        switch (code) {            case Method_getUser:                if (!auth(data)) {                    return false;                }                User user = this.getUser(data.readString());                reply.writeNoException();                if (user != null) {                    reply.writeInt(1);                    user.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);                } else {                    reply.writeInt(0);                }                return true;            case Method_setUser:                if (!auth(data)) {                    return false;                }                String token = data.readString();                User arg1 = null;                if ((0 != data.readInt())) {                    arg1 = User.CREATOR.createFromParcel(data);                }                this.setUser(token, arg1);                reply.writeNoException();                return true;        }        return super.onTransact(code, data, reply, flags);    }    private boolean auth(Parcel data) {        data.enforceInterface(DESCRIPTION);        // 模拟权限验证        String token = data.readString();        return !TextUtils.equals(token, "123");    }    static class Proxy implements IUserManagerInterface {        IBinder remote;        Proxy(IBinder remote) {            this.remote = remote;        }        @Override        public User getUser(String token) throws RemoteException {            Parcel data = Parcel.obtain();            Parcel reply = Parcel.obtain();            User result = null;            try {                data.writeInterfaceToken(DESCRIPTION);                data.writeString(token);                boolean success = remote.transact(IUserManagerInterface.Method_getUser, data, reply, 0);                reply.readException();                if ((0 != reply.readInt())) {                    result = User.CREATOR.createFromParcel(reply);                }            } finally {                data.recycle();                reply.recycle();            }            return result;        }        @Override        public void setUser(String token, User user) throws RemoteException {            Parcel data = Parcel.obtain();            Parcel reply = Parcel.obtain();            try {                data.writeInterfaceToken(DESCRIPTION);                data.writeString(token);                if (user != null) {                    data.writeInt(1);                    user.writeToParcel(data, 0);                } else {                    data.writeInt(0);                }                boolean success = remote.transact(IUserManagerInterface.Method_getUser, data, reply, 0);                reply.readException();            } finally {                data.recycle();                reply.recycle();            }        }        @Override        public IBinder asBinder() {            return remote;        }    }}

分析 Binder 的调用过程

创建一个 Service

public class UserService extends Service {    User mUser;    public UserService() {        mUser = new User();        mUser.name = "Stefan";        mUser.age = 13;    }    @Nullable    @Override    public IBinder onBind(Intent intent) {        return new IUserManagerInterfaceImpl() {            @Override            public void setUser(String token, User user) throws RemoteException {                mUser = user;            }            @Override            public User getUser(String token) throws RemoteException {                return mUser;            }        };    }}

然后bindService

IUserManagerInterface userManagerInterface;ServiceConnection connection = new ServiceConnection() {    @Override    public void onServiceConnected(ComponentName name, IBinder service) {        userManagerInterface = IUserManagerInterfaceImpl.asInterface(service);        onServiceConnect();    }    @Override    public void onServiceDisconnected(ComponentName name) {}    };    bindService(new Intent(this, UserService.class), connection, BIND_AUTO_CREATE);}private void onServiceConnect() {    try {        User user = userManagerInterface.getUser("token_1234");    } catch (RemoteException e) {        e.printStackTrace();    }}

bindService成功之后,会首先调用asInterface方法获得Binder对象,所以在 asInterface方法处断点看下

不跨进程分析

Service 不单独声明 process属性

binder-local

可以看到,调用直接返回了 queryLocalInterface返回的IInterface对象,该对象其实就是在上面 Service 的onBind方法中创建的 IUserManagerInterfaceImpl匿名内部类。客户端调用的直接是那个onBind返回的对象的方法。

跨进程分析

Service 单独声明 process属性

binder-remote

这时候就返回了代理对象,然后接着就是调用getUser方法。

binder-proxy

走到了 Proxy 的 getUser,这是还没有发生跨进程的调用,下一行remote.transact就会发起跨进程请求,将我们请求的方法编号、输入数据、输出数据作为参数传入,接下来的调用就会走到另一个进程里,同时客户端这边线程会被挂起等待。Debug 也需要 attach 到另一个进程上。onTransact将执行在服务端进程上:

binder-onTransact

onTransact里根据方法编号调用对应的方法,这里的this是在 Service 的 onBind中返回的对象。在这里会将结果写到 replay中,然后结束,程序执行切换会客户端进程。

proxy-getUser

Proxy 继续执行,从 reply中取出结果,最后返回。

总结

  • Proxy 中的逻辑是运行在客户端进程的,且默认在主线程,需要注意阻塞问题,所以bindService成功之后,可以通过单开线程来做 IPC 调用
  • onTransact 运行在服务端进程中,且运行在 Binder 线程池中,Binder 中的逻辑无论耗时都应该使用同步实现

参考 《Android 艺术探索读书笔记》

更多相关文章

  1. BitmapFactory类的decodeStream方法在网络超时或较慢的时候无法
  2. Android(安卓)过度渲染及优化方法--3D效果(JakeWharton大神的scal
  3. android 4.0以上设置wifi静态IP以及DNS的方法
  4. android锁屏原理(一)
  5. Android免Root权限Hook系统函数修改程序运行时内存指令逻辑
  6. Android(安卓)一文学会无障碍服务(AccessibilityService)
  7. Android进程管理详解
  8. Android源代码下载过程中无法下载repo的解决方法
  9. Android(安卓)四大组件之 Service

随机推荐

  1. @android:style/Theme.Dialog 和 @style/
  2. 三、mono for android 学习:参考书籍
  3. LocationManager Android自动定位使用以
  4. Hardware Acceleration | Android Develo
  5. 【Android】获得已安装应用
  6. android网站汇总
  7. android service 精辟解说(摘)
  8. Android 横屏时禁止输入法全屏
  9. 利用Android的Log 演示一个activity的生
  10. android 读写文件数据