最近在复习主席的《Android开发艺术探索》这本书,将第二章IPC机制相关的部分简单总结下。
开启多进程的方式:给四大组件在AndroidMenifest文件中指定 android:process属性。
Android中的IPC方式主要有六种,分别为:
- 使用Bundle
- 使用文件共享
- 使用Messenger
- 使用AIDL
- 使用ContentProvider
- 使用Socket
下面我们主要看下 3. 使用Messenger 和 4.使用AIDL 这两种跨进程通信的方式。其实Messenger的底层实现也是AIDL。
使用Messenger:
实现一个Messenger可以分为两个步骤,分别为服务端和客户端。
服务端进程:首先,我们需要在服务端创建一个Service来处理客户端的连接请求,同时创建一个Handler,并通过它来创建一个Messenger对象,然后在Service 的 onBind 方法中返回这个Messenger对象底层的Binder即可。
客户端进程:客户端进程中,首先要绑定服务端的Service,绑定成功后用服务端返回的IBinder对象创建一个Messenger,通过这个Messenger就可以向服务端发送消息了,消息类型为Message对象。
下面我们新建一个Demo来测试下,我们在客户端进程中向服务端发送一句话,在服务端进程中打印出这句话:
1.新建项目MessengerTest,创建MessengerService,让其运行在单独的进程中。
<service
android:name=".MessengerService"
android:process=":remote"/>
2.服务端进程代码:
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.客户端进程代码:
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();
}
}
测试结果如下:
08-19 23:13:42.646 27432-27432/? E/MessengerService: receive msg from client: hello world
上述我们只是发送消息到服务端进程,服务端进程对msg进行相应的操作。也就是说我们只是实现了客户端到服务端的单向通信,如果我们需要服务端处理完毕后回复客户端进程的话,要怎么操作呢?修改后的代码如下:
服务端进程:
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();
}
}
客户端进程:
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();
}
}
测试结果如下:
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: 你好,世界
有关Messenger的操作方式到这里就介绍完毕了,下面我们看下AIDL方式。使用AIDL来进行进程间通信,同样分为服务端和客户端两个方面。
服务端
服务端首先要创建一个Service用来监听客户端的连接请求,然后创建一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明,最后在Service中实现这个AIDL接口即可。客户端
客户端所要做的事情就稍微简单一些,首先需要绑定服务端的Service,绑定成功后,将服务端返回的Binder对象转成AIDL接口所属的类型,接着就可以调用AIDL中的方法了。
下面我们看下代码:
- 新建项目AIDLTest,创建BookManagerService,让其运行在单独的进程中。
<service
android:name=".BookManagerService"
android:process=":remote"/>
-
AIDL文件创建,为了方便AIDL的开发,将所有和AIDL相关的类和文件全部放入同一个包中,如下所示:
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);
}
- 服务端进程代码:
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;
}
}
- 客户端进程代码:
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艺术开发探索"));
} 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节点下
main{
java.srcDirs = ['src/main/java','src/main/aidl']
}
}
测试结果如下:
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艺术开发探索
现在我们考虑下,如果用户不想时不时地去查询图书列表了,太累了,他去问图书馆,“当有新书的时候,能不能把新书的信息告诉我呢?”。很显然这是一种观察者模式,下面我们来实现下:
- 在上述代码的基础上,新增 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);
}
- 服务端代码修改如下:
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艺术开发探索"));
} 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();
}
}
运行结果如下:
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销毁时,我们会解除已经注册到服务端的listener,结果服务端无法找到我们之前注册的那个listener,解注册宣布失败。原因是:对象是不能跨进程直接传输的,对象的跨进程传输本质上都是反序列化的过程,Binder会把客户端传递过来的对象重新转化并生成一个新的对象。解决方式是采用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();
}
}
}
}
}
这样,跨进程解注册问题就解决了,到这里AIDL的基本使用方法就介绍完毕了。