IPC機(jī)制簡介
IPC是Inter-Process Communication的縮寫,含義就是跨進(jìn)程通信。
首先我們要理解什么是進(jìn)程,什么是線程。按操作系統(tǒng)的描述,進(jìn)程是資源分配的最小單位,而線程是CPU調(diào)度的最小單位,一個進(jìn)程可以包含多個線程(主線程、子線程)。多線程需要考慮并發(fā)問題。
Android中的主線程是也叫UI線程,在主線程執(zhí)行耗時操作會ANR
多進(jìn)程的兩種情況
1 某個應(yīng)用由于自身原因需要采用多進(jìn)程模式來實現(xiàn)(如:某些模塊由于特殊原因需要運行在獨立進(jìn)程)
2 為了加大一個應(yīng)用可使用的內(nèi)存通過多進(jìn)程來獲取多份內(nèi)存空間
Android中的多進(jìn)程模式
在正式講解進(jìn)程間通信前我們先了解Android中的多進(jìn)程模式。
通過給四大組件指定android:process屬性可以輕易開啟多進(jìn)程(看起來簡單,但是有許多需要注意的問題)
一般情況下Android中多進(jìn)程是指一個應(yīng)用中存在多個進(jìn)程的情況。首先在Android使用多進(jìn)程只有一中方式就是為四大組件指定android:process屬性(特例:使用JNI在native底層fork一個新進(jìn)程)
1 android:process=”:test” (私有進(jìn)程其他應(yīng)用組件不可在該進(jìn)程)
2 android:process=”com.test.l” (全局進(jìn)程其他組件可用ShareUID方式跑在相同進(jìn)程中)
UID:在Linux中的代表用戶ID,android系統(tǒng)為每個應(yīng)用分配的標(biāo)識,也就是說android中的每個應(yīng)用其實就相當(dāng)于一個用戶。兩個應(yīng)用如果想通過shareUID的方式跑在同一個進(jìn)程中必須保證UID相同,并且簽名一致。那么同進(jìn)程中就可以相互訪問對方的私有數(shù)據(jù)(如data,res資源,組件信息,內(nèi)存數(shù)據(jù)等等)
ShareUID擴(kuò)展
通過shareduserid來獲取系統(tǒng)權(quán)限
(1)在AndroidManifest.xml中添加android:sharedUserId=”android.uid.system”
(2)在Android.mk文件里面添加LOCAL_CERTIFICATE := platform(使用系統(tǒng)簽名)
(3)在源碼下面進(jìn)行mm編譯
這樣生成的apk能夠獲取system權(quán)限,可以在任意system權(quán)限目錄下面進(jìn)行目錄或者文件的創(chuàng)建,以及訪問其他apk資源等(注意創(chuàng)建的文件(夾)只有創(chuàng)建者(比如system,root除外)擁有可讀可寫權(quán)限
運行機(jī)制與IPC基礎(chǔ)
首先我們知道Android系統(tǒng)是基于JVM(Dalvik與ART)
其次系統(tǒng)會為每個進(jìn)程分配一個獨立的虛擬機(jī),意味著進(jìn)程間的內(nèi)存的相互獨立的
一般來說使用多進(jìn)程會有幾個問題
1 靜態(tài)成員與單利模式完全失效
2 線程同步機(jī)制完全失效
3 SP可靠性下降
4 Application創(chuàng)建多次
問:進(jìn)程間的對象傳遞問題怎么解決?
通過IBinder來實現(xiàn)進(jìn)程間對象的轉(zhuǎn)換
IPC基礎(chǔ)概念介紹
主要包含三部分:Serialiazable,Parcelable以及Binder
序列化:將對象轉(zhuǎn)化為字節(jié)的過程
Serialiazable:Java提供的序列化接口(標(biāo)記接口)
Parcelable:android提供的序列化接口
Serialiazable與Parcelable的區(qū)別:Serialiazable使用簡單但是需要大量I/O操作,Parcelable使用較繁瑣,主要用于內(nèi)存序列化,效率高
Binder
直觀的看,Binder是Android中的一個類,實現(xiàn)了IBinder接口
從不同角度理解Binder:
1 從IPC角度,Binder是跨進(jìn)程通信方式
2 從FrameWork角度,Binder是ServiceManager連接各種Manager(如am,wm等)的橋梁
3 從應(yīng)用層角度,Binder是客戶端與服務(wù)端通信的媒介
AIDL的初步了解
1 創(chuàng)建Book.java實體類(實現(xiàn)Parcelable接口,支持內(nèi)存序列化)
2 創(chuàng)建Book類的aidl聲明
3 創(chuàng)建IBookManager的aidl接口
實現(xiàn)Parcelable接口的Book類
public class Book implements Parcelable{
private int id;
private String name;
private double price;
public Book(int id, String name, double price) {
this.id = id;
this.name = name;
this.price = price;
}
public Book() {
}
protected Book(Parcel in) {
id = in.readInt();
name = in.readString();
price = in.readDouble();
}
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];
}
};
@Override
public String toString() {
return "Book{" +
"id=" + id +
", name='" + name + '\'' +
", price=" + price +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(id);
parcel.writeString(name);
parcel.writeDouble(price);
}
}
其實內(nèi)部是通過Parcel進(jìn)行對象的轉(zhuǎn)換的,咱們順便看看Parcel類的描述
Container for a message (data and object references) that can
be sent through an IBinder. A Parcel can contain both flattened data
that will be unflattened on the other side of the IPC (using the various
methods here for writing specific types, or the general
{@link Parcelable} interface), and references to live {@link IBinder}
objects that will result in the other side receiving a proxy IBinder
connected with the original IBinder in the Parcel.
簡單的翻譯下,Parcel是IBinder發(fā)送消息(數(shù)據(jù)和對象引用)的容器,Parcel可以數(shù)據(jù)通過各個操作基礎(chǔ)類型的方法將數(shù)據(jù)轉(zhuǎn)化為扁平化傳遞給IPC的另一端,并且通過IBinder使得對方收到的代理IBinder對象中包裹著原始IBinder??偟恼f它的作用就是實現(xiàn)對象數(shù)據(jù)在內(nèi)存中的傳遞
Book的aidl聲明
package com.example.pangyunxiao.ipcdemo.com.itszt.l;
parcelable Book;
我用的IDE是Android Studio,直接右鍵創(chuàng)建aidl文件。會自動幫你創(chuàng)建aidl目錄,并且將你創(chuàng)建的aidl聲明放置到你的項目包名下,如果的eclipse則開發(fā)者要自行注意。
IBookManager的聲明
package com.example.pangyunxiao.ipcdemo.com.itszt.l;
import com.example.pangyunxiao.ipcdemo.com.itszt.l.Book;
import com.example.pangyunxiao.ipcdemo.com.itszt.l.IOnNewBookAddedListener;
// Declare any non-default types here with import statements
interface IBookManager {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
void addBook(in Book book);
List<Book> getBooks();
void addBookAndNotify(in Book book);
void registerListener(IOnNewBookAddedListener l);
void unregisterListener(IOnNewBookAddedListener l);
}
注意問題
1 實體類的包名與aidl文件的包名必須一致
2 IBookManager中必須import所需實體類
3 接口中聲明的方法參數(shù)需要使用in/out/inout
4 aidl文件以及實體類在兩端都要有,并且同個包下
IBookManager.java簡析
接著咱們編譯一下項目,會發(fā)現(xiàn)自動生成的IBookManager.java類。路徑:eclipse中位于gen目錄下,as中位于build\generated\source\aidl\debug目錄下,接著咱們來簡要分析下這個類(由于類比長,就不把代碼全貼出來啦)
public interface IBookManager extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.example.pangyunxiao.ipcdemo.com.itszt.l.IBookManager {
private static final java.lang.String DESCRIPTOR = "com.example.pangyunxiao.ipcdemo.com.itszt.l.IBookManager";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.pangyunxiao.ipcdemo.com.itszt.l.IBookManager interface,
* generating a proxy if needed.
*/
public static com.example.pangyunxiao.ipcdemo.com.itszt.l.IBookManager asInterface(android.os.IBinder obj) {
...
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
...
}
private static class Proxy implements com.example.pangyunxiao.ipcdemo.com.itszt.l.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;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
...
}
@Override
public void addBook(com.example.pangyunxiao.ipcdemo.com.itszt.l.Book book) throws android.os.RemoteException {
...
}
@Override
public java.util.List<com.example.pangyunxiao.ipcdemo.com.itszt.l.Book> getBooks() throws android.os.RemoteException {
...
}
@Override
public void addBookAndNotify(com.example.pangyunxiao.ipcdemo.com.itszt.l.Book book) throws android.os.RemoteException {
...
}
@Override
public void registerListener(com.example.pangyunxiao.ipcdemo.com.itszt.l.IOnNewBookAddedListener l) throws android.os.RemoteException {
...
}
@Override
public void unregisterListener(com.example.pangyunxiao.ipcdemo.com.itszt.l.IOnNewBookAddedListener l) throws android.os.RemoteException {
...
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_getBooks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_addBookAndNotify = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
static final int TRANSACTION_registerListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
static final int TRANSACTION_unregisterListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public void addBook(com.example.pangyunxiao.ipcdemo.com.itszt.l.Book book) throws android.os.RemoteException;
public java.util.List<com.example.pangyunxiao.ipcdemo.com.itszt.l.Book> getBooks() throws android.os.RemoteException;
public void addBookAndNotify(com.example.pangyunxiao.ipcdemo.com.itszt.l.Book book) throws android.os.RemoteException;
public void registerListener(com.example.pangyunxiao.ipcdemo.com.itszt.l.IOnNewBookAddedListener l) throws android.os.RemoteException;
public void unregisterListener(com.example.pangyunxiao.ipcdemo.com.itszt.l.IOnNewBookAddedListener l) throws android.os.RemoteException;
}
DESCRIPTOR:Binder的唯一標(biāo)識
1 這是個接口類并且集成了IInterface接口,所有可以在Binder中傳輸?shù)慕涌诙夹枰^承IIterface接口
2 Stub內(nèi)部類:是IBookManager類的內(nèi)部類,就是一個Binder類
asInterface 靜態(tài)方法:Binder對象轉(zhuǎn)換為接口類型對象
asBinder 非靜態(tài)方法:接口類型對象轉(zhuǎn)換為Binder對象
onTransact方法:當(dāng)客戶端發(fā)起遠(yuǎn)程調(diào)用時,在服務(wù)端會執(zhí)行該方法;參數(shù)code:標(biāo)識調(diào)用的方法;參數(shù)data:傳遞的參數(shù);參數(shù)reply:用于存儲返回值;該方法返回值是boolean表示遠(yuǎn)程調(diào)用是否成功(可做權(quán)限驗證)
3 Proxy:Stub的內(nèi)部類,作用是作為IBookManager的代理類(asInterface返回的其實就是它);在該類中主要實現(xiàn)了各個方法的執(zhí)行流程。例如addBook:方法運行在客戶端,首先獲取兩個Parcel對象(參數(shù)_data與返回值_reply),然后把參數(shù)信息寫入_data中,調(diào)用IBinder對象的transact方法發(fā)起遠(yuǎn)程調(diào)用,此時當(dāng)前線程被掛起然后服務(wù)器端的onTransact執(zhí)行,當(dāng)執(zhí)行完畢,當(dāng)前線程繼續(xù)執(zhí)行,如果方法有返回值的話通過_reply取得。
注意點
當(dāng)客戶端發(fā)起請求時當(dāng)前線程會被掛起直至服務(wù)端返回數(shù)據(jù)。那么如果一個遠(yuǎn)程方法是耗時的就不能在UI線程發(fā)起遠(yuǎn)程請求
2 AIDL文件并不是必須的,只是為了方便系統(tǒng)根據(jù)aidl文件生存java類,我們可以拋開aidl文件直接寫一個Binder
(1創(chuàng)建IBookManager接口類2IBookManager實現(xiàn)類3 Servic中返回)
3 Binder運行在服務(wù)端進(jìn)程,如果服務(wù)器進(jìn)程由于某種原因終止了,那么遠(yuǎn)程調(diào)用將失敗(稱為Binder死亡),可以通過Binder對象的linkToDeath方法設(shè)置死亡代理
實現(xiàn)IPC的方式
怎么實現(xiàn)IPC?其實只要完成它的本質(zhì)需求,就是在進(jìn)程之間傳遞數(shù)據(jù),那就算實現(xiàn)了IPC。因此我們有很多種方式可以完成這個目標(biāo)。實際開發(fā)過程中可以選擇合適的方式去實現(xiàn)這個數(shù)據(jù)傳遞過程。
1 使用Bundle
這是最簡單的方式,它就是通過Bundle在不同進(jìn)程的組件之間傳遞數(shù)據(jù)
startActivity(new Intent(...).putExtra("data",bundle));
2 使用文件共享
利用多進(jìn)程同時讀寫同個外部文件達(dá)到是數(shù)據(jù)交互的目的
存儲形式?jīng)]有限制:xml,文本,對象序列化等等
缺點:由于Linux系統(tǒng)對文件并發(fā)讀寫沒有限制,會導(dǎo)致數(shù)據(jù)不同步問題,所以該方式只適合于對數(shù)據(jù)同步要求不高的進(jìn)程間通信
3 使用共享參數(shù)
共享參數(shù)是android中一中輕量級存儲方案,底層用實現(xiàn)xml文件,系統(tǒng)對它的讀寫有一定的緩存策略(內(nèi)存中會有一份sp的備份)在多進(jìn)程模式下,系統(tǒng)對它的讀/寫是不可靠的,高并發(fā)讀/寫時有可能會丟失數(shù)據(jù)
4 使用Messenger
Messenger(信使):在不同的進(jìn)程之間傳遞Message對象,是一種輕量級的IPC方案,底層實現(xiàn)就是AIDL
客戶端使用服務(wù)器的messenger向服務(wù)器發(fā)消息
服務(wù)器使用客戶端的messenger向客戶端發(fā)消息
(類似Handler的使用)
注意Service的隱式啟動再5.0+上的問題,需要將隱式Intent轉(zhuǎn)為顯式
服務(wù)端Handler
public class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what == 1){
Log.e("MessengerHandler", msg.getData().getString("msg"));
Messenger client = msg.replyTo;
Message message = Message.obtain();
message.what = 1;
Bundle bundle = new Bundle();
bundle.putString("reply","shou dao le.");
message.setData(bundle);
try {
client.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
服務(wù)端Service
public class MessengerService extends Service {
Messenger messenger = new Messenger(new MessengerHandler());
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
}
<service android:name=".MessengerService" android:enabled="true" android:exported="true">
<intent-filter>
<action android:name="com.itszt.lww"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
客戶端Handler
public class ClientMHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.what == 1){
String str = msg.getData().getString("reply");
if (BuildConfig.DEBUG) Log.e("ClientMHandler", str);
}
}
}
客戶端調(diào)起遠(yuǎn)程服務(wù)
intent = new Intent("com.itszt.lww");
bindService(createExplicitFromImplicitIntent(this,intent),conn, Service.BIND_AUTO_CREATE);
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Messenger messenger = new Messenger(iBinder);
if(messenger == null) return;
Message message = Message.obtain();
message.what = 1;
Bundle bundle = new Bundle();
bundle.putString("msg","i am a client.");
message.setData(bundle);
message.replyTo = new Messenger(new ClientMHandler());
try {
messenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
使用AIDL
在上述的AIDL初了解的基礎(chǔ)上,咱們來實現(xiàn)簡單的AIDL通信。增加一個Service提供給客戶端調(diào)起,并返回咱們定義IBookManager對應(yīng)的IBinder對象
public class AIDLService extends Service {
// 保證高并發(fā)問題
List<Book> books = new CopyOnWriteArrayList<>();
RemoteCallbackList<IOnNewBookAddedListener> listeners = new RemoteCallbackList<>();
{
books.add(new Book(1,"java",66.3));
books.add(new Book(2,"php",61.3));
books.add(new Book(3,"c++",65.3));
}
IBookManager bookManager = new IBookManager.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public void addBook(Book book) throws RemoteException {
books.add(book);
}
@Override
public List<Book> getBooks() throws RemoteException {
// 前面我們有提到AIDL只支持ArrayList
// 那么為什么我們這邊可以返回CopyOrWriteArrayList
// 是這樣的,服務(wù)器返回的CopyOrWriteArrayList會按List規(guī)范生成一個ArrayList傳給客戶端
// 類似的還有ConcurrentHashMap
return books;
}
@Override
public void addBookAndNotify(Book book) throws RemoteException {
addBook(book);
if(listeners == null) return;
int count = listeners.beginBroadcast();
for(int i = 0;i<count;i++){
IOnNewBookAddedListener listener = listeners.getBroadcastItem(i);
listener.onNewBook();
}
listeners.finishBroadcast();
}
@Override
public void registerListener(IOnNewBookAddedListener l) throws RemoteException {
if(listeners == null)
listeners = new RemoteCallbackList<>();
listeners.beginBroadcast();
listeners.register(l);
listeners.finishBroadcast();
if (BuildConfig.DEBUG) Log.e("AIDLServiceAdd", "listeners.size():" + listeners.beginBroadcast());
listeners.finishBroadcast();
}
@Override
public void unregisterListener(IOnNewBookAddedListener l) throws RemoteException {
if(listeners != null){
listeners.beginBroadcast();
listeners.unregister(l);
listeners.finishBroadcast();
}
if (BuildConfig.DEBUG) Log.e("AIDLServiceRemove", "listeners.size():" + listeners.beginBroadcast());
listeners.finishBroadcast();
}
};
@Override
public IBinder onBind(Intent intent) {
return bookManager.asBinder();
}
}
<service android:name=".AIDLService" android:enabled="true" android:exported="true">
<intent-filter>
<action android:name="com.itszt.aidl"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</service>
客戶端調(diào)起服務(wù)獲得IBinder
public class MainActivity extends Activity {
IBookManager bookManager;
IOnNewBookAddedListener listener = new IOnNewBookAddedListener.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public void onNewBook() throws RemoteException {
Toast.makeText(MainActivity.this, "收到新書通知啦??!", Toast.LENGTH_SHORT).show();
}
};
Intent intent;
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
if(iBinder == null) return;
bookManager = IBookManager.Stub.asInterface(iBinder);
if(bookManager == null) return;
try {
iBinder.linkToDeath(new IBinder.DeathRecipient() {
@Override
public void binderDied() {
unbindService(conn);
bindService(intent,conn,Service.BIND_AUTO_CREATE);
}
},0);
bookManager.registerListener(listener);
Toast.makeText(MainActivity.this, bookManager.getBooks().toString(), Toast.LENGTH_SHORT).show();
bookManager.addBook(new Book(5,"mXx",90));
bookManager.addBookAndNotify(new Book(5,"mXx",90));
// Toast.makeText(MainActivity.this, bookManager.getBooks().toString(), Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
intent = new Intent("com.itszt.aidl");
bindService(createExplicitFromImplicitIntent(this,intent),conn, Service.BIND_AUTO_CREATE);
}
public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
// Retrieve all services that can match the given intent
PackageManager pm = context.getPackageManager();
List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
// Make sure only one match was found
if (resolveInfo == null || resolveInfo.size() != 1) {
return null;
}
// Get component info and create ComponentName
ResolveInfo serviceInfo = resolveInfo.get(0);
String packageName = serviceInfo.serviceInfo.packageName;
String className = serviceInfo.serviceInfo.name;
ComponentName component = new ComponentName(packageName, className);
// Create a new intent. Use the old one for extras and such reuse
Intent explicitIntent = new Intent(implicitIntent);
// Set the component to be explicit
explicitIntent.setComponent(component);
return explicitIntent;
}
@Override
protected void onDestroy() {
if(bookManager!=null && bookManager.asBinder().isBinderAlive()){
try {
bookManager.unregisterListener(listener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
super.onDestroy();
}
}
AIDL中可以使用的數(shù)據(jù)類型
基本數(shù)據(jù)類型(int char double boolean long)
String和CharSequence
List:只支持ArrayList并且每個元素都被aidl支持
Map:只支持HashMap并且每個元素都被aidl支持
Parcelable:所有實現(xiàn)了Pacelable接口的對象
AIDL:所有aidl接口本身也可以在aidl中使用
注意:
1 在AIDL文件中必須顯式import所使用到的Parcelable子類以及aidl
2 所有用于AIDL的Parcelable類必須創(chuàng)建一個同名的aidl文件(聲明它為Parcelable類型)
3 除了基本數(shù)據(jù)類型外其他類型的方法參數(shù)都必須標(biāo)識方向(in/out/inout)
不要圖方便全部使用inout,因為這在底層實現(xiàn)是有開銷的
Stub是服務(wù)器與Binder的中介
Proxy是客戶端與Binder的中介
多進(jìn)程間是內(nèi)存獨立的,進(jìn)程1傳遞對象A給進(jìn)程2,Binder會將對象A重新轉(zhuǎn)換生成一個新的對象。對象是不能跨進(jìn)程直接傳輸?shù)?,對象的跨進(jìn)程傳輸本質(zhì)是序列化
RemoteCallbackLsit是系統(tǒng)專門提供用于管理跨進(jìn)程Listener的,其內(nèi)部封裝一個Map
6 ContentProvider
ContentProvider 同樣可以實現(xiàn)IPC,或者換個說法。ContentProvider 本質(zhì)就是通過AIDL實現(xiàn)的。只不過它的職責(zé)專一,就是為其他應(yīng)用提供數(shù)據(jù)的
Binder連接池
在前面說到AIDL的使用及原理的時候,我們可以看到在服務(wù)端只是創(chuàng)建了一個Binder然后返回給客戶端使用而已。于是我們可以想到是不是我們可以只有一個Service,對于不同可客戶端我們只是去返回一個不同的Binder即可,這樣就避免了創(chuàng)建了大量的Service。在任玉剛的《android開發(fā)藝術(shù)探索》給出了一個Binder連接池的概念,很巧妙的避免了Service的多次創(chuàng)建。這個Binder連接池類似于設(shè)計模式中的工廠方法模式。為每一個客戶端創(chuàng)建他們所需要的Binder對象。那么下面我們看一下它是如何實現(xiàn)的
首先我定義了多個AIDL,總的就是提供BookManager與StudentManager的功能。注意,這邊還有個IBinderPool,這個的功能是提供用戶選擇具體需要的IBinder對象
服務(wù)端 BinderPoolService 中返回了binderPool對象,客戶端可以通過該對象的queryBinder方法獲取對應(yīng)的服務(wù)
public class BinderPoolService extends Service {
public class BookManagerImpl extends IBookManager.Stub{
List<Book> bs = new CopyOnWriteArrayList<>();
{
bs.add(new Book(1,"android",33));
}
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public List<Book> getBooks() throws RemoteException {
return bs;
}
}
public class StudentManagerImpl extends IStudentManager.Stub{
List<Student> ss = new CopyOnWriteArrayList<>();
{
ss.add(new Student(1,"lee",19));
}
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public List<Student> getStudents() throws RemoteException {
return ss;
}
}
IBookManager bookManager = new BookManagerImpl();
IStudentManager studentManager = new StudentManagerImpl();
IBinderPool binderPool = new IBinderPool.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public IBinder queryBinder(int binderCode){
switch (binderCode){
case 1:
return bookManager.asBinder();
case 2:
return studentManager.asBinder();
}
return null;
}
};
@Override
public IBinder onBind(Intent intent) {
return binderPool.asBinder();
}
}
客戶端的定義了 BinderPool,相當(dāng)于提供遠(yuǎn)程服務(wù)調(diào)用工具類。這邊可以考慮使用CountDownLatch將遠(yuǎn)程服務(wù)的連接這個異步操作轉(zhuǎn)為同步
public class BinderPool {
private Context context;
private CountDownLatch countDownLatch;
private static BinderPool binderPool;
private IBinderPool iBinderPool;
private BinderPool(Context context) {
this.context = context;
// 連接遠(yuǎn)程線程池服務(wù)
connectBinderPoolService();
}
public static BinderPool getInstance(Context context) {
if (binderPool == null) {
synchronized (String.class) {
if (binderPool == null)
binderPool = new BinderPool(context);
}
}
return binderPool;
}
private synchronized void connectBinderPoolService() {
// countDownLatch = new CountDownLatch(1);
Intent intent = new Intent("com.itszt.lwwp");
context.bindService(ServiceIntentUtil.createExplicitFromImplicitIntent(context, intent),
conn, Service.BIND_AUTO_CREATE);
// try {
// // countDownLatch.await();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
iBinderPool = IBinderPool.Stub.asInterface(iBinder);
// countDownLatch.countDown();
if (null != iBinderPool) {
try {
iBinderPool.asBinder().linkToDeath(deathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (iBinderPool != null) {
iBinderPool.asBinder().unlinkToDeath(this, 0);
iBinderPool = null;
// 重連
connectBinderPoolService();
}
}
};
public IBinder queryBinder(int binderCode) {
try {
if (iBinderPool != null)
return iBinderPool.queryBinder(binderCode);
return null;
} catch (RemoteException e) {
e.printStackTrace();
return null;
}
}
}
客戶端通過BinderPool調(diào)起遠(yuǎn)程服務(wù)
new Thread(){
@Override
public void run() {
binderPool = BinderPool.getInstance(MainActivity.this);
}
}.start();
public void onTest(View view) {
if(binderPool == null) return;
IBookManager bookManager = IBookManager.Stub.asInterface(binderPool.queryBinder(1));
IStudentManager studentManager = IStudentManager.Stub.asInterface(binderPool.queryBinder(2));
try {
Toast.makeText(this, "bookManager.getBooks():" + bookManager.getBooks().toString(), Toast.LENGTH_SHORT).show();
Toast.makeText(this, "studentManager.getBooks():" + studentManager.getStudents().toString(), Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
總結(jié)
IPC是什么?是跨進(jìn)程通信
AIDL是什么?是IPC方式的一種
IPC為什么會導(dǎo)致那么多問題?內(nèi)存獨立
我們在什么時候需要IPC?某功能需要運行在獨立進(jìn)行;提升應(yīng)用分配內(nèi)存;應(yīng)用間交互