首先,進程通常指一個執行單元,在移動設備上就是一個程序或應用,咱們在Android中所說的多進程(IPC)通常指一個應用包含多個進程。之因此要使用多進程有兩方面緣由:某些模塊因爲特殊的需求要運行在單獨的進程;增長應用可用的內存空間。java
Android中開啓多線程只有一種方法,就是在AndroidManifest.xml中註冊Service、Activity、Receiver、ContentProvider時指定android:process
屬性,例如:android
<service android:name=".MyService" android:process=":remote">
</service>
<activity android:name=".MyActivity" android:process="com.shh.ipctest.remote2">
</activity>
複製代碼
咱們爲MyService
和MyActivity
指定的android:process
屬性值有所不一樣,它們的區別以下:git
:remote
:以:
開頭是一種簡寫,系統會在當前進程名前附件當前包名,完整的進程名爲:com.shh.ipctest:remote
,同時以:
開頭的進程屬於當前應用的私有進程,其它應用的組件不能和它跑在同一進程。com.shh.ipctest.remote2
:這是完整的命名方式,不會附加包名,其它應用若是和該進程的ShareUID
、簽名
相同,則能夠和它跑在同一個進程,實現數據共享。開啓多進程雖簡單,但會引起以下問題,必須引發注意。github
對於前兩個問題,能夠這麼理解,在Android中,系統會爲每一個應用或進程分配獨立的虛擬機,不一樣的虛擬機天然佔有不一樣的內存地址空間,因此同一個類的對象會產生不一樣的副本,致使共享數據失敗,必然也不能實現線程的同步。安全
因爲SharedPreferences
底層採用讀寫XML的文件的方式實現,多進程併發的的讀寫極可能致使數據異常。bash
Application
被屢次建立和前兩個問題相似,系統在分配多個虛擬機時至關於把同一個應用從新啓動屢次,必然會致使 Application 屢次被建立,爲了防止在 Application 中出現無用的重複初始化,可以使用進程名來作過濾,只讓指定進程的才進行全局初始:網絡
public class MyApplication extends Application{
@Override
public void onCreate() {
super.onCreate();
String processName = "com.shh.ipctest";
if (getPackageName().equals(processName)){
// do some init
}
}
}
複製代碼
Android中支持的多進程通訊方式主要有如下幾種,它們之間各有優缺點,可根據使用場景選擇選擇:多線程
這裏咱們主要討論四大組件中Service在多進程通訊中的使用,這就涉及到了 AIDL、Messenger這兩種多進程通訊方式,接下來重點看這兩種 IPC 方式。併發
AIDL 的意思是 Android 接口定義語言,使用AIDL進行進程間通訊須要定義服務端和客戶端,其中客戶端和服務端能夠在同一應用也能夠在不一樣應用。這裏咱們服務端能夠看作是圖書館,爲客戶端提供近期新書查詢、圖書捐贈、新書通知的服務。app
先建立一個 AIDL 文件,聲明服務端要暴露給客戶端的接口,而後建立一個 Service 監聽客戶端的鏈接請求,並在其中實現 AIDL 文件中的接口。
注意,爲了方便開發,咱們通常把 AIDL 相關的文件放在同一包中,這樣當客戶端是另外一個應用時可方便的把整個包複製到客戶端工程中。最終的AIDL文件包以下:
首先了解下 AIDL 文件支持的幾種數據類型:
Book
是實現了Parcelable
的圖書類,只定義了圖書名name
字段,按照規定若是 AIDL 文件用到了自定義Parcelable
對象,同時須要提供一個Book.aidl
文件:
package com.shh.ipctest;
parcelable Book;
複製代碼
ILibraryManager.aidl
定義了服務端要暴露給客戶端的接口:
package com.shh.ipctest;
import com.shh.ipctest.Book;
interface ILibraryManager{
// 近期新書查詢
List<Book> getNewBookList();
// 圖書捐贈
void donateBook(in Book book);
}
複製代碼
注意,儘管ILibraryManager.aidl
和Book
在同一包中,仍是須要顯示的導入Book
類。除了基本類型數據外,其它類型的參數須要標註方向,可選的方向標識有:
in
:輸入類型參數out
:輸出類型參數inout
:輸入輸出類型參數接下來就是LibraryManagerService
這個服務類了,在編寫服務類前要先編譯項目,這樣在服務類裏使用 AIDL 生成的java類。
public class LibraryManagerService extends Service {
private static final String TAG = "LibraryManagerService";
// CopyOnWriteArrayList 支持併發讀寫
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
private Binder mBinder = new ILibraryManager.Stub() {
@Override
public List<Book> getNewBookList() throws RemoteException {
return mBookList;
}
@Override
public void donateBook(Book book) throws RemoteException {
mBookList.add(book);
}
};
public LibraryManagerService() {
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book("book0"));
mBookList.add(new Book("book1"));
}
}
複製代碼
首先經過ILibraryManager.Stub()
建立一個mBinder
對象,並實現了ILibraryManager.aidl
中定義的接口方法,在onBind()
方法中返回建立的mBinder
,並在服務onCreate()
時添加兩本書。 最後在 AndroidManifest.xml 註冊服務:
<service android:name=".LibraryManagerService" android:process=":aidl_remote">
</service>
複製代碼
到這裏服務端的基本功能就完成了。
這裏先把客戶端和服務端放在同一個應用,客戶端的實現相對簡單些,首先在項目 app 的build.gradle
指定 AIDL
文件路徑:
android {
......
// 指定 aidl 路徑
sourceSets {
main {
java.srcDirs = ['src/main/java', 'src/main/aidl']
}
}
}
複製代碼
以後就是綁定服務了:
public class AIDLActivity extends AppCompatActivity {
private static final String TAG = "AIDLActivity";
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
ILibraryManager libraryManager = ILibraryManager.Stub.asInterface(service);
try {
// 近期新書查詢
List<Book> books = libraryManager.getNewBookList();
Log.e(TAG, "books:" + books.toString());
// 捐贈一本書
libraryManager.donateBook(new Book("book" + books.size()));
List<Book> books2 = libraryManager.getNewBookList();
Log.e(TAG, "books:" + books2.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl);
bindNewService();
}
private void bindNewService() {
Intent intent = new Intent(AIDLActivity.this, LibraryManagerService.class);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(mServiceConnection);
super.onDestroy();
}
}
複製代碼
先實現ServiceConnection
接口,在onServiceConnected()
方法中將IBinder
對象轉換成ILibraryManager
對象,經過該對象就能調用ILibraryManager.aidl
中聲明的方法了。 接下來綁定服務:
Intent intent = new Intent(AIDLActivity.this, LibraryManagerService.class);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
複製代碼
運行項目後觀察log:
若是客戶端和服務端在不一樣的應用怎麼實現呢?首先將服務端的 AIDL 包複製到客戶端項目的main
目錄下,在客戶端項目 app 的build.gradle
指定 AIDL
文件路徑,最後就是綁定服務了,因爲客戶端須要隱式綁定服務,因此要先修改服務端LibraryManagerService
的註冊方式爲:
<service android:name=".LibraryManagerService" android:process=":aidl_remote">
<intent-filter>
<action android:name="android.intent.action.LibraryManagerService" />
</intent-filter>
</service>
複製代碼
而後就是在客戶端隱式啓動服務:
Intent intent = new Intent();
intent.setAction("android.intent.action.LibraryManagerService");
intent.setPackage("com.shh.ipctest");
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
複製代碼
若是要添加一個新書提醒功能,即圖書館每採購一本新書後須要通知訂閱了新書提醒功能的用戶,要怎麼修改服務端和客戶端呢?
首先建立一個服務端通知客戶端的 IOnNewBookArrivedListener.aidl
接口:
package com.shh.ipctest;
import com.shh.ipctest.Book;
interface IOnNewBookArrivedListener {
void onNewBookArrived(in Book book);
}
複製代碼
咱們約定服務端要先註冊後才能收到通知,同時也能夠取消註冊,因此要給以前的ILibraryManager.aidl
添加連個方法了:
package com.shh.ipctest;
import com.shh.ipctest.Book;
import com.shh.ipctest.IOnNewBookArrivedListener;
interface ILibraryManager{
......
// 註冊通知
void register(IOnNewBookArrivedListener listener);
// 取消註冊
void unregister(IOnNewBookArrivedListener listener);
}
複製代碼
接下來就是修改服務端的LibraryManagerService
:
// 只保留了相關核心代碼
public class LibraryManagerService extends Service {
......
// 系統提供的專門用於保存、刪除跨進程 listener 的類
private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();
// AtomicBoolean 支持併發讀寫
private AtomicBoolean mIsServiceDestroy = new AtomicBoolean(false);
private Binder mBinder = new ILibraryManager.Stub() {
......
@Override
public void register(IOnNewBookArrivedListener listener) throws RemoteException {
mListenerList.register(listener);
Log.e(TAG, "register success");
}
@Override
public void unregister(IOnNewBookArrivedListener listener) throws RemoteException {
mListenerList.unregister(listener);
Log.e(TAG, "unregister success");
}
};
.......
@Override
public void onCreate() {
super.onCreate();
......
// 在子線程中每隔3秒建立一本新書,並通知全部已註冊的客戶端
new Thread(new Runnable() {
@Override
public void run() {
// 若是服務還沒終止
while (!mIsServiceDestroy.get()) {
try {
Thread.sleep(3 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Book book = new Book("book" + mBookList.size());
mBookList.add(book);
bookArrivedNotify(book);
}
}
}).start();
}
private void bookArrivedNotify(Book book) {
int n = mListenerList.beginBroadcast();
for (int i = 0; i < n; i++) {
IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
if (listener != null) {
try {
listener.onNewBookArrived(book);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
mListenerList.finishBroadcast();
}
@Override
public void onDestroy() {
super.onDestroy();
mIsServiceDestroy.set(true);
}
}
複製代碼
注意這裏用到了RemoteCallbackList
類,它是系統提供的專門用於刪除跨進程 listener 的類,用普通的集合難以保證客戶端註冊的 listener 和服務端存儲的 listener 是同一個,會取消註冊失敗。在的register()
、unregister()
中實現了通知接口的綁定和解綁操做。在onCreate()
週期性的通知客戶端有新書了。
在客戶端中須要完成通知接口的註冊和取消註冊:
// 只保留了相關核心代碼
public class AIDLActivity extends AppCompatActivity {
......
private ILibraryManager mLibraryManager;
private Handler mHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_NEW_BOOK_ARRIVED:
Log.e(TAG, "new book:" + msg.obj);
break;
}
return true;
}
});
private IOnNewBookArrivedListener listener = new IOnNewBookArrivedListener.Stub() {
@Override
public void onNewBookArrived(Book book) throws RemoteException {
// 因爲 onNewBookArrived 方法在子線程被調用,因此經過Handler切換到UI線程,方便UI操做
mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, book).sendToTarget();
}
};
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
ILibraryManager libraryManager = ILibraryManager.Stub.asInterface(service);
mLibraryManager = libraryManager;
try {
......
// 註冊通知
libraryManager.register(listener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
......
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl);
bindNewService();
}
@Override
protected void onDestroy() {
unbindService(mServiceConnection);
if (mLibraryManager != null && mLibraryManager.asBinder().isBinderAlive()) {
try {
// 取消註冊
mLibraryManager.unregister(listener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
super.onDestroy();
}
}
複製代碼
首先是建立IOnNewBookArrivedListener
接口的對象,爲了方便 UI 操做能夠在回調中用Handler
將服務端通知的結果切換到 UI 線程,並打印新書名,在onServiceConnected()
中註冊接口,在onDestroy()
中取消註冊。運行項目後看下客戶端、服務端的log:
目前咱們的服務端任何客戶端均可以鏈接,項目開發中爲了安全起見,咱們須要在服務端作權限校驗,只有客戶端配置了指定的權限,才能調用服務端的方法。
服務端可使用permission
+包名
的方式來驗證,首先在服務端的AndroidManifest.xml聲明一個permission
:
<permission android:name="com.shh.ipctest.permission.ACCESS_LIBRARY_SERVICE" android:protectionLevel="normal" />
複製代碼
接下來就是在LibraryManagerService
中校驗要鏈接的客戶端是配置了該permission
,以及包名是否符合指定規則,能夠作校驗的地方有兩個,第一個是在onBind()
中校驗:
@Override
public IBinder onBind(Intent intent) {
if (!passBindCheck()) {
Log.e(TAG, "bind denied");
return null;
}
return mBinder;
}
private boolean permissionCheck() {
// 客戶端是否已申請了指定權限
int check = checkCallingOrSelfPermission("com.shh.ipctest.permission.ACCESS_LIBRARY_SERVICE");
if (check == PackageManager.PERMISSION_DENIED) {
return false;
}
// 檢驗客戶端包名會否以com.shh開頭
String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
if (packages != null && packages.length > 0 && !packages[0].startsWith("com.shh")) {
return false;
}
return true;
}
複製代碼
注意,onBind()
是在客戶端鏈接服務端時調用,若是客戶端不能在此處經過校驗則無發鏈接到服務。若是客戶端和服務端是兩個應用,則沒法在onBind()
實現校驗的功能!
第二個地方是在ILibraryManager.Stub
類的onTransact()
方法中,在該方法裏校驗解決了客戶端和服務端是兩個應用時沒法在onBind()
實現校驗的問題,代碼以下:
private Binder mBinder = new ILibraryManager.Stub() {
......
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
if (!permissionCheck()) {
Log.e(TAG, "bind denied");
return false;
}
return super.onTransact(code, data, reply, flags);
}
};
複製代碼
注意,該方法在onBind()
以後執行,意味着客戶端和服務端已經鏈接,當客戶端調用服務端的方法時會走onTransact()
方法。
最後須要在客戶端的AndroidManifest.xml配置permission
權限:
<uses-permission android:name="com.shh.ipctest.permission.ACCESS_LIBRARY_SERVICE" />
複製代碼
服務端進程可能會因爲內存不足等緣由意外終止而致使服務被殺死,因此有必要在這種狀況下從新鏈接到服務。鏈接的方式有兩種:
第一種相對簡單,是在ServiceConnection
接口的onServiceDisconnected()
方法中從新鏈接服務,注意該方法在UI線程執行。
第二種是給客戶端獲得的Binder
對象註冊一個DeathRecipient
監聽,首先來建立該監聽:
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (mLibraryManager != null) {
mLibraryManager.asBinder().unlinkToDeath(mDeathRecipient, 0);
mLibraryManager = null;
// 從新鏈接服務
bindNewService();
}
}
}
複製代碼
當服務被殺死時binderDied()
方法會被調用,接下來就是在服務鏈接成功後設置死亡監聽:
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
ILibraryManager libraryManager = ILibraryManager.Stub.asInterface(service);
mLibraryManager = libraryManager;
try {
// 將mLibraryManager轉成Binder對象而後註冊死亡監聽
mLibraryManager.asBinder().linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
......
}
複製代碼
在 AIDL 的例子中,客戶端之因此能和服務端通訊,主要依靠系統提供的Binder
類實現,它實現了IBinder
接口,咱們在前邊的例子已經用到了Binder
類,例如在服務端的 Service 中建立了一個Binder
對象並在onBind()
中返回:
Binder mBinder = new ILibraryManager.Stub(){
......
}
複製代碼
當在客戶端綁定服務成功後,會獲得服務端返回的Binder
對象,並將其轉換成可調用服務端暴露的方法的ILibraryManager
對象:
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
ILibraryManager libraryManager = ILibraryManager.Stub.asInterface(service);
.......
}
......
};
複製代碼
能夠發現不論是客戶端仍是服務端都用到了ILibraryManager.java
類,回想一下以前咱們建立過一個ILibraryManager.aidl
文件,因此ILibraryManager.java
應該是ILibraryManager.aidl
在編譯後生成的,果真在以下目錄有這個類:
ILibraryManager.java
的代碼以下:
public interface ILibraryManager extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements com.shh.ipctest.ILibraryManager {
// Binder 的惟一標識
private static final java.lang.String DESCRIPTOR = "com.shh.ipctest.ILibraryManager";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
// 將服務端的 Binder 對象轉換成客戶端須要的接口對象
// 若是客戶端和服務端在同一進程則返回 Stub 對象自己,不然返回Stub.Proxy這個代理對象
public static com.shh.ipctest.ILibraryManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.shh.ipctest.ILibraryManager))) {
return ((com.shh.ipctest.ILibraryManager) iin);
}
return new com.shh.ipctest.ILibraryManager.Stub.Proxy(obj);
}
// 返回當前 Binder 對象
@Override
public android.os.IBinder asBinder() {
return this;
}
/** * 運行在服務端的 Binder 線程池,客戶端發起的跨進程方法調用最終會交給該方法處理 * @param code 肯定客戶端調用的是哪一個方法 * @param data 保存要調用的方法須要的參數 * @param reply 保存要調用的方法最後的返回值 * @param flags * @return 返回false調用 * @throws android.os.RemoteException */
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getNewBookList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.shh.ipctest.Book> _result = this.getNewBookList();
// 封裝返回的數據
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_donateBook: {
data.enforceInterface(DESCRIPTOR);
com.shh.ipctest.Book _arg0;
if ((0 != data.readInt())) {
// 反序列化出具體的請求參數
_arg0 = com.shh.ipctest.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.donateBook(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_register: {
data.enforceInterface(DESCRIPTOR);
com.shh.ipctest.IOnNewBookArrivedListener _arg0;
_arg0 = com.shh.ipctest.IOnNewBookArrivedListener.Stub.asInterface(data.readStrongBinder());
this.register(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_unregister: {
data.enforceInterface(DESCRIPTOR);
com.shh.ipctest.IOnNewBookArrivedListener _arg0;
_arg0 = com.shh.ipctest.IOnNewBookArrivedListener.Stub.asInterface(data.readStrongBinder());
this.unregister(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
// 客戶端跨進程調用服務端方法時,會先在客戶端執行該類裏的對應方法,
// 調用mRemote.transact()發起遠程過程調用(RPC)請求,同時客戶端對應的線程會掛起,
// 進而執行服務端的onTransact()方法,直到 RPC 請求結束,客戶端線程繼續執行,
// 這多是一個耗時的過程,因此要避免客戶端出現 ANR 的問題。
private static class Proxy implements com.shh.ipctest.ILibraryManager {
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;
}
@Override
public java.util.List<com.shh.ipctest.Book> getNewBookList() throws android.os.RemoteException {
// 保存請求參數的 Parcel 對象
android.os.Parcel _data = android.os.Parcel.obtain();
// 保存返回結果的 Parcel 對象
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.shh.ipctest.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
// 發起 RPC 請求
mRemote.transact(Stub.TRANSACTION_getNewBookList, _data, _reply, 0);
_reply.readException();
// 從 RPC 請求的結果中取出最終要返回的數據
_result = _reply.createTypedArrayList(com.shh.ipctest.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void donateBook(com.shh.ipctest.Book book) 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 ((book != null)) {
_data.writeInt(1);
// 將參數序列化保存
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_donateBook, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public void register(com.shh.ipctest.IOnNewBookArrivedListener listener) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((listener != null)) ? (listener.asBinder()) : (null)));
mRemote.transact(Stub.TRANSACTION_register, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public void unregister(com.shh.ipctest.IOnNewBookArrivedListener listener) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((listener != null)) ? (listener.asBinder()) : (null)));
mRemote.transact(Stub.TRANSACTION_unregister, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
// 客戶端可調用的方法的對應 code
static final int TRANSACTION_getNewBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_donateBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_register = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_unregister = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
}
public java.util.List<com.shh.ipctest.Book> getNewBookList() throws android.os.RemoteException;
public void donateBook(com.shh.ipctest.Book book) throws android.os.RemoteException;
public void register(com.shh.ipctest.IOnNewBookArrivedListener listener) throws android.os.RemoteException;
public void unregister(com.shh.ipctest.IOnNewBookArrivedListener listener) throws android.os.RemoteException;
}
複製代碼
ILibraryManager
類繼承android.os.IInterface
接口,其實 aidl類型文件中定義的接口在編譯後都會生成一個繼承IInterface
的java類。這個類看起來有點複雜,但結構仍是很清晰的,主要有兩部分,首先是繼承了Binder
的Stub
類,還有ILibraryManager.aidl
中接口聲明的方法。Stub
類中關鍵部分都作了註釋,其實它的核心就是將客戶端進程中發起的跨進程方法調用轉換到服務端進程去執行,最終獲得執行結果!因此直觀的看,在 Service 中Binder
是做爲跨進程通訊的橋樑存在的,在此基礎上,咱們才能更好的理解使用 AIDL 背後的原理!
Messenger
是一種輕量級的多進程通訊方式,它是在 AIDL 的基礎上封裝而成的,能夠看作是 AIDL 的簡化版,支持一對多的串行實時通訊,一次只處理一個請求,不存在併發的問題。和 AIDL 的使用相似,但要簡單的多,一樣須要實現服務端和客戶端。
首先來看服務端,功能就是接收客戶端發送的消息,同時回覆一條消息:
public class MessengerService extends Service {
private static final String TAG = "MessengerService";
// 將Messenger和Handler關聯起來
private Messenger mServiceMessenger = new Messenger(new MessengerHandler());
public MessengerService() {
}
@Override
public IBinder onBind(Intent intent) {
return mServiceMessenger.getBinder();
}
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MessengerActivity.MESSAGE_FROM_CLIENT:
// 打印接收到的客戶端消息
Log.e(TAG, "receive message from client:" + msg.getData().getString("msg"));
// 給客戶端回覆一條消息
Messenger clientMessenger = msg.replyTo;
Message message = Message.obtain();
message.what = MessengerActivity.MESSAGE_FROM_SERVICE;
Bundle bundle = new Bundle();
bundle.putString("msg", "I am fine,thank you!");
message.setData(bundle);
try {
clientMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
}
}
複製代碼
首先建立一個Handler
對象,並在其handleMessage()
中進行消息的接收和回覆,注意回覆消息是經過客戶端傳遞過來的Messenger
對象發送一個Message
對象,有了Handler
對象後,須要把它和服務端建立的Messenger
關聯起來:
Messenger mServiceMessenger = new Messenger(new MessengerHandler());
複製代碼
並在onBind()
中返回服務端Messenger
包含的Binder
對象。
下來看客戶端的實現,和服務端鏈接成功後,給服務端發送一條消息,並接收服務端回覆的消息:
public class MessengerActivity extends AppCompatActivity {
private static final String TAG = "MessengerActivity";
public static final int MESSAGE_FROM_CLIENT = 1;
public static final int MESSAGE_FROM_SERVICE = 2;
private Messenger mServiceMessenger;
private Messenger mClientMessenger = new Messenger(new MessengerHandler());
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mServiceMessenger = new Messenger(service);
Message message = Message.obtain();
message.what = MESSAGE_FROM_CLIENT;
Bundle bundle = new Bundle();
bundle.putString("msg", "how are you?");
message.setData(bundle);
// 傳遞服務端回覆客戶端時須要使用的Messenger
message.replyTo = mClientMessenger;
try {
mServiceMessenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenger);
Intent intent = new Intent(MessengerActivity.this, MessengerService.class);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(mServiceConnection);
super.onDestroy();
}
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MessengerActivity.MESSAGE_FROM_SERVICE:
Log.e(TAG, "receive message from service:" + msg.getData().getString("msg"));
break;
}
}
}
}
複製代碼
在onServiceConnected()
中,將服務端的Binder
轉換成服務端的Messenger
對象,而後發送消息,因爲服務端還須要給客服端回覆消息,因此須要在客戶端建立一個Messenger
對象附加在消息上發送給服務端使用。
在Messenger
中也能夠進行權限校驗、服務端終止從新鏈接的操做,實現了 AIDL 的相似。
最後看下效果:
文中內容參考了《Android 開發藝術探索》,但願對你我有所幫助吧! 源碼地址:github.com/Othershe/IP…