Android 上的多进程情景
Android 中每个应用的进程都 fork 自 Zygote 进程, Zygote 进程在启动时自己会创建一个虚拟机,从而也就让每个应用拥有了自己的虚拟机。
当应用涉及多进程时,想当于启动了多个虚拟机,在单进程情况下的一些情景将会失效:
- 静态变量: 由于静态变量是位于虚拟机内存的方法区,每个虚拟机彼此独立,多个进程访问的也就不会是同一个变量
- 单利模式:单利也是基于一个虚拟机的,多个虚拟机将失效
- 线程同步机制:同步锁基于同一进程
- SharedPerfrence 不再可靠: SP 内部是以读写 xml 文件的方式,且只有线程锁。多进程环境下,文件同时读写将不可靠。
- Application 类的
onCreate
会在每个进程启动时被调用: 在含有多进程的应用里,需要在 Application 类的onCreate
里区分当前的进程,避免多个进程都执行了重复的代码。
如何开启多进程
在 AndroidManifest
中,给四大组件设置单独的 android:process
属性。
这种方式,有两种情况:
- 当前应用私有的进程,声明
process
属性带:
号,其他应用的组件不能运行在该进程中。
<activity android:name=".AbcActivity" android:process=":remote"/>
- 不带
:
号的全局进程。其他应用可以通过 SharedUID 方式跑在该进程中。
<activity android:name=".AbcActivity" android:process="remote"/>
序列化和反序列化
Java Serializable 接口
让对象支持序列化,只需实现 Serializable
接口,并声明一个serialVersionUID
。serialVersionUID
不是必需的,但是如果不声明会对反序列化过程产生影响。序列化后的数据中的serialVersionUID
只有和当前类的serialVersionUID
相同时,才能被正常地反序列化。
- 静态属性属于类,不会被序列化
-
transitent
声明的属性不会被序列化
Android Parcelable 接口
Android 提供的序列化接口,相比 Serializable
性能更好,因为它主要用于在内存上序列化和反序列化。实现方式就是类实现 Parcelable
接口,并实现 createFromParcel
和 writeToParcel
等方法。
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<User> CREATOR = new Creator<User>() {
@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 类
- 创建 User.aidl
// User.aidl
package com.jy.app2;
parcelable User;
- 创建 IUserManagerInterface.aidl
// IUserManagerInterface.aidl
package 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
类一致。
- 编译生成的
IUserManagerInterface
Java 接口
package com.jy.app2;
// 所有可以在 Binder 中传输的接口,都需要继承 IInterface
public 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
方法处断点看下
不跨进程分析
binder-localService 不单独声明
process
属性
可以看到,调用直接返回了 queryLocalInterface
返回的IInterface
对象,该对象其实就是在上面 Service 的onBind
方法中创建的 IUserManagerInterfaceImpl
匿名内部类。客户端调用的直接是那个onBind
返回的对象的方法。
跨进程分析
binder-remoteService 单独声明
process
属性
这时候就返回了代理对象,然后接着就是调用getUser
方法。
走到了 Proxy 的 getUser
,这是还没有发生跨进程的调用,下一行remote.transact
就会发起跨进程请求,将我们请求的方法编号、输入数据、输出数据作为参数传入,接下来的调用就会走到另一个进程里,同时客户端这边线程会被挂起等待。Debug 也需要 attach 到另一个进程上。onTransact
将执行在服务端进程上:
onTransact
里根据方法编号调用对应的方法,这里的this
是在 Service 的 onBind
中返回的对象。在这里会将结果写到 replay
中,然后结束,程序执行切换会客户端进程。
Proxy 继续执行,从 reply
中取出结果,最后返回。
总结
- Proxy 中的逻辑是运行在客户端进程的,且默认在主线程,需要注意阻塞问题,所以
bindService
成功之后,可以通过单开线程来做 IPC 调用 -
onTransact
运行在服务端进程中,且运行在 Binder 线程池中,Binder 中的逻辑无论耗时都应该使用同步实现
参考 《Android 艺术探索读书笔记》
网友评论