一.简介
IPC: 进程间通信或者跨进程通信指两个进程间进行数据交换的过程
ANR: Android主线程才能操作UI,如果把大量耗时任务放到主线程中执行就会造成界面无法响应,导致ANR的发生。
Android中进程间通信方式最有特色的是Binder,除此之外还支持Socket通信。
二.Android中的多进程模式
2.1.Android使用多进程的方式
在Andorid中使用多进程只有一种方法,给四大组件在AndroidManifest中指定android:process属性。
- 声明process时没有指定process属性就会运行在默认进程中,默认进程为包名。
- :remote是一种简写的方法,含义是在当前进程名前面附加当前包名,而且以:开头的进程属于当前应用的私有进程,其他应用组件不可以和它跑进同一个进程。
- Android系统会为每一个应用分配一个唯一的UID,具有相同UID并且签名相同的应用才可以共享应用数据,这种情况下,可以相互访问对方的私有数据。
2.2.多进程会造成的问题
- 静态成员和单例模式完全失效
- 线程同步机制完全失效
- SharedPreferences的可靠性下降
- Application多次创建
三.IPC基础概念简介
3.1.Serializable接口和Parcelable接口
对象的序列化,当我们需要把对象持久化存储或者通过网络传输给其他客户端,此时就需要序列化。
Serializable接口: java提供的一个序列化接口,是一个空接口,为对象提供标准的序列化和反序列化操作。只需要在类中声明指定一个long标识即可。静态成员属于类不属于对象,用transient关键字标识的成员变量不参与序列化的过程。
Parcelable接口: Android提供的序列化接口,只要实现这个接口,一个类的对象就可以实现序列化并通过Intent和Binder传递。
- describeContents():内容描述功能直接返回0.
- writeToParcel(Parcel out, int flags):序列化由此方法完成,最终通过Parcel中一系列的write方法完成
- Parcelable.Creator CREATOR:序列化功能由CREATOR完成,内部标明如何创建序列化对象和数组并通过Parcel的read完成反序列化过程。
两种序列化方式的比较: Serializable用起来简单,开销大,序列化和反序列化需要大量IO操作。Parcelable是Android中的序列化方式,使用麻烦,效率高,主要用在内存序列化上。
3.2.Binder
Binder实现了IBinder接口,从IPC角度,Binder是Android中一种跨进程通信方式,还可以理解为一种虚拟的物理设备,设备驱动是dev/binder。
从framework角度来讲Binder是ServiceManager连接各种Manager和响应ManagerService的桥梁。
从应用层来说Binder是客户端和服务端进行通信的媒介,当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个对象,客户端可以获取服务端提供的服务或数据。
四.Android中的IPC方式
Android中,Binder主要用在Service中,包含AIDL和Messenger,普通Service中的Binder不涉及进程间通信,Messenger的底层是AIDL
4.1.使用Bundle
Activity,Service,Receiver都支持在Intent中传递Bundle数据,Bundle实现了Parcelable接口,可以方便的在不同进程间传输,可以在Bundle中附加需要传输给远程进程的数据的信息并通过Intent发送出去。
4.2.使用文件共享
共享文件也可以实现进程间通信,两个进程通过读/写同一个文件来交换数据,Android允许多个线程对同一个文件进行写操作。除了可以交换文本信息还可以序列化对象到文件系统的同时另一个进程恢复这个对象。
适用于对数据同步要求不高的进程之间进行通信。
4.3.使用Messenger
Messenger是一种轻量级的IPC方案,底层是AIDL,对AIDL进行了封装,可以简便的进行进程间通信,一次只处理一个请求,所以服务端不考虑同步的问题。
实现步骤:
- 服务端进程:需要在服务端创建一个Service处理客户端的请求,同时创建一个Handler并以它作为参数来创建一个Messenger对象,然后再Service的onBind中返回这个Messenger底层的Binder即可。
/**
* @author lvlin
*/
public class MessengerService extends Service {
private static final String TAG = "MessengerService";
private static class MessengerHandler extends Handler{
@Override
public void handleMessage(@NonNull Message msg) {
Log.d(TAG, "msg" + msg.what);
switch (msg.what){
case 1:
Log.d(TAG, "receive msg from client: " + msg.getData().getString("msg"));
Messenger client = msg.replyTo;
Message replayMessage = Message.obtain(null, 2);
Bundle bundle = new Bundle();
bundle.putString("reply", "你的消息我已收到, 稍后回复你");
replayMessage.setData(bundle);
try {
client.send(replayMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
default:
super.handleMessage(msg);
}
}
}
private final Messenger messenger = new Messenger(new MessengerHandler());
public MessengerService() {
}
@Override
public void onCreate() {
Log.d(TAG, "onCreate");
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return messenger.getBinder();
}
}
- 客户端进程:首先绑定服务端的Service,绑定成功后用服务端返回的IBinder对象创建一个Messenger,通过这个Messenger就可以向服务端发送消息,消息类型为Message对象,如果想要服务端可以回应客户端,就和服务端一样创建一个Handler和Messenger并把这个Messenger对象通过message的replyTo参数传递给服务端,服务端通过这个参数就可以回应客户端了。
public class MessengerActivity extends AppCompatActivity {
private static final String TAG = "MessengerActivity";
private Messenger mService;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mService = new Messenger(iBinder);
Message msg = Message.obtain(null, 1);
Bundle data = new Bundle();
data.putString("msg", "this is client");
msg.setData(data);
msg.replyTo = getReplyMessenger;
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
private Messenger getReplyMessenger = new Messenger(new MessengerHandler());
private static class MessengerHandler extends Handler{
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case 2:
Log.d(TAG, "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_main);
Intent intent = new Intent(this, MessengerService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(connection);
super.onDestroy();
}
}
4.4.使用AIDL
Messenger是以串行的方式处理客户端发来的消息,如果大量消息同时发送到客户端,也就是由大量的并发请求,就应该使用AIDL。
实现步骤:
- 服务端:创建一个Service来监听客户端的连接请求,然后创建一个AIDL文件。将暴露给客户端的接口在AIDL文件中声明。最后在Service中实现AIDL接口即可。
- 客户端:绑定服务端的Service,绑定成功后,将服务端返回的Binder对象转成AIDL接口所属的类型,接可以调用AIDL中声明的方法了。
- 目录结构:
- AIDL:
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
void registerListener(IOnNewBookArrivedListener listener);
void unregisterListener(IOnNewBookArrivedListener listener);
}
package com.lvlin.aidldemo;
parcelable Book;
import com.lvlin.aidldemo.Book;
interface IOnNewBookArrivedListener {
void onNewBookArrived(in Book book);
}
- 实体类:
/**
* @author lvlin
* @change 2021/5/8
**/
public class Book implements Parcelable {
public int bookId;
public String bookName;
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
protected Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(bookId);
parcel.writeString(bookName);
}
}
- 服务端:
public class BookManagerService extends Service {
private static final String TAG = "BookManagerService";
private AtomicBoolean mIsServiceDestoryed = 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);
}
};
public BookManagerService() {
}
@Override
public void onCreate() {
super.onCreate();
mBookList.add(new Book(1, "Android"));
mBookList.add(new Book(2, "Ios"));
new Thread(new ServiceWorker()).start();
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public void onDestroy() {
mIsServiceDestoryed.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 listener = mListenerList.getBroadcastItem(i);
if(listener != null){
listener.onNewBookArrived(book);
}
}
mListenerList.finishBroadcast();
}
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();
}
}
}
}
}
- 客户端:
public class BookManagerActivity extends AppCompatActivity {
private static final String TAG = "BookManagerActivity";
private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;
private IBookManager mRemoteBookManager;
private Handler mHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what){
case MESSAGE_NEW_BOOK_ARRIVED:
Log.d(TAG, "recelve new book" + msg.obj);
break;
default:
super.handleMessage(msg);
}
}
};
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
IBookManager bookManager = IBookManager.Stub.asInterface(iBinder);
try {
mRemoteBookManager = bookManager;
List<Book> list = bookManager.getBookList();
for(Book book : list){
Log.d(TAG, "query book list :" + book.bookName);
}
Book newBook = new Book(3, "Android开发艺术探索");
bookManager.addBook(newBook);
List<Book> newList = bookManager.getBookList();
for(Book book : list){
Log.d(TAG, "query new book list :" + book.bookName);
}
bookManager.registerListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub() {
@Override
public void onNewBookArrived(Book book) throws RemoteException {
mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, book).sendToTarget();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
if(mRemoteBookManager != null && mRemoteBookManager.asBinder().isBinderAlive()){
Log.d(TAG, "unRegistener listener :" + mOnNewBookArrivedListener);
try {
mRemoteBookManager.unregisterListener(mOnNewBookArrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
unbindService(mConnection);
super.onDestroy();
}
}
AIDL文件支持的数据类型:
- 基本数据类型(int,long,char,long,boolean,boolean)
- String,CharSequence
- List:只支持ArrayList,里边每个元素也需要被支持
- Map:只支持HashMap,里边每个元素需要被支持,key和value.
- Parcelable:实现该接口的对象。
- AIDL:所有的AIDL接口接口本身亦可以在AIDL文件中使用。
注意
- 客户端调用远程服务的方法,被调用的方法运行在服务端的Binder线程池中,同时客户端线程会被挂起,这个时候如果客户端线程长时间阻塞在这里,而如果客户端线程是UI线程的话,就会导致客户端ANR。
- 由于客户端的onServiceConnected和onServiceDisconnected方法都运行在UI线程所以不可直接在它们里边调用服务端的耗时方法。
- 由于服务端本身就运行在服务端的Binder线程中,所以服务端方法本身就可以执行大量耗时操作,不要再服务端执行异步任务。
- 当远程服务端调用客户端的listener中的方法时,被调用的方法也运行在Binder线程池中,只不过是客户端的线程池,同样不可以在服务端调用客户端的耗时方法。
4.5.使用ContentProvider
四大组件之一,使用方法不多叙述。
4.6.使用Socket
Socket也称为套接字,是网络通信的概念,分为流式套接字和用户数据包套接字两种分别对应于网络的传输控制层的TCP和UDP协议。
实现步骤
- 远程Service建立一个TCP服务,主界面连接TCP服务。不仅可以实现跨进程通信,也可以实现设备间通信,前提是设备之间IP互相可见。
- 服务端:
/**
* @author lvlin
* @change 2021/5/6
**/
public class TCPServerService extends Service {
private static final String TAG = "TCPServerService";
private boolean isServiceDestoryed = false;
private String [] definedMessages = new String[] {
"hello",
"hi",
"bye",
"see you later"
};
@Override
public void onCreate() {
Log.d(TAG, "onCreate");
new Thread(new TcpServer()).start();
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
isServiceDestoryed = true;
super.onDestroy();
}
private class TcpServer implements Runnable{
@Override
public void run() {
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(8688);
} catch (IOException e) {
Log.d(TAG, "establish tcp server failed, port 8688");
e.printStackTrace();
return;
}
while (!isServiceDestoryed){
try {
final Socket client = serverSocket.accept();
Log.d(TAG, "establish tcp server success, port 8688");
new Thread(){
@Override
public void run() {
try {
responseClient(client);
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void responseClient(Socket client) throws IOException{
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(client.getOutputStream())), true);
out.println("welcome to chat room!");
while (!isServiceDestoryed){
String str = in.readLine();
Log.d(TAG, "message from client:" + str);
if (str == null){
break;
}
int i = new Random().nextInt(definedMessages.length);
String msg = definedMessages [i];
out.println(msg);
Log.d(TAG, "message send:" + msg);
}
Log.d(TAG, "client exit:");
out.close();
in.close();
client.close();
}
}
- 客户端:
public class TCPClientActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "TCPClientActivity";
private static final int MESSAGE_RECEIVE_NEW_MSG = 1;
private static final int MESSAGE_SOCKET_CONNECTED = 2;
private Button sendButton;
private TextView chatContent;
private EditText inputContent;
private PrintWriter printWriter;
private Socket clientSocket;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sendButton = (Button) findViewById(R.id.chat_send);
chatContent = (TextView) findViewById(R.id.chat_content);
inputContent = (EditText) findViewById(R.id.chat_input);
sendButton.setOnClickListener(this);
Intent service = new Intent(this, TCPServerService.class);
startService(service);
Log.d(TAG, "onCreate");
new Thread(){
@Override
public void run() {
connectTCPServer();
}
}.start();
}
@Override
protected void onDestroy() {
if(clientSocket != null){
try {
clientSocket.shutdownInput();
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
super.onDestroy();
}
@SuppressLint("HandlerLeak")
private final Handler tcpHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
switch (msg.what) {
case MESSAGE_RECEIVE_NEW_MSG:
chatContent.setText(chatContent.getText() + (String) msg.obj);
break;
case MESSAGE_SOCKET_CONNECTED:
sendButton.setEnabled(true);
break;
default:
break;
}
}
};
private void connectTCPServer() {
Socket socket = null;
while (socket == null){
try {
socket = new Socket("localhost", 8688);
clientSocket = socket;
printWriter = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())),
true);
tcpHandler.sendEmptyMessage(MESSAGE_SOCKET_CONNECTED);
Log.d(TAG, "connect server success");
} catch (IOException e) {
SystemClock.sleep(1000);
e.printStackTrace();
Log.d(TAG, "connect server failed, retry...");
}
}
try {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(
socket.getInputStream()
));
while (!TCPClientActivity.this.isFinishing()){
String msg = bufferedReader.readLine();
Log.d(TAG, "receive ;" + msg);
if(msg != null){
String time = formatDateTime(System.currentTimeMillis());
final String showedMsg = "server" + time + ":" +msg + "\n";
tcpHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG, showedMsg).sendToTarget();
}
}
Log.d(TAG, "exit...");
printWriter.close();
bufferedReader.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private String formatDateTime(long time){
return new SimpleDateFormat("(HH:mm:ss)").format(new Date(time));
}
@Override
public void onClick(View view) {
if(view == sendButton){
final String msg = inputContent.getText().toString();
if(!TextUtils.isEmpty(msg) && printWriter != null){
new Thread(new Runnable() {
@Override
public void run() {
printWriter.println(msg);
}
}).start();
inputContent.setText("");
String time = formatDateTime(System.currentTimeMillis());
final String showMsg = "self" + time + ":" + msg + "\n";
chatContent.setText(chatContent.getText() + showMsg);
}
}
}
}