Android中的多进程通信方式
AIDL:功能强大,支持进程间一对多的实时并发通信,并可实现 RPC (远程过程调用)。
Messenger:支持一对多的串行实时通信, AIDL 的简化版本。
Bundle:四大组件的进程通信方式,只能传输 Bundle 支持的数据类型。
ContentProvider:强大的数据源访问支持,主要支持 CRUD 操作,一对多的进程间数据共享,例如我们的应用访问系统的通讯录数据。
BroadcastReceiver:即广播,但只能单向通信,接收者只能被动的接收消息。
文件共享:在非高并发情况下共享简单的数据。
Socket:通过网络传输数据。
IPC适合的场景及优缺点
-
Intent
Activity,Service,Receiver 都支持在 Intent 中传递 Bundle 数据,而 Bundle实现了 Parcelable 接口,可以在不同的进程间进行传输。
在一个进程中启动了另一个进程的 Activity,Service 和 Receiver ,可以在Bundle 中附加要传递的数据通过 Intent 发送出去。
Intent intent = new Intent(MainActivity.this, LoginActivity.class);
Bundle bundle = new Bundle();
bundle.putString("msg", "This is a message");
intent.putExtras(bundle);
startActivity(intent);
-
文件共享
Windows 上,一个文件如果被加了排斥锁会导致其他线程无法对其进行访问,包括读和写;而 Android 系统基于 Linux ,使得其并发读取文件没有限制地进行,甚至允许两个线程同时对一个文件进行读写操作,尽管这样可能会出问题。
可以在一个进程中序列化一个对象到文件系统中,在另一个进程中反序列化恢复这个对象(注意:并不是同一个对象,只是内容相同)
SharedPreferences 是个特例,系统对它的读 / 写有一定的缓存策略,即内存中会有一份ShardPreferences 文件的缓存,系统对他的读 / 写就变得不可靠,当面对高并发的读写访问,SharedPreferences 有很多大的几率丢失数据。因此,IPC 不建议采用 SharedPreferences
读写共享文件:
public class FileShare {
public static final String SHARE_FILE = "/sharefile";
/**
* 将对象写入文件
*/
public void writeObject() {
NoteBook noteBook = new NoteBook();
noteBook.setName("cr");
noteBook.setContent("Empty");
File file = new File(Environment.getExternalStorageDirectory() + SHARE_FILE);
ObjectOutputStream outputStream = null;
try{
outputStream = new ObjectOutputStream(new FileOutputStream(file));
outputStream.writeObject(noteBook);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 从文件读取对象
*/
public void readObject() {
File file = new File(Environment.getExternalStorageDirectory() + SHARE_FILE);
ObjectInputStream objectInputStream = null;
try {
objectInputStream = new ObjectInputStream(new FileInputStream(file));
NoteBook noteBook = (NoteBook) objectInputStream.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
} finally {
try {
objectInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
class NoteBook implements Serializable {
private String mName;
private String mContent;
public void setName(String name) {
mName = name;
}
public void setContent(String content) {
mContent = content;
}
}
-
Messenger
Messenger是一种轻量级的 IPC 方案,它的底层实现是 AIDL ,可以在不同进程中传递 Message 对象,它一次只处理一个请求,在服务端不需要考虑线程同步的问题,服务端不存在并发执行的情形。
构建一个运行在独立进程中的服务端Service,创建Messenger,将Messenger的binder返回给Service的onBind方法。然后接收客户端Messege需要用Handler接收。
public class MessengerService extends Service {
/**
* 处理客户端消息,并用于构建Messenger
*/
private static class MessengerHandler extends Handler {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case 100:
System.out.println(msg.getData().getString("data"));
sendToClient(msg);
break;
}
}
}
/**
* 给客户度发送消息
*/
private static void sendToClient(Message message) {
Messenger client = message.replyTo; //通过 message拿到客户传递过来的 Messenger,通过该对象发送message
//当然,回传消息还是要通过message
Message msg = Message.obtain(null, 100);
Bundle bundle = new Bundle();
bundle.putString("data", "客户端, 我收到你的消息了");
msg.setData(bundle);
try {
client.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
/**
* 构建Messenger对象
*/
private final Messenger mMessenger = new Messenger(new MessengerHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
//将Messenger对象的Binder返回给客户端
return mMessenger.getBinder();
}
}
注册service,当然要设置在独立的进程
<service android:name=".messenger.MessengerService"
android:process=":messengerprocess"/>
然后客户端是通过绑定服务端返回的binder来创建Messenger对象,并通过这个Messenger对象来向服务端发送Message消息
class MainActivity : ComponentActivity() {
private var mService: Messenger?= null
private lateinit var btnMsg: Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnMsg = findViewById(R.id.btn_msg)
//绑定service
bindService(Intent(this, MessengerService::class.java),
connection, Context.BIND_AUTO_CREATE)
btnMsg.setOnClickListener {
mService?.let {
//创建消息,通过Bundle传递数据
var message = Message.obtain(null, 100)
message.data = Bundle().apply {
putString("data", "服务端,我给你发送消息了")
}
//将客户端的Messenger对象传递给服务端,才能让双方互相通信
message.replyTo = mClientMessenger
//像服务端进程发送消息
it.send(message)
}
}
}
/**
* 客户端Messenger对象
*/
private val mClientMessenger: Messenger = Messenger(MessengerHandler())
/**
* 用于构建客户端的Messenger对象,并处理服务端的消息
*/
private class MessengerHandler : Handler() {
override fun handleMessage(msg: Message) {
when (msg.what) {
100 -> {
println(msg.data.getString("data"))
}
}
}
}
private val connection: ServiceConnection = object: ServiceConnection {
override fun onServiceConnected(name: ComponentName?, ibinder: IBinder?) {
mService = Messenger(ibinder)
}
override fun onServiceDisconnected(name: ComponentName?) {
}
}
override fun onDestroy() {
//解绑service
unbindService(connection)
super.onDestroy()
}
}
打印结果:
Messenger作为服务端、客户端两个进程通信的桥梁,底层是运用的Binder通信机制,媒介是Message,用messenger.send方法发送消息,上层通过handler接收消息。
总结:
- 使用Messager来传递Message,Message中能使用的字段只有what、arg1、arg2、Bundle和replyTo,自定义的Parcelable对象无法通过object字段来传输
- Message中的Bundle支持多种数据类型,replyTo字段用于传输Messager对象,以便进程间相互通信
- Messager以串行的方式处理客户端发来的消息,不适合有大量并发的请求
- Messager方法只能传递消息,不能跨进程调用方法
Messenger参考:
https://juejin.cn/post/6844903665795334158
-
AIDL
AIDL是Android中IPC(Inter-Process Communication)方式中的一种,AIDL是Android Interface definition language的缩写,AIDL的作用是让你可以在自己的APP里绑定一个其他APP的service,这样你的APP可以和其他APP交互。
AIDL底层也是通过Binder实现的。
Messenger 是以串行的方式处理客户端发来的消息,如果大量消息同时发送到服务端,服务端只能一个一个处理,所以大量并发请求就不适合用 Messenger ,而且Messenger 只适合传递消息,不能跨进程调用服务端的方法。AIDL 可以解决并发和跨进程调用方法的问题,要知道 Messenger 本质上也是 AIDL ,只不过系统做了封装方便上层的调用而已。
详情见另一篇文章:AIDL简单例子
-
ContentProvider
ContentProvider 的底层数据,可以是 SQLite 数据库,可以是文件,也可以是内存中的数据。
-
Socket
Socket也称作“套接字“,是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用以实现进程在网络中通信。
分为流式套接字和数据包套接字,分别对应网络传输控制层的TCP和UDP协议。
参考:
https://cloud.tencent.com/developer/article/2158294
//www.greatytc.com/p/84a12977dc26