一、使用Intent
- Activity、Service,Receiver都支持在Intent中传递Bundle数据,而Bundle实现了Parcelable接口,可以在不同的进程间进行传输。
- 在一个进程中启动了另外一个进程的Activity、Service或Receiver,可以在Bundle中重复加要传递的数据通过Intent发送出去。
二、使用文件共享
- 可以通过序列化来实现文件共享,即一个进程序列化一个对象到文件系统,另外一个进程反序列化恢复该对象(但这两个对象不一样,只是内容一样);
- SharedPreferences是个特例,系统对他的读/写有一定的缓存策略,即内存会有一份SharedPreferences文件的缓存,系统对他的读/写就变得不靠谱,在高并发的读写访问中,SharedPreferences有很大的几率丢失数据,因而IPC不建议采用SharedPreferences。
三、使用Messenger
Messenger是一种轻量级的IPC方案,它底层实现是AIDL,可以在不同进程中传递Message对象,它一次只处理一个请求,在服务端不需要考虑线程同步的问题,服务端不存在并发执行的情况。
- 服务端进程:服务端创建一个Service来处理客户端请求,同时通过一个Handler对象来实例化一个Messenger对象,然后在Service的onBind中返回这个Messenger对象底层的Binder即可。
public class MessengerService extends Service {
private static final String TAG = MessengerService.class.getSimpleName();
private class MessengerHandler extends Handler {
/**
* @param msg
*/
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case Constants.MSG_FROM_CLIENT:
Log.d(TAG, "receive msg from client: msg = [" +
msg.getData().getString(Constants.MSG_KEY) + "]");
Toast.makeText(MessengerService.this, "receive msg from client: msg = [" +
msg.getData().getString(Constants.MSG_KEY) + "]",
Toast.LENGTH_SHORT).show();
Messenger client = msg.replyTo;
Message replyMsg = Message.obtain(null, Constants.MSG_FROM_SERVICE);
Bundle bundle = new Bundle();
bundle.putString(Constants.MSG_KEY, "我已经收到你的消息,稍后回复你!");
replyMsg.setData(bundle);
try {
client.send(replyMsg);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
private Messenger mMessenger = new Messenger(new MessengerHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}- 客户端进程:首先绑定服务器Service,绑定成功后用服务器的IBinder对象创建一个Messenger,通过这个Messenger可以向服务器发送消息,消息类型是Message。如果需要服务器响应,则需要创建一个Handler并通过它来创建一个Messenger(和服务器一样),并通过Message的replyTo参数传递给服务器。服务器通过Message的replyTo参数可以回应客户端。
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private Messenger mGetReplyMessenger = new Messenger(new MessageHandler());
private Messenger mService;
private class MessageHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case Constants.MSG_FROM_SERVICE:
Log.d(TAG, "received msg form service: msg = [" +
msg.getData().getString(Constants.MSG_KEY) + "]");
Toast.makeText(MainActivity.this, "received msg form service: msg = [" +
msg.getData().getString(Constants.MSG_KEY) + "]",
Toast.LENGTH_SHORT).show();
break;
default:
super.handleMessage(msg);
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void bindService(View v) {
Intent mIntent = new Intent(this, MessengerService.class);
bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
public void sendMessage(View v) {
Message msg = Message.obtain(null,Constants.MSG_FROM_CLIENT);
Bundle data = new Bundle();
data.putString(Constants.MSG_KEY, "Hello! This is client.");
msg.setData(data);
msg.replyTo = mGetReplyMessenger;
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
protected void onDestroy() {
unbindService(mServiceConnection);
super.onDestroy();
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
/**
* @param name
* @param service
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = new Messenger(service);
Message msg = Message.obtain(null,Constants.MSG_FROM_CLIENT);
Bundle data = new Bundle();
data.putString(Constants.MSG_KEY, "Hello! This is client.");
msg.setData(data);
//
msg.replyTo = mGetReplyMessenger;
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
/**
* @param name
*/
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
}注意:
客户端和服务端是通过拿到对方的Messenger来发送Message的。只不过客户端通过bindService,onServiceConnected,而服务端通过message.replayTo来获取对方的Messenger。Messenger中有一个Handler一串行的方式处理队列的消息,不存在并发执行,因而我们不用考虑线程同步的问题。
四、使用AIDL
Messenger是以串行的方式处理客户端发来的消息,如果大量消息同时发送到服务端,服务端不可能一个一个进行处理,因为效率低,所以大量并发请求不适合用Messenger,而且Messenger只适合传递消息,不能跨进程调用服务端的方法。AIDL可以解决并发和跨进程调用方法的问题。要知道Messenger本质上也是AIDL,只不过系统做了封装让上层的调用。
AIDL文件支持的数据类型:
- 基本数据类型
- String和CharSequence
- ArrayList,里面的元素必须能够被AIDL支持
- HashMap,里面的元素必须能够被AIDL支持
- Parcelable,实现Parcelable接口的对象。注意:如果AIDL文件中用到了自定义的Parcelable对象,必须创建一个和它同名的AIDL文件。
- AIDL,AIDL接口本身也可以在AIDL文件中使用。
服务端
服务端创建一个Service用来监听客户端的连接请求,然后创建一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明,最后在Service中实现这个AIDL接口即可。
客户端
绑定服务端的Service,绑定成功后,将服务器返回的Binder对象转成AIDL接口所属的类型,然后就可以调用AIDL中的方法。客户端调用远程服务的方法,被调用的方法运行在服务端的Binder线程池中,同时客户端的线程会被挂起,如果服务端方法执行比较好使,就会导致客户端线程长时间阻塞,导致ANR,客户端的onServiceConntected和onServiceDisconnected方法都在UI线程中。
五、使用ContentProvider
用于在不同应用间数据共享,和Messenger底层实现同样是Binder和AIDL,系统做了封装,使用功能简单。
系统预置了许多ContentProvider,如通讯录、日程表,需要跨进程访问。使用方法:继承ContentProvider类实现6个抽象方法,这六个方法均运行在ContentProvider进程中,除onCreate运行在主进程中,其余五个方法均由外界回调运行在Binder线程池中。
ContentProvider的底层数据,可以是SQLite数据库,也可以使文件,也可以是内存中的数据。
六、使用Socket
网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用:Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。
Socket本身可以传输任意字节流。