Android 上的多進(jìn)程情景
Android 中每個(gè)應(yīng)用的進(jìn)程都 fork 自 Zygote 進(jìn)程蚂维, Zygote 進(jìn)程在啟動(dòng)時(shí)自己會(huì)創(chuàng)建一個(gè)虛擬機(jī),從而也就讓每個(gè)應(yīng)用擁有了自己的虛擬機(jī)地消。
當(dāng)應(yīng)用涉及多進(jìn)程時(shí)速梗,想當(dāng)于啟動(dòng)了多個(gè)虛擬機(jī)良蒸,在單進(jìn)程情況下的一些情景將會(huì)失效:
- 靜態(tài)變量: 由于靜態(tài)變量是位于虛擬機(jī)內(nèi)存的方法區(qū)舟扎,每個(gè)虛擬機(jī)彼此獨(dú)立遥巴,多個(gè)進(jìn)程訪問(wèn)的也就不會(huì)是同一個(gè)變量
- 單利模式:?jiǎn)卫彩腔谝粋€(gè)虛擬機(jī)的,多個(gè)虛擬機(jī)將失效
- 線程同步機(jī)制:同步鎖基于同一進(jìn)程
- SharedPerfrence 不再可靠: SP 內(nèi)部是以讀寫 xml 文件的方式享幽,且只有線程鎖铲掐。多進(jìn)程環(huán)境下,文件同時(shí)讀寫將不可靠值桩。
- Application 類的
onCreate
會(huì)在每個(gè)進(jìn)程啟動(dòng)時(shí)被調(diào)用: 在含有多進(jìn)程的應(yīng)用里摆霉,需要在 Application 類的onCreate
里區(qū)分當(dāng)前的進(jìn)程,避免多個(gè)進(jìn)程都執(zhí)行了重復(fù)的代碼奔坟。
如何開啟多進(jìn)程
在 AndroidManifest
中携栋,給四大組件設(shè)置單獨(dú)的 android:process
屬性。
這種方式咳秉,有兩種情況:
- 當(dāng)前應(yīng)用私有的進(jìn)程婉支,聲明
process
屬性帶:
號(hào),其他應(yīng)用的組件不能運(yùn)行在該進(jìn)程中澜建。
<activity android:name=".AbcActivity" android:process=":remote"/>
- 不帶
:
號(hào)的全局進(jìn)程磅摹。其他應(yīng)用可以通過(guò) SharedUID 方式跑在該進(jìn)程中。
<activity android:name=".AbcActivity" android:process="remote"/>
序列化和反序列化
Java Serializable 接口
讓對(duì)象支持序列化霎奢,只需實(shí)現(xiàn) Serializable
接口,并聲明一個(gè)serialVersionUID
饼灿。serialVersionUID
不是必需的幕侠,但是如果不聲明會(huì)對(duì)反序列化過(guò)程產(chǎn)生影響。序列化后的數(shù)據(jù)中的serialVersionUID
只有和當(dāng)前類的serialVersionUID
相同時(shí)碍彭,才能被正常地反序列化晤硕。
- 靜態(tài)屬性屬于類,不會(huì)被序列化
-
transitent
聲明的屬性不會(huì)被序列化
Android Parcelable 接口
Android 提供的序列化接口庇忌,相比 Serializable
性能更好舞箍,因?yàn)樗饕糜谠趦?nèi)存上序列化和反序列化。實(shí)現(xiàn)方式就是類實(shí)現(xiàn) Parcelable
接口皆疹,并實(shí)現(xiàn) createFromParcel
和 writeToParcel
等方法疏橄。
Binder
- 從 IPC 角度,Binder 是 Android 的一種跨進(jìn)程通信方式
- 從 Android Framework 角度略就,Binder 是 ServiceManager 連接各種 Manager 和相應(yīng) ManagerService 的橋梁
- 從 Android 應(yīng)用層捎迫,Binder 是客戶端和服務(wù)端進(jìn)行通信的媒介,當(dāng)
bindService
時(shí)表牢,服務(wù)端會(huì)返回一個(gè)包含了服務(wù)端業(yè)務(wù)調(diào)用的 Binder 對(duì)象窄绒,通過(guò)這個(gè) Binder 對(duì)象,客戶端就可以像調(diào)用客戶端本地方法一樣調(diào)用服務(wù)端的方法崔兴。 - 普通 Service 中的 Binder 不涉及進(jìn)程間通信
- 多進(jìn)程的應(yīng)用會(huì)較多涉及 Binder彰导,Binder 也主要用在 Service 上
Android 提供了 AIDL
描述語(yǔ)言來(lái)方便開發(fā)者創(chuàng)建 Binder 類蛔翅,也可以自己手寫實(shí)現(xiàn) Binder 類。
模擬一個(gè)數(shù)據(jù)類 User
位谋,并實(shí)現(xiàn) Parcelable
接口山析,使用跨進(jìn)程的方式,從遠(yuǎn)程 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 中構(gòu)造 User 對(duì)象
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 對(duì)象中
dest.writeString(name);
dest.writeInt(age);
}
}
使用 AIDL盖腿,自動(dòng)生成 Binder 類
- 創(chuàng)建 User.aidl
// User.aidl
package com.jy.app2;
parcelable User;
- 創(chuàng)建 IUserManagerInterface.aidl
// IUserManagerInterface.aidl
package com.jy.app2;
import com.jy.app2.User;
interface IUserManagerInterface {
User getUser();
void setUser(in User user);
}
需要注意,User.aidl
的文件名和內(nèi)部聲明的 pracelable
都要和 Java 類 User
一致损同,且 User.aidl
的包路徑也要和 Java User
類一致翩腐。
- 編譯生成的
IUserManagerInterface
Java 接口
package com.jy.app2;
// 所有可以在 Binder 中傳輸?shù)慕涌冢夹枰^承 IInterface
public interface IUserManagerInterface extends android.os.IInterface {
// 一個(gè) Binder 類膏燃,當(dāng)客戶端和服務(wù)端在同一個(gè)進(jìn)程時(shí)不會(huì)走 onTransact 過(guò)程
// 當(dāng)客戶端和服務(wù)端不在同一個(gè)進(jìn)程時(shí)茂卦,會(huì)走 onTransact 過(guò)程,并且邏輯有內(nèi)部類 Proxy 完成
public static abstract class Stub extends android.os.Binder implements com.jy.app2.IUserManagerInterface {
// Binder 的唯一標(biāo)識(shí)
private static final java.lang.String DESCRIPTOR = "com.jy.app2.IUserManagerInterface";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/*
* 用于將服務(wù)端的 Binder 對(duì)象组哩,轉(zhuǎn)換成客戶端所需的 IInterface 接口對(duì)象(使用 AIDL 生成的)等龙。
* 這個(gè)過(guò)程是區(qū)分進(jìn)程的:如果客戶端和服務(wù)端在同一個(gè)進(jìn)程,此方法返回服務(wù)端的 Stub 對(duì)象本身伶贰;否則
* 就返回 Stub 的內(nèi)部類 Proxy 對(duì)象
*/
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);
}
// 此方法返回當(dāng)前的 Binder 對(duì)象
@Override
public android.os.IBinder asBinder() {
return this;
}
// 此方法運(yùn)行在服務(wù)端的 Binder 線程池中蛛砰,當(dāng)客戶端發(fā)起跨進(jìn)程請(qǐng)求時(shí),遠(yuǎn)程請(qǐng)求會(huì)通過(guò)系統(tǒng)底層封裝之后黍衙,交給該方法執(zhí)行
// 該方法中會(huì)服務(wù)端根據(jù) code 參數(shù)確定應(yīng)該執(zhí)行的目標(biāo)方法泥畅,接著從 data 中取出目標(biāo)方法需要的參數(shù)(如果目標(biāo)參數(shù)需要傳入?yún)?shù)),目標(biāo)方法執(zhí)行完成后琅翻,將結(jié)果寫入 reply 中(如果目標(biāo)方法有返回值)位仁。
// 如果該方法返回 false,代表客戶端請(qǐng)求失敗方椎。所以可以在這里面加自己的業(yè)務(wù)聂抢,比如權(quán)限驗(yàn)證,當(dāng)不通過(guò)時(shí)直接返回 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;
}
// 此方法運(yùn)行在客戶端棠众,當(dāng)客戶端遠(yuǎn)程調(diào)用此方法時(shí)琳疏,先創(chuàng)建輸入和輸出 Parcel _data 和 _reply
// 然后調(diào)用 transact 發(fā)起 RPC 遠(yuǎn)程調(diào)用,同時(shí)線程掛起闸拿;然后服務(wù)端的 onTransact 被調(diào)用轿亮,直到
// RPC 結(jié)果返回,客戶端線程繼續(xù)運(yùn)行胸墙,并從 _reply 中取出 RPC 的返回結(jié)果我注,最后返回結(jié)果
@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;
}
// 此方法同上面,只是多了將參數(shù)寫入到 _data 迟隅,由于該方法沒(méi)有返回值但骨,所以不會(huì)從 _reply 中取結(jié)果
@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();
}
}
}
//兩個(gè)整形励七,用于標(biāo)識(shí)客戶端請(qǐng)求的方法
static final int TRANSACTION_getUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_setUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
// 服務(wù)端 Binder 需要實(shí)現(xiàn)的方法
public com.jy.app2.User getUser() throws android.os.RemoteException;
public void setUser(com.jy.app2.User user) throws android.os.RemoteException;
}
手寫實(shí)現(xiàn) Binder 類
先定義一個(gè)繼承了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;
}
實(shí)現(xiàn)接口,并繼承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;
}
// 當(dāng)不是跨進(jìn)程時(shí)奔缠,直接返回服務(wù)端本身的 Binder
// 當(dāng)是跨進(jìn)程時(shí)掠抬,返回代理對(duì)象
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);
// 模擬權(quán)限驗(yàn)證
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 的調(diào)用過(guò)程
創(chuàng)建一個(gè) 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();
}
}
當(dāng) bindService
成功之后,會(huì)首先調(diào)用asInterface
方法獲得Binder
對(duì)象校哎,所以在 asInterface
方法處斷點(diǎn)看下
不跨進(jìn)程分析
Service 不單獨(dú)聲明
process
屬性
可以看到两波,調(diào)用直接返回了 queryLocalInterface
返回的IInterface
對(duì)象,該對(duì)象其實(shí)就是在上面 Service 的onBind
方法中創(chuàng)建的 IUserManagerInterfaceImpl
匿名內(nèi)部類闷哆⊙埽客戶端調(diào)用的直接是那個(gè)onBind
返回的對(duì)象的方法。
跨進(jìn)程分析
Service 單獨(dú)聲明
process
屬性
這時(shí)候就返回了代理對(duì)象抱怔,然后接著就是調(diào)用getUser
方法劣坊。
走到了 Proxy 的 getUser
,這是還沒(méi)有發(fā)生跨進(jìn)程的調(diào)用屈留,下一行remote.transact
就會(huì)發(fā)起跨進(jìn)程請(qǐng)求局冰,將我們請(qǐng)求的方法編號(hào)、輸入數(shù)據(jù)灌危、輸出數(shù)據(jù)作為參數(shù)傳入康二,接下來(lái)的調(diào)用就會(huì)走到另一個(gè)進(jìn)程里,同時(shí)客戶端這邊線程會(huì)被掛起等待勇蝙。Debug 也需要 attach 到另一個(gè)進(jìn)程上沫勿。onTransact
將執(zhí)行在服務(wù)端進(jìn)程上:
onTransact
里根據(jù)方法編號(hào)調(diào)用對(duì)應(yīng)的方法,這里的this
是在 Service 的 onBind
中返回的對(duì)象浅蚪。在這里會(huì)將結(jié)果寫到 replay
中,然后結(jié)束烫罩,程序執(zhí)行切換會(huì)客戶端進(jìn)程惜傲。
Proxy 繼續(xù)執(zhí)行,從 reply
中取出結(jié)果贝攒,最后返回盗誊。
總結(jié)
- Proxy 中的邏輯是運(yùn)行在客戶端進(jìn)程的,且默認(rèn)在主線程隘弊,需要注意阻塞問(wèn)題哈踱,所以
bindService
成功之后,可以通過(guò)單開線程來(lái)做 IPC 調(diào)用 -
onTransact
運(yùn)行在服務(wù)端進(jìn)程中梨熙,且運(yùn)行在 Binder 線程池中开镣,Binder 中的邏輯無(wú)論耗時(shí)都應(yīng)該使用同步實(shí)現(xiàn)
參考 《Android 藝術(shù)探索讀書筆記》