一、IPC與多線程
1、IPC簡介
線程:① CPU最小的調(diào)度單元 ② 一種有限的系統(tǒng)資源
進(jìn)程:一個執(zhí)行單元凿跳。一般指一個程序或一個應(yīng)用坑傅。一個進(jìn)程可以包含多個線程僵驰。
IPC:進(jìn)程間通信。
多線程的情況
1)因為某些原因自身需要采用多線程模式來實現(xiàn)唁毒。比如:某些模塊需要運(yùn)行在單獨(dú)進(jìn)程蒜茴;為了加大一個應(yīng)用可以使用的內(nèi)存。
2)需要從其他應(yīng)用獲取數(shù)據(jù)浆西。
2粉私、Android中的多進(jìn)程模式
2.1、開啟多進(jìn)程模式
在Android中一個應(yīng)用開啟多進(jìn)程唯一辦法:給四大組件在AndroidMenifest.xml中指定android:process
屬性近零。
<service
android:name=".MyService"
android:process=":remote" />
<service
android:name=".SecondService"
android:process="com.example.xiang.myapplication.remote" />
默認(rèn)進(jìn)程名是包名诺核。“:remote”是一種省略寫法久信,完整名為“com.example.xiang.myapplication:remote”進(jìn)程名窖杀,以“:”開頭,屬于當(dāng)前應(yīng)用的私有進(jìn)程裙士,其他應(yīng)用的組件不可以和它跑在同一個進(jìn)程中入客。
2.2、多進(jìn)程模式的運(yùn)行機(jī)制
Android系統(tǒng)為每一個進(jìn)程分配了一個獨(dú)立的虛擬機(jī)腿椎,不同的虛擬機(jī)在內(nèi)存分配上有不同的地址空間桌硫。將導(dǎo)致存在如下問題:
1)靜態(tài)成員和單例完全失效
2)線程同步機(jī)制完全失效
3)SharedPreferences的可靠性下降
4)Application會多次創(chuàng)建
不同的進(jìn)程擁有獨(dú)立的虛擬機(jī)、Application和內(nèi)存空間酥诽,導(dǎo)致通過內(nèi)存來共享數(shù)據(jù)鞍泉,都會共享失敗。
Android的IPC方式
1)Intent
2)文件共享方式
3)Binder(AIDL和Messenger)
4)ContentProvider
5)Socket
二肮帐、IPC的基礎(chǔ)概念
為什么要序列化咖驮?
1)永久性保存對象的字節(jié)序列到本地文件中
2)通過序列化在網(wǎng)絡(luò)中傳遞對象
3)通過序列化在進(jìn)程間傳遞對象
1、Serializable接口
Serializable是Java提供的一個序列化接口训枢,是一個空接口托修,為對象提供標(biāo)準(zhǔn)的序列化和反序列化操作。
private static final long serialVersionUID = 8154678445665565611L;
serialVersionUID是用來輔助序列化和序列化的過程恒界,原則上序列化后的數(shù)據(jù)中的serialVersionUID只有和當(dāng)前類的serialVersionUID相同才能夠正常地被反序列化睦刃。一般我們應(yīng)該手動指定serialVersionUID的值,比如1L(或者根據(jù)類結(jié)構(gòu)生成hash值)十酣。若不指定涩拙,反序列化時當(dāng)前類有所改變(比如增加或者刪除了成員變量)际长,那么系統(tǒng)會重新計算當(dāng)前類的hash值并賦給serialVersionUID,導(dǎo)致serialVersionUID不一致兴泥,于是反序列化失敗工育,程序就會crash。
靜態(tài)成員變量屬于類不屬于對象搓彻,不會參加序列化
用transient標(biāo)記的成員變量不會參與序列化
2如绸、Parcelable接口
public class User implements Parcelable {
private int userId;
private String userName;
private Book book;
protected User(Parcel in) {
userId = in.readInt();
userName = in.readString();
//book是一個可序列化對象,需要傳遞當(dāng)前線程的上下文類加載器
book = in.readParcelable(Thread.currentThread().getContextClassLoader());
}
/**
* 實現(xiàn)反序列化
*/
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];
}
};
/**
* 幾乎所有情況都返回0
* @return
*/
@Override
public int describeContents() {
return 0;
}
/**
* 實現(xiàn)序列化
*
* @param parcel
* @param i
*/
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(userId);
parcel.writeString(userName);
parcel.writeParcelable(book, i);
}
}
3旭贬、Binder
3.1怔接、AIDL接口的創(chuàng)建
Binder就是Android中實現(xiàn)了IBinder接口的一個類,是跨進(jìn)程通信的媒介稀轨。在Android開發(fā)中扼脐,Binder主要用在Service中,包括Messenger(底層其實是AIDL)和AIDL靶端。
//Book.java
package com.example.xiang.myapplication;
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable {
private int bookId;
private String bookName;
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
/**
* 幾乎所有情況都返回0
* @return
*/
@Override
public int describeContents() {
return 0;
}
/**
* 實現(xiàn)序列化
* @param parcel
* @param i
*/
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(bookId);
parcel.writeString(bookName);
}
/**
* 實現(xiàn)反序列化
*/
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
public Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
}
//Book.aidl
package com.example.xiang.myapplication;
parcelable Book;
// IBookManager.aidl
package com.example.xiang.myapplication;
import com.example.xiang.myapplication.Book;//必須導(dǎo)入谎势,否則報錯
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
Book.aidl是Book類在AIDL中的聲明。IBookManager.aidl是定義的一個接口杨名,雖然Book類和IBookManager在同一個包中,但是還是要顯示導(dǎo)入Book類猖毫。目錄結(jié)構(gòu)如下:
(新建Book.aidl時候台谍,直接填Book為名字時候會報錯,只有先創(chuàng)建完之后再RENAME才不會報錯)
3.2吁断、Binder類分析
系統(tǒng)為IBookManager.aidl自動生成的Binder類
package com.example.xiang.myapplication;
public interface IBookManager extends android.os.IInterface {
public static abstract class Stub extends Binder implements IBookManager {
//Binder的唯一標(biāo)識
private static final String DESCRIPTOR = "com.example.xiang.myapplication.IBookManager";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static IBookManager asInterface(IBinder obj) {
if ((obj == null)) {
return null;
}
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IBookManager))) {
return ((IBookManager) iin);
}
return new IBookManager.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, Parcel data,
Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(DESCRIPTOR);
List<Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
Book _arg0;
if ((0 != data.readInt())) {
_arg0 = Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements IBookManager {
private IBinder mRemote;
Proxy(IBinder remote) {
mRemote = remote;
}
@Override
public IBinder asBinder() {
return mRemote;
}
public String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public List<Book> getBookList() throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
List<Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
//_data寫入?yún)?shù)
//發(fā)起遠(yuǎn)程調(diào)用趁蕊,當(dāng)前線程掛起
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addBook(Book book) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = 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();
}
}
}
static final int TRANSACTION_getBookList = (IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (IBinder.FIRST_CALL_TRANSACTION + 1);
}
public List<Book> getBookList() throws RemoteException;
public void addBook(Book book) throws RemoteException;
}
-
asInterface
//將服務(wù)端的Binder對象轉(zhuǎn)換為客戶端所需的AIDL接口類型的對象,如果C/S位于同一進(jìn)程仔役,此方法返回就是服務(wù)端的Stub對象本身掷伙,否則返回的就是系統(tǒng)封裝后的Stub.proxy對象 -
asBinder
//返回當(dāng)前的Binder對象 -
onTransact
//運(yùn)行在服務(wù)端 -
Proxy#getBookList
//運(yùn)行在客戶端,內(nèi)部實現(xiàn)過程如下:首先創(chuàng)建該方法所需要的輸入型對象Parcel對象_data又兵,輸出型Parcel對象_reply和返回值對象List任柜。然后把該方法的參數(shù)信息寫入_data( 如果有參數(shù));接著調(diào)用transact方法發(fā)起RPC( 遠(yuǎn)程過程調(diào)用)沛厨,同時當(dāng)前線程掛起(因此不能再UI線程中發(fā)起遠(yuǎn)程請求)宙地;然后服務(wù)端的onTransact方法會被調(diào)用(服務(wù)端的Binder方法運(yùn)行在線程池,所以需要采用同步方式實現(xiàn))逆皮,直到RPC過程返回后宅粥,當(dāng)前線程繼續(xù)執(zhí)行,并從_reply中取出RPC過程的返回結(jié)果电谣,最后返回_reply中的數(shù)據(jù)秽梅。
AIDL的本質(zhì):系統(tǒng)提供的一個快速實現(xiàn)Binder的工具而已抹蚀。
3.3、遠(yuǎn)程服務(wù)端Service的實現(xiàn)
public class BookManagerService extends Service {
private static final String TAG = "BookManagerService";
//CopyOnWriteArrayList支持并發(fā)讀/寫
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
private Binder mBinder = new IBookManager.Stub() {
@Override
public List<Book> getBookList() throws RemoteException {
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
};
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(1, "Android開發(fā)藝術(shù)探索"));
mBookList.add(new Book(2, "Android進(jìn)階之光"));
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
//注冊Service
<service
android:name="com.example.service.MessengerService"
android:process=":remote" />
AIDL方法(getBookList和addBook)是運(yùn)行在Binder線程池中的企垦,所以需要處理線程同步环壤,這里采用CopyOnWriteArrayList來進(jìn)行自動的線程同步。
3.4竹观、客戶端的實現(xiàn)
public class BookManagerActivity extends Activity {
private static final String TAG = "BookManagerActivity";
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IBookManager bookManager = IBookManager.Stub.asInterface(service);
try {
List<Book> list = bookManager.getBookList();
Log.d(TAG, "查詢圖書列表:" + list.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(connection);
super.onDestroy();
}
}
服務(wù)端的方法可能需要很久才能執(zhí)行完畢镐捧,上面這樣寫的目的是為了更好的了解AIDL的實現(xiàn)步驟。
對象是不能跨進(jìn)程直接傳輸?shù)某粼觯瑢ο罂邕M(jìn)程傳輸?shù)谋举|(zhì)是反序列化過程懂酱,這也是為什么AIDL中定義的對象必須實現(xiàn)Parcelable接口。
4誊抛、Binder的兩個重要方法
linkToDeath(DeathRecipient recipient, int flags)
//設(shè)置Binder的死亡代理列牺。當(dāng)Binder死亡時,系統(tǒng)會回調(diào)DeathRecipient的binderDied()
方法拗窃,所以需要在此方法中移除之前綁定的binder代理(調(diào)用unlinkToDeath
)并重新綁定遠(yuǎn)程服務(wù)
unlinkToDeath(DeathRecipient recipient, int flags)
//移除死亡代理
isBinderAlive()
//判斷Binder是否死亡
DeathRecipient是IBinder的一個內(nèi)部接口瞎领,里面只有一個方法binderDied()
5、補(bǔ)充說明
AIDL文件支持的數(shù)據(jù)類型
1)基本數(shù)據(jù)類型(int随夸、long九默、char、boolean等)
2)String和CharSequence
3)List:只支持ArrayList宾毒,里面的每個元素必須被AIDL所支持
4)Map:只支持HashMap驼修,里面的每個元素必須被AIDL所支持,包括key和value
5)Parcelable:所有實現(xiàn)了Parcelable接口的對象
6)AIDL:所有AIDL接口本身也可以在AIDL文件中使用
- 自定義的Parcelable對象(如上例中的Book類)诈铛,必須新建一個和它同名的AIDL文件(Book.aidl)乙各,并添加相應(yīng)的內(nèi)容。
- 自定義Parcelable對象和AIDL對象必須要顯式import進(jìn)來幢竹。
- 除了基本數(shù)據(jù)類型的其他類型參數(shù)耳峦,都需要標(biāo)上方向:in、out或者inout焕毫。
- AIDL接口中只支持方法蹲坷,不支持聲明靜態(tài)常量。
- 建議把所有和AIDL相關(guān)的類和文件全部放在同一個包中咬荷,好處是冠句,若客戶端在另一應(yīng)用(模塊),復(fù)制整個包即可幸乒。
- AIDL的包結(jié)構(gòu)在服務(wù)端和客戶端必須保持一致懦底,否則運(yùn)行出錯。
DeathRecipient是IBinder的一個內(nèi)部接口,里面只有一個方法binderDied()
三聚唐、Android中的IPC方式
1丐重、使用Intent
啟動另一個進(jìn)程的Activity、Service和Receiver的時候杆查,在Bundle(實現(xiàn)了Parcelable接口)中附加信息扮惦,并通過Intent進(jìn)行傳遞
2、使用文件共享
序列化一個對象到文件系統(tǒng)中的同時從另一個進(jìn)程中恢復(fù)這個對象亲桦,適合對數(shù)據(jù)同步要求不高的進(jìn)程之間進(jìn)行通信崖蜜,并且要妥善處理并發(fā)讀寫問題。
SharedPreferences底層實現(xiàn)采用XML文件來存儲鍵值對客峭。系統(tǒng)對它的讀/寫有一定的緩存策略豫领,即在內(nèi)存中會有一份 SharedPreferences文件的緩存,因此在多進(jìn)程模式下舔琅,系統(tǒng)對它的讀/寫變得不可靠等恐,面對高并發(fā)讀/寫時SharedPreferences 有很大幾率丟失數(shù)據(jù),因此不建議在IPC中使用SharedPreferences备蚓。
3课蔬、使用Messenger(信使)
一種輕量級的IPC方案,底層實現(xiàn)是AIDL郊尝。服務(wù)端以串行的方式處理客戶端發(fā)來的Message對象二跋。
舉個例子
//MessengerService.java
//服務(wù)端代碼
public class MessengerService extends Service {
private static final String TAG = "MessengerService";
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case Constants.MSG_FROM_CLIENT:
//收到客戶端的消息
Log.d(TAG, "收到客戶端發(fā)來的信息:" + msg.getData().getString("msg"));
Messenger client = msg.replyTo;
Message replyMessage = Message.obtain(null, Constants.MSG_FROM_SERVER);
Bundle bundle = new Bundle();
bundle.putString("reply","你的消息我已經(jīng)收到,稍后回復(fù)你流昏。");
try {
client.send(replyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
}
private final Messenger mMessenger = new Messenger(new MessengerHandler());
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
//注冊Service
<service
android:name="com.example.service.MessengerService"
android:process=":remote" />
//MainActivity.java
//客戶端實現(xiàn)
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
private Messenger mService;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mService = new Messenger(iBinder);
Message msg = Message.obtain(null, Constants.MSG_FROM_CLIENT);
Bundle data = new Bundle();
data.putString("msg", "hello, this is client");
msg.setData(data);
//注意這一句
msg.replyTo = mGetReplyMessenger;
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());
private class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case Constants.MSG_FROM_SERVER:
Log.d(TAG, "收到服務(wù)端的回復(fù)" + msg.getData().getString("reply"));
break;
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Intent intent = new Intent(this, MessengerService.class);
findViewById(R.id.btn_bind).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
});
}
@Override
protected void onDestroy() {
unbindService(connection);
super.onDestroy();
}
}
Messenger中傳遞的數(shù)據(jù)必須將數(shù)據(jù)放入Message同欠,Message和Messenger都實現(xiàn)了Parcelable接口(放入Message中的對象也要實現(xiàn)Parcelable接口才能傳遞)。工作原理如圖所示:
[站外圖片上傳中...(image-7382db-1516020836113)]
Messenger常用方法
Messenger(Handler target)
Messenger(IBinder target)
send(Message message)
getBinder() IBinder
Message相關(guān)屬性
replyTo
//返回一個可以答復(fù)的Messenger
4横缔、使用AIDL
見上面的例子