最近在復(fù)習(xí)主席的《Android開發(fā)藝術(shù)探索》這本書,將第二章IPC機(jī)制相關(guān)的部分簡單總結(jié)下俺附。
開啟多進(jìn)程的方式:給四大組件在AndroidMenifest文件中指定 android:process屬性噪服。
Android中的IPC方式主要有六種唾那,分別為:
- 使用Bundle
- 使用文件共享
- 使用Messenger
- 使用AIDL
- 使用ContentProvider
- 使用Socket
下面我們主要看下 3. 使用Messenger 和 4.使用AIDL 這兩種跨進(jìn)程通信的方式埠帕。其實Messenger的底層實現(xiàn)也是AIDL。
使用Messenger:
實現(xiàn)一個Messenger可以分為兩個步驟话侄,分別為服務(wù)端和客戶端亏推。
服務(wù)端進(jìn)程:首先,我們需要在服務(wù)端創(chuàng)建一個Service來處理客戶端的連接請求满葛,同時創(chuàng)建一個Handler,并通過它來創(chuàng)建一個Messenger對象罢屈,然后在Service 的 onBind 方法中返回這個Messenger對象底層的Binder即可嘀韧。
客戶端進(jìn)程:客戶端進(jìn)程中,首先要綁定服務(wù)端的Service缠捌,綁定成功后用服務(wù)端返回的IBinder對象創(chuàng)建一個Messenger锄贷,通過這個Messenger就可以向服務(wù)端發(fā)送消息了,消息類型為Message對象曼月。
下面我們新建一個Demo來測試下谊却,我們在客戶端進(jìn)程中向服務(wù)端發(fā)送一句話,在服務(wù)端進(jìn)程中打印出這句話:
1.新建項目MessengerTest哑芹,創(chuàng)建MessengerService炎辨,讓其運行在單獨的進(jìn)程中。
<service
android:name=".MessengerService"
android:process=":remote"/>
2.服務(wù)端進(jìn)程代碼:
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 MyConstants.MSG_FROM_CLIENT:
Log.e(TAG,"receive msg from client: "+msg.getData().getString("msg"));
break;
default:
break;
}
}
}
private final Messenger mMessenger = new Messenger(new MessengerHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
3.客戶端進(jìn)程代碼:
public class MainActivity extends AppCompatActivity {
private Button mBtnSend;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMessenger = new Messenger(service);
Message msg = Message.obtain(null,MyConstants.MSG_FROM_CLIENT);
Bundle data = new Bundle();
data.putString("msg","hello world");
msg.setData(data);
try {
mMessenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private Messenger mMessenger;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtnSend = (Button) findViewById(R.id.mBtnSend);
mBtnSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,MessengerService.class);
bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
}
});
}
@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}
}
測試結(jié)果如下:
08-19 23:13:42.646 27432-27432/? E/MessengerService: receive msg from client: hello world
上述我們只是發(fā)送消息到服務(wù)端進(jìn)程聪姿,服務(wù)端進(jìn)程對msg進(jìn)行相應(yīng)的操作碴萧。也就是說我們只是實現(xiàn)了客戶端到服務(wù)端的單向通信,如果我們需要服務(wù)端處理完畢后回復(fù)客戶端進(jìn)程的話末购,要怎么操作呢破喻?修改后的代碼如下:
服務(wù)端進(jìn)程:
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 MyConstants.MSG_FROM_CLIENT:
Log.e(TAG,"receive msg from client: "+msg.getData().getString("msg"));
Messenger client = msg.replyTo;
Message replyMessage = Message.obtain(null,MyConstants.MSG_FROM_SERVICE);
Bundle bundle = new Bundle();
bundle.putString("reply","你好,世界");
replyMessage.setData(bundle);
try {
client.send(replyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
break;
}
}
}
private final Messenger mMessenger = new Messenger(new MessengerHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
客戶端進(jìn)程:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private Button mBtnSend;
private Messenger mMessenger;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMessenger = new Messenger(service);
Message msg = Message.obtain(null,MyConstants.MSG_FROM_CLIENT);
Bundle data = new Bundle();
data.putString("msg","hello world");
msg.setData(data);
msg.replyTo = mGetReplyMessenger;
try {
mMessenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());
private static class MessengerHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MyConstants.MSG_FROM_SERVICE:
Log.e(TAG,"receive msg from service: "+msg.getData().getString("reply"));
break;
default:
break;
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtnSend = (Button) findViewById(R.id.mBtnSend);
mBtnSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this,MessengerService.class);
bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
}
});
}
@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}
}
測試結(jié)果如下:
08-19 23:34:04.236 4737-4737/com.example.administrator.messengertest:remote E/MessengerService: receive msg from client: hello world
08-19 23:34:04.236 4386-4386/com.example.administrator.messengertest E/MainActivity: receive msg from service: 你好盟榴,世界
有關(guān)Messenger的操作方式到這里就介紹完畢了曹质,下面我們看下AIDL方式。使用AIDL來進(jìn)行進(jìn)程間通信擎场,同樣分為服務(wù)端和客戶端兩個方面羽德。
服務(wù)端
服務(wù)端首先要創(chuàng)建一個Service用來監(jiān)聽客戶端的連接請求,然后創(chuàng)建一個AIDL文件迅办,將暴露給客戶端的接口在這個AIDL文件中聲明玩般,最后在Service中實現(xiàn)這個AIDL接口即可。客戶端
客戶端所要做的事情就稍微簡單一些礼饱,首先需要綁定服務(wù)端的Service坏为,綁定成功后究驴,將服務(wù)端返回的Binder對象轉(zhuǎn)成AIDL接口所屬的類型,接著就可以調(diào)用AIDL中的方法了匀伏。
下面我們看下代碼:
- 新建項目AIDLTest洒忧,創(chuàng)建BookManagerService,讓其運行在單獨的進(jìn)程中够颠。
<service
android:name=".BookManagerService"
android:process=":remote"/>
-
AIDL文件創(chuàng)建熙侍,為了方便AIDL的開發(fā),將所有和AIDL相關(guān)的類和文件全部放入同一個包中履磨,如下所示:
Book.java:
package com.example.administrator.aidltest;
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable{
public int bookId;
public String bookName;
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(this.bookId);
dest.writeString(this.bookName);
}
protected Book(Parcel in) {
this.bookId = in.readInt();
this.bookName = in.readString();
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel source) {
return new Book(source);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
}
Book.aidl:
// Book.aidl
package com.example.administrator.aidltest;
parcelable Book;
IBookManager.aidl:
// IBookManager.aidl
package com.example.administrator.aidltest;
import com.example.administrator.aidltest.Book;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
- 服務(wù)端進(jìn)程代碼:
package com.example.administrator.aidltest;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class BookManagerService extends Service{
private static final String TAG = "BMS";
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
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);
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
- 客戶端進(jìn)程代碼:
package com.example.administrator.aidltest;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private Button mBookAdd;
private Button mBookGet;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
bookManager = IBookManager.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private IBookManager bookManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initListener();
serviceBind();
}
private void initView(){
mBookAdd = (Button) findViewById(R.id.mBookAdd);
mBookGet = (Button) findViewById(R.id.mBookGet);
}
private void serviceBind(){
Intent intent = new Intent(MainActivity.this,BookManagerService.class);
bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
}
private void initListener(){
mBookAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (bookManager != null){
try {
bookManager.addBook(new Book(001,"第一行代碼"));
bookManager.addBook(new Book(002,"Android藝術(shù)開發(fā)探索"));
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
mBookGet.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (bookManager != null){
try {
List<Book> mBookList = bookManager.getBookList();
for (int i = 0; i<mBookList.size(); i++){
Log.e(TAG,"query book list: "+mBookList.get(i).bookName+"\n");
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
}
@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}
}
- 編譯運行出錯蛉抓,提示找不到符號:
Error:(20, 34) 錯誤: 找不到符號
符號: 類 Book
位置: 類 BookManagerService
解決方式:在 app 目錄下的build.gradle文件中添加java.srcDirs路徑,如下所示:
sourceSets{ //android節(jié)點下
main{
java.srcDirs = ['src/main/java','src/main/aidl']
}
}
測試結(jié)果如下:
08-21 00:01:18.899 30868-30868/com.example.administrator.aidltest E/MainActivity: query book list: 第一行代碼
08-21 00:01:18.899 30868-30868/com.example.administrator.aidltest E/MainActivity: query book list: Android藝術(shù)開發(fā)探索
現(xiàn)在我們考慮下剃诅,如果用戶不想時不時地去查詢圖書列表了巷送,太累了,他去問圖書館矛辕,“當(dāng)有新書的時候笑跛,能不能把新書的信息告訴我呢?”聊品。很顯然這是一種觀察者模式飞蹂,下面我們來實現(xiàn)下:
- 在上述代碼的基礎(chǔ)上,新增 IOnNewBookArrivedListener.aidl文件:
// IOnNewBookArrivedListener.aidl
package com.example.administrator.aidltest;
import com.example.administrator.aidltest.Book;
interface IOnNewBookArrivedListener {
void onNewBookArrived(in Book newBook);
}
- 在原有 IBookManager 接口中增加兩個方法:
// IBookManager.aidl
package com.example.administrator.aidltest;
import com.example.administrator.aidltest.Book;
import com.example.administrator.aidltest.IOnNewBookArrivedListener;
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener listener);
void unregisterListener(IOnNewBookArrivedListener listener);
}
- 服務(wù)端代碼修改如下:
package com.example.administrator.aidltest;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
public class BookManagerService extends Service{
private static final String TAG = "BMS";
private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList =
new CopyOnWriteArrayList<IOnNewBookArrivedListener>();
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 registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
if (!mListenerList.contains(listener)){
mListenerList.add(listener);
}
}
@Override
public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
if (mListenerList.contains(listener)){
mListenerList.remove(listener);
}
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public void onCreate() {
super.onCreate();
new Thread(new ServiceWorker()).start();
}
@Override
public void onDestroy() {
mIsServiceDestroyed.set(true);
super.onDestroy();
}
private void onNewBookArrived(Book book) throws RemoteException {
mBookList.add(book);
for (int i=0; i<mListenerList.size(); i++){
IOnNewBookArrivedListener listener = mListenerList.get(i);
listener.onNewBookArrived(book);
}
}
private class ServiceWorker implements Runnable{
@Override
public void run() {
while (!mIsServiceDestroyed.get()){
try {
Thread.sleep(3000);
}catch (Exception e){
e.printStackTrace();
}
int bookId = mBookList.size()+1;
Book newBook = new Book(bookId,"new book#"+bookId);
try {
onNewBookArrived(newBook);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
}
- 客戶端代碼修改如下:
package com.example.administrator.aidltest;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;
private IBookManager mRemoteBookManager;
private Button mBookAdd;
private Button mBookGet;
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MESSAGE_NEW_BOOK_ARRIVED:
Log.e(TAG,"receive new book : "+msg.obj);
break;
default:
break;
}
}
};
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
bookManager = IBookManager.Stub.asInterface(service);
try {
mRemoteBookManager = bookManager;
bookManager.registerListener(mListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mRemoteBookManager = null;
}
};
private IOnNewBookArrivedListener mListener = new IOnNewBookArrivedListener.Stub(){
@Override
public void onNewBookArrived(Book newBook) throws RemoteException {
mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED,newBook).sendToTarget();
}
};
private IBookManager bookManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initListener();
serviceBind();
}
private void initView(){
mBookAdd = (Button) findViewById(R.id.mBookAdd);
mBookGet = (Button) findViewById(R.id.mBookGet);
}
private void serviceBind(){
Intent intent = new Intent(MainActivity.this,BookManagerService.class);
bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
}
private void initListener(){
mBookAdd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (bookManager != null){
try {
bookManager.addBook(new Book(001,"第一行代碼"));
bookManager.addBook(new Book(002,"Android藝術(shù)開發(fā)探索"));
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
mBookGet.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (bookManager != null){
try {
List<Book> mBookList = bookManager.getBookList();
for (int i = 0; i<mBookList.size(); i++){
Log.e(TAG,"query book list: "+mBookList.get(i).bookName+"\n");
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
}
@Override
protected void onDestroy() {
if (mRemoteBookManager != null && mRemoteBookManager.asBinder().isBinderAlive()){
try {
mRemoteBookManager.unregisterListener(mListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(mConnection);
super.onDestroy();
}
}
運行結(jié)果如下:
08-22 00:24:59.829 12598-12598/com.example.administrator.aidltest E/MainActivity: receive new book : com.example.administrator.aidltest.Book@3010b98b
08-22 00:25:02.829 12598-12598/com.example.administrator.aidltest E/MainActivity: receive new book : com.example.administrator.aidltest.Book@1c298a68
08-22 00:25:05.829 12598-12598/com.example.administrator.aidltest E/MainActivity: receive new book : com.example.administrator.aidltest.Book@1a4bce81
我們接著分析翻屈,在MainActivity銷毀時陈哑,我們會解除已經(jīng)注冊到服務(wù)端的listener,結(jié)果服務(wù)端無法找到我們之前注冊的那個listener伸眶,解注冊宣布失敗芥颈。原因是:對象是不能跨進(jìn)程直接傳輸?shù)模瑢ο蟮目邕M(jìn)程傳輸本質(zhì)上都是反序列化的過程赚抡,Binder會把客戶端傳遞過來的對象重新轉(zhuǎn)化并生成一個新的對象爬坑。解決方式是采用RemoteCallbackList,代碼修改如下:
BookManagerService:
package com.example.administrator.aidltest;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
public class BookManagerService extends Service{
private static final String TAG = "BMS";
private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList =
new RemoteCallbackList<IOnNewBookArrivedListener>();
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 registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
mListenerList.register(listener);
}
@Override
public void unregisterListener(IOnNewBookArrivedListener listener) throws RemoteException {
mListenerList.unregister(listener);
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public void onCreate() {
super.onCreate();
new Thread(new ServiceWorker()).start();
}
@Override
public void onDestroy() {
mIsServiceDestroyed.set(true);
super.onDestroy();
}
private void onNewBookArrived(Book book) throws RemoteException {
mBookList.add(book);
final int N = mListenerList.beginBroadcast();
for (int i = 0; i < N; i++){
IOnNewBookArrivedListener l = mListenerList.getBroadcastItem(i);
if (l != null){
l.onNewBookArrived(book);
}
}
mListenerList.finishBroadcast();
}
private class ServiceWorker implements Runnable{
@Override
public void run() {
while (!mIsServiceDestroyed.get()){
try {
Thread.sleep(3000);
}catch (Exception e){
e.printStackTrace();
}
int bookId = mBookList.size()+1;
Book newBook = new Book(bookId,"new book#"+bookId);
try {
onNewBookArrived(newBook);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
}
這樣涂臣,跨進(jìn)程解注冊問題就解決了盾计,到這里AIDL的基本使用方法就介紹完畢了。