目录
- Bundle
- 文件共享
- Messenger
- AIDL
- ContentProvider
- Socket
Android中的IPC方式
由于篇幅有限,这篇文章只重点介绍Messenger和AIDL,其他通信方式可自行查阅资料。
Bundle
Activity、Service、Receiver都支持在Intent中传递Bundle数据。这是一种最简单的进程间通信方式。
当然,我们传输的数据必须能够序列化,比如基本数据类型,实现了Parcelable接口的对象,实现了Serializable接口的对象以及一些Android支持的特殊对象。
文件共享
文件共享是一种不错的进程间通信的方式,两个进程通过读/写同一个文件来交换数据,比如A进程把数据写入文件,B进程通过读取这个文件来获取数据。
要尽量避免并发写这种情况的发生或者考虑使用线程同步来限制多个线程的写操作。
文件共享方式适合在对数据同步要求不高的进程之间进行通信,并且要妥善处理并发读/写的问题。
从本质上来说,SharedPreferences也属于文件的一种。在多进程模式下,系统对它的读/写就变得不可靠,当面对高并发的读/写访问Sharedpreferences有很大几率会丢失数据,因此,不建议在进程间通信中使用SharedPreferences。
Messenger
Messenger是一种轻量级的IPC方案,它的底层实现是AIDL。
服务端进程
首先,我们需要在服务端创建一个Service来处理客户端的连接请求,同时创建一个Handler并通过它来创建一个Messenger对象,然后在Service的onBind中返回这个Messenger对象底层的Binder即可。
/**
* 在服务端创建一个Service来处理客户端的连接请求
*/
public class MessengerService extends Service {
private static final String TAG = "MessengerService";
public static final int MSG_FROM_CLIENT = 101;
public static final int MSG_FROM_SERVICE = 102;
//创建一个Handler
private static class MessengerHandler extends Handler{
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MSG_FROM_CLIENT:
Log.i(TAG, "receive msg from Client: "+ msg.getData().getString("msg"));
//拿到客户端的Messenger,给客户端发送反馈信息
Messenger client = msg.replyTo;
Message replyMessage = Message.obtain(null, MSG_FROM_SERVICE);
Bundle data = new Bundle();
data.putString("reply", "嗯,你的消息我已经收到,稍后回复你。");
replyMessage.setData(data);
try {
client.send(replyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
//通过Handler来创建一个Messenger对象
private final Messenger messenger = new Messenger(new MessengerHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
//在Service的onBind中返回这个Messenger对象底层的Binder
return messenger.getBinder();
}
}
注册这个Service,让其在独立进程中运行。
<service android:name=".MessengerService"
android:process=":remote"/>
客户端进程
客户端进程中,首先要绑定服务端的Service,绑定成功后用服务端返回的IBinder对象创建一个Messenger,通过这个Messenger就可以向服务端发送消息了,发消息类型为Message对象。如果需要服务端能够回应客户端,就和服务端一样,我们还需要创建一个Handdler并创建一个新的Messenger,并把这个Messenger对象通过Message的replyTo参数传递给服务端,服务端通过这个replyTo参数就可以回应客户端。
public class MessengerActivity extends AppCompatActivity {
private static final String TAG = "MessengerActivity";
private Messenger mService;
//客户端的Messenger,传给服务端,用来接收服务端发送的反馈信息
private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MessengerService.MSG_FROM_SERVICE:
Log.i(TAG, "receive msg from Client: "+ msg.getData().getString("reply"));
break;
default:
super.handleMessage(msg);
}
}
}
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//通过服务端Messenger对象底层的Binder对象创建一个Messenger,用来给服务端发送消息
mService = new Messenger(service);
Message msg = Message.obtain(null, MessengerService.MSG_FROM_CLIENT);
Bundle data = new Bundle();
data.putString("msg", "hello, this is client.");
msg.setData(data);
//客户端的Messenger需要通过msg.replyTo传给服务端,用于接收服务端发送的反馈信息
msg.replyTo = mGetReplyMessenger;
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, MessengerService.class);
bindService(intent, connection, BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(connection);
super.onDestroy();
}
}
Messenger的工作原理
AIDL
AIDL是Messenger的底层实现,因此Messenger本质上也是AIDL,只不过系统为我们做了封装,从而方便上层的调用而已。
服务端
服务端首先要创建一个Service用来监听客户端的连接请求,然后创建一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明,最后在Service中实现这个AIDL接口即可。
创建一个AIDL文件
package com.tin.ipcdemo;
import com.tin.ipcdemo.Book;
import com.tin.ipcdemo.IOnNewBookArrivedListener;
interface IBookManager {
//两个业务接口暴露给客户端调用
List<Book> getBookList();
void addBook(in Book book);
//注册和反注册回调接口
void registerListener(IOnNewBookArrivedListener listener);
void unregisterListener(IOnNewBookArrivedListener listener);
}
在Service中实现AIDL接口
public class BookManagerService extends Service {
private static final String TAG = "BMS";
private AtomicBoolean mIsServiceDestoryed = new AtomicBoolean(false);
//CopyOnWriteArrayList支持并发读/写
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<>();
//实现IBookManager接口
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);
}
};
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(1, "Android"));
mBookList.add(new Book(2, "IOS"));
new Thread(new ServiceWorker()) .start();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
//权限控制
int check = checkCallingOrSelfPermission("com.tin.ipcdemo.permission.ACCESS_BOOK_SERVICE");
if(check == PackageManager.PERMISSION_DENIED){
return null;
}
return mBinder;
}
@Override
public void onDestroy() {
mIsServiceDestoryed.set(true);
super.onDestroy();
}
private class ServiceWorker implements Runnable {
@Override
public void run() {
while (!mIsServiceDestoryed.get()){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
int bookId = mBookList.size() + 1;
Book newBook = new Book(bookId, "new book#" + bookId);
try {
onNewBookArrived(newBook);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
}
private void onNewBookArrived(Book newBook) throws RemoteException {
//当数据变化时,通过回调接口通知客户端
mBookList.add(newBook);
final int N = mListenerList.beginBroadcast();
for(int i = 0; i < N; i++){
IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
if(listener != null){
listener.onNewBookArrived(newBook);
}
}
mListenerList.finishBroadcast();
}
}
客户端
客户端所要做事情就稍微简单一些,首先需要绑定服务端的Service,绑定成功后,将服务端返回的Binder对象转成AIDL接口所属的类型,接着就可以调用AIDL中的方法了。
public class BookManagerActivity extends Activity {
private static final String TAG = "BookManagerActivity";
private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;
private IBookManager mRemoteBookManager;
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case MESSAGE_NEW_BOOK_ARRIVED:
Log.i(TAG, "receive new book: " + msg.obj);
break;
default:
super.handleMessage(msg);
}
}
};
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//拿到服务端的接口对象
IBookManager bookManager = IBookManager.Stub.asInterface(service);
try {
mRemoteBookManager = bookManager;
List<Book> list = bookManager.getBookList();
Log.i(TAG, "query book list: " + list.toString());
Book newBook = new Book(3, "Android开发艺术探索");
bookManager.addBook(newBook);
List<Book> newList = bookManager.getBookList();
Log.i(TAG, "query book list: " + newList.toString());
//注册回调
bookManager.registerListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mRemoteBookManager = null;
}
};
private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
@Override
public void onNewBookArrived(Book newBook) throws RemoteException {
//由于回调方法执行在Binder线程中,所以使用handler切换到主线程中执行
handler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, newBook).sendToTarget();
}
};
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, mConnection, BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
if(mRemoteBookManager != null && mRemoteBookManager.asBinder().isBinderAlive()){
try {
//反注册回调
mRemoteBookManager.unregisterListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(mConnection);
super.onDestroy();
}
}
ContentProvider
ContentProvider是Android中提供的专门用来不同应用之间数据共享的方式,从这一点来看,他天生就是适合进程间通信,和Messenger一样,ContentProvider的底层实现同样也是Binder。
ContentProvider主要以表格的形式来组织数据,并且可以包含多个表,对于每个表格来说,它们都具有行和列的层次性,行往往对应一条记录,而列对应一条记录中的一个字段,这点和数据库很类似。除了表格的形式,ContentProvider还支持文件数据,比如图片、视频等。文件数据和表格数据的结构不同,因此处理这类数据时可以在ContentProvider中返回文件的句柄给外界从而让文件来访问Contentprovider中的文件信息。
Socket
Socket来实现进程通信,Socket也叫做套接字,是网络通信中的概念,他分为流式套接字和用户数据报套接字两种,分别是应用于网络的传输控制层中的Tcp和UDP协议。
TCP面向的连接协议,提供稳定的双向通讯功能,TCP连接的建立需要经过“三次握手”才能完成,为了提供稳定的数据传输功能,其本身提供了超时重传机制,因此具有很高的稳定性:
而UDP是无连接的,提供不稳定的单向通信功能,当然UDP也可以实现双向通信功能。在性能上,UDP具有更好的效率,其缺点是不保证数据一定能够正确传输,尤其是在网络拥塞的情况下。
参考资料:
《Android开发艺术探索》
网友评论