Android中的IPC方式
IPC是Inter-ProcessCommunication的缩写,进程间通信、跨进程通信,是指两个进程之间进行数据交换的过程。说起进程间通信,我们首先脑补下什么是进程,什么是线程,进程和线程是截然不同的概念。按照操作系统中的描述,线程是CPU调度的最小单元,同时线程是一种有限的系统资源。而进程一般指一个执行单元,在PC和移动设备上指一个程序或者一个应用。一个进程可以包含多个线程,因此进程和线程是包含与被包含的关系。最简单的情况下,一个进程中可以只有一个线程,即主线程,在Android里面主线程也叫UI线程,在UI线程里才能操作界面元素。通常情况下一个进程中需要执行大量耗时的任务,如果这些任务放在主线程中去执行就会造成界面长时间无法响应,严重影响用户体验,这种情况在PC系统和移动系统中都存在,在Android中有一个特殊的名字叫做ANR(ApplicationNotResponding),即应用无响应。解决这个问题就需要用到线程,把一些耗时的任务放在线程中即可。
对于Android来说,它是一种基于Linux内核的移动操作系统,它的进程间通信方式并不能完全继承自Linux,它有自己的进程间通信方式。在Android中最有特色的进程间通信方式就是Binder了,通过Binder可以轻松地实现进程间通信。除了Binder,Android还支持Socket,通过Socket也可以实现任意两个终端之间的通信,当然同一个设备上的两个进程通过Socket通信自然也是可以的。
具体IPC方式有很多,比如可以通过在Intent中附加extras来传递信息,或者通过共享文件的方式来共享数据,还可以采用Binder方式来跨进程通信,另外,ContentProvider天生就是支持跨进程访问的,因此我们也可以采用它来进行IPC。此外,通过网络通信也是可以实现数据传递的,所以Socket也可以实现IPC。下面就具体介绍几种主要的IPC方式。
1.使用Bundle
四大组件中的三大组件(Activity、Service、BroadcastReceiver)都是支持在Intent中传递Bundle数据的,由于Bundle实现了Parcelable接口,所以它可以方便地在不同的进程间传输。这里传输的数据必须可以被序列化,比如基本数据类型和实现了Parcelable、Serializable接口的类都可以传输。
2.使用文件共享
这个看起来比较好理解,就是在SD卡中存储个文件用来共享,但是这个明显的有并发问题。众所周知,SharedPreferences是Android提供的一个轻量级的存储方案,在xml中以键值对的形式存储。同样的,在多进程的情况下,读写并不是可靠的。
3.使用Messenger
Messenger很多地方翻译为信使,这个也算贴切。Messenger是一中轻量级的IPC方式,底层实现也是基于AIDL。
Messenger的使用方法很简单,它对AIDL做了封装,使得我们可以更简便地进行进程间通信。同时,由于它一次处理一个请求,因此在服务端我们不用考虑线程同步的问题,这是因为服务端中不存在并发执行的情形。实现一个Messenger有如下几个步骤,分为服务端和客户端。
A.服务端进程首先,我们需要在服务端创建一个Service来处理客户端的连接请求,同时创建一个Handler并通过它来创建一个Messenger对象,然后在Service的onBind中返回这个Messenger对象底层的Binder即可。
B.客户端进程客户端进程中,首先要绑定服务端的Service,绑定成功后用服务端返回的IBinder对象创建一个Messenger,通过这个Messenger就可以向服务端发送消息了,发消息类型为Message对象。如果需要服务端能够回应客户端,就和服务端一样,我们还需要创建一个Handler并创建一个新的Messenger,并把这个Messenger对象通过Message的replyTo参数传递给服务端,服务端通过这个replyTo参数就可以回应客户端。这听起来可能还是有点抽象,不过看了下面的两个例子,读者肯定就都明白了。首先,我们来看一个简单点的例子,在这个例子中服务端无法回应客户端。
/**
* Created by Stone on 2018/8/16.
*/
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 1:
Log.i(TAG, "receive msg from Client:" + msg.getData().getString("msg"));
break;
default:
super.handleMessage(msg);
}
}
}
private final Messenger mMessenger = new Messenger(new MessengerHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
public class MessengerActivity extends AppCompatActivity {
private static final String TAG = " MessengerActivity";
private TextView tvResult;
private Messenger mService;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
Message msg = Message.obtain(null, 1);
Bundle data = new Bundle();
data.putString("msg", " hello, this is client.");
msg.setData(data);
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void onServiceDisconnected(ComponentName className) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenger);
tvResult = findViewById(R.id.tv_result);
Intent intent = new Intent(this, MessengerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}
}
问题来了,实际使用中不可能只有客户端访问服务端,还有服务端响应客户端,那我们怎么改进下呢?直接上代码:
/**
* Created by Stone on 2018/8/16.
*/
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 1:
Log.i(TAG, "receive msg from Client:" + msg.getData().getString("msg"));
Messenger client = msg.replyTo;
Message relpyMessage = Message.obtain(null, 2);
Bundle bundle = new Bundle();
bundle.putString(" reply", "消息已收到");
relpyMessage.setData(bundle);
try {
client.send(relpyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
private final Messenger mMessenger = new Messenger(new MessengerHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
public class MessengerActivity extends AppCompatActivity {
private static final String TAG = " MessengerActivity";
private TextView tvResult;
private Messenger mService;
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
Message msg = Message.obtain(null, 1);
Bundle data = new Bundle();
data.putString("msg", " hello, this is client.");
msg.setData(data);
//添加处理回复的Messenger
msg.replyTo = mGetReplyMessenger;
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
public void onServiceDisconnected(ComponentName className) {
}
};
private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());
private class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 2:
Log.i(TAG, " receive msg from Service:" + msg.getData().getString(" reply"));
tvResult.append("\n receive msg from Service:" + msg.getData().getString(" reply"));
break;
default:
super.handleMessage(msg);
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenger);
tvResult = findViewById(R.id.tv_result);
Intent intent = new Intent(this, MessengerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}
}
从上面代码可以看到,在收到客户端的消息后,服务端把返回的消息放到了msg.replyTo中,这也是一个Messenger,具体接受的Messenger在MessengerActivity中定义,当然也是一组完整的Messenger跟Handler,具体可以看代码。这里IPC的类型必须是Message支持的类型。最后给出Messenger的工作原理:
Messenger原理4.使用AIDL
简单的使用可参考Android Binder---AIDL ,这里接着Android Binder---AIDL介绍的AIDL继续往下写。考虑到用户每次都是访问getStudent方法比较费劲,如果StudentList有更新再通知用户去获取岂不是很省力?这里就要用到观察者模式了。我们创建一个新的AIDL接口,这个用来监听StudentList的变化。
// IOnNewStudentArrivedListener.aidl
package com.stone.demoandroid.entity;
import com.stone.demoandroid.entity.Student;
// Declare any non-default types here with import statements
interface IOnNewStudentArrivedListener {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void onNewStudentArrived(in Student newStudent);
}
同样的,之前的Manager也要有所变化添加两个方法:registerListener、unregisterListener
// IStudentManager.aidl
package com.stone.demoandroid.entity;
// Declare any non-default types here with import statements
import com.stone.demoandroid.entity.Student;
import com.stone.demoandroid.entity.IOnNewStudentArrivedListener;
interface IStudentManager {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
List<Student> getStudentList();
void addStudent(in Student student);
void registerListener( IOnNewStudentArrivedListener listener);
void unregisterListener( IOnNewStudentArrivedListener listener);
}
Service跟Activity的代码太长了就不全部贴出来了,有兴趣的同学可以直接去Demo下载查看。这里有一点需要提一下,CopyOnWriteArrayList不能处理跨进程的Listener,需要用RemoteCallbackList,因为CopyOnWriteArrayList在服务端使用的Listener并不是客户端定义的那个,看名字也知道只是一个Copy。还有就是并没有处理ANR和Binder意外死亡的问题,有兴趣额同学可以处理下。
5.使用ContentProvider
ContentProvider是Android中提供的专门用于不同应用间进行数据共享的方式,从这一点来看,它天生就适合进程间通信。和Messenger一样,ContentProvider的底层实现同样也是Binder,由此可见,Binder在Android系统中是何等的重要。虽然ContentProvider的底层实现是Binder,但是它的使用过程要比AIDL简单许多,这是因为系统已经为我们做了封装,使得我们无须关心底层细节即可轻松实现IPC。具体的实现这里就不赘述了,四大组件之一,用法一搜一堆,有兴趣的可以看下Demo
6.使用Socket
Socket也称为“套接字”,是网络通信中的概念,它分为流式套接字和用户数据报套接字两种,分别对应于网络的传输控制层中的TCP和UDP协议。本科的时候选修过JavaSocket,作业有实现TCP和UDP协议,考试竟然是上机,自己实现一个局域网的聊天工具。
具体的实现,大家可以看下Demo
选择合适的IPC(任玉刚. Android开发艺术探索. 电子工业出版社. ),这里借大佬总结的帖在这里希望对大家有帮助:
image主要的几个IPC的方式就简单介绍到这里,有什么问题可以留言。