一、IPC与多线程
1、IPC简介
线程:① CPU最小的调度单元 ② 一种有限的系统资源
进程:一个执行单元。一般指一个程序或一个应用。一个进程可以包含多个线程。
IPC:进程间通信。
多线程的情况
1)因为某些原因自身需要采用多线程模式来实现。比如:某些模块需要运行在单独进程;为了加大一个应用可以使用的内存。
2)需要从其他应用获取数据。
2、Android中的多进程模式
2.1、开启多进程模式
在Android中一个应用开启多进程唯一办法:给四大组件在AndroidMenifest.xml中指定android:process
属性。
<service
android:name=".MyService"
android:process=":remote" />
<service
android:name=".SecondService"
android:process="com.example.xiang.myapplication.remote" />
默认进程名是包名。“:remote”是一种省略写法,完整名为“com.example.xiang.myapplication:remote”进程名,以“:”开头,属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中。
2.2、多进程模式的运行机制
Android系统为每一个进程分配了一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间。将导致存在如下问题:
1)静态成员和单例完全失效
2)线程同步机制完全失效
3)SharedPreferences的可靠性下降
4)Application会多次创建
不同的进程拥有独立的虚拟机、Application和内存空间,导致通过内存来共享数据,都会共享失败。
Android的IPC方式
1)Intent
2)文件共享方式
3)Binder(AIDL和Messenger)
4)ContentProvider
5)Socket
二、IPC的基础概念
为什么要序列化?
1)永久性保存对象的字节序列到本地文件中
2)通过序列化在网络中传递对象
3)通过序列化在进程间传递对象
1、Serializable接口
Serializable是Java提供的一个序列化接口,是一个空接口,为对象提供标准的序列化和反序列化操作。
private static final long serialVersionUID = 8154678445665565611L;
serialVersionUID是用来辅助序列化和序列化的过程,原则上序列化后的数据中的serialVersionUID只有和当前类的serialVersionUID相同才能够正常地被反序列化。一般我们应该手动指定serialVersionUID的值,比如1L(或者根据类结构生成hash值)。若不指定,反序列化时当前类有所改变(比如增加或者删除了成员变量),那么系统会重新计算当前类的hash值并赋给serialVersionUID,导致serialVersionUID不一致,于是反序列化失败,程序就会crash。
静态成员变量属于类不属于对象,不会参加序列化
用transient标记的成员变量不会参与序列化
2、Parcelable接口
public class User implements Parcelable {
private int userId;
private String userName;
private Book book;
protected User(Parcel in) {
userId = in.readInt();
userName = in.readString();
//book是一个可序列化对象,需要传递当前线程的上下文类加载器
book = in.readParcelable(Thread.currentThread().getContextClassLoader());
}
/**
* 实现反序列化
*/
public static final Creator<User> CREATOR = new Creator<User>() {
@Override
public User createFromParcel(Parcel in) {
return new User(in);
}
@Override
public User[] newArray(int size) {
return new User[size];
}
};
/**
* 几乎所有情况都返回0
* @return
*/
@Override
public int describeContents() {
return 0;
}
/**
* 实现序列化
*
* @param parcel
* @param i
*/
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(userId);
parcel.writeString(userName);
parcel.writeParcelable(book, i);
}
}
3、Binder
3.1、AIDL接口的创建
Binder就是Android中实现了IBinder接口的一个类,是跨进程通信的媒介。在Android开发中,Binder主要用在Service中,包括Messenger(底层其实是AIDL)和AIDL。
//Book.java
package com.example.xiang.myapplication;
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable {
private int bookId;
private String bookName;
public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
/**
* 几乎所有情况都返回0
* @return
*/
@Override
public int describeContents() {
return 0;
}
/**
* 实现序列化
* @param parcel
* @param i
*/
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(bookId);
parcel.writeString(bookName);
}
/**
* 实现反序列化
*/
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];
}
};
public Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
}
}
//Book.aidl
package com.example.xiang.myapplication;
parcelable Book;
// IBookManager.aidl
package com.example.xiang.myapplication;
import com.example.xiang.myapplication.Book;//必须导入,否则报错
interface IBookManager {
List<Book> getBookList();
void addBook(in Book book);
}
Book.aidl是Book类在AIDL中的声明。IBookManager.aidl是定义的一个接口,虽然Book类和IBookManager在同一个包中,但是还是要显示导入Book类。目录结构如下:
AIDL目录结构(新建Book.aidl时候,直接填Book为名字时候会报错,只有先创建完之后再RENAME才不会报错)
3.2、Binder类分析
系统为IBookManager.aidl自动生成的Binder类
package com.example.xiang.myapplication;
public interface IBookManager extends android.os.IInterface {
public static abstract class Stub extends Binder implements IBookManager {
//Binder的唯一标识
private static final String DESCRIPTOR = "com.example.xiang.myapplication.IBookManager";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static IBookManager asInterface(IBinder obj) {
if ((obj == null)) {
return null;
}
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IBookManager))) {
return ((IBookManager) iin);
}
return new IBookManager.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, Parcel data,
Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(DESCRIPTOR);
List<Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
Book _arg0;
if ((0 != data.readInt())) {
_arg0 = Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements IBookManager {
private IBinder mRemote;
Proxy(IBinder remote) {
mRemote = remote;
}
@Override
public IBinder asBinder() {
return mRemote;
}
public String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public List<Book> getBookList() throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
List<Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
//_data写入参数
//发起远程调用,当前线程挂起
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addBook(Book book) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getBookList = (IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (IBinder.FIRST_CALL_TRANSACTION + 1);
}
public List<Book> getBookList() throws RemoteException;
public void addBook(Book book) throws RemoteException;
}
-
asInterface
//将服务端的Binder对象转换为客户端所需的AIDL接口类型的对象,如果C/S位于同一进程,此方法返回就是服务端的Stub对象本身,否则返回的就是系统封装后的Stub.proxy对象 -
asBinder
//返回当前的Binder对象 -
onTransact
//运行在服务端 -
Proxy#getBookList
//运行在客户端,内部实现过程如下:首先创建该方法所需要的输入型对象Parcel对象_data,输出型Parcel对象_reply和返回值对象List。然后把该方法的参数信息写入_data( 如果有参数);接着调用transact方法发起RPC( 远程过程调用),同时当前线程挂起(因此不能再UI线程中发起远程请求);然后服务端的onTransact方法会被调用(服务端的Binder方法运行在线程池,所以需要采用同步方式实现),直到RPC过程返回后,当前线程继续执行,并从_reply中取出RPC过程的返回结果,最后返回_reply中的数据。
AIDL的本质:系统提供的一个快速实现Binder的工具而已。
3.3、远程服务端Service的实现
public class BookManagerService extends Service {
private static final String TAG = "BookManagerService";
//CopyOnWriteArrayList支持并发读/写
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
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 onCreate() {
super.onCreate();
mBookList.add(new Book(1, "Android开发艺术探索"));
mBookList.add(new Book(2, "Android进阶之光"));
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
//注册Service
<service
android:name="com.example.service.MessengerService"
android:process=":remote" />
AIDL方法(getBookList和addBook)是运行在Binder线程池中的,所以需要处理线程同步,这里采用CopyOnWriteArrayList来进行自动的线程同步。
3.4、客户端的实现
public class BookManagerActivity extends Activity {
private static final String TAG = "BookManagerActivity";
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IBookManager bookManager = IBookManager.Stub.asInterface(service);
try {
List<Book> list = bookManager.getBookList();
Log.d(TAG, "查询图书列表:" + list.toString());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = new Intent(this, BookManagerService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(connection);
super.onDestroy();
}
}
服务端的方法可能需要很久才能执行完毕,上面这样写的目的是为了更好的了解AIDL的实现步骤。
对象是不能跨进程直接传输的,对象跨进程传输的本质是反序列化过程,这也是为什么AIDL中定义的对象必须实现Parcelable接口。
4、Binder的两个重要方法
linkToDeath(DeathRecipient recipient, int flags)
//设置Binder的死亡代理。当Binder死亡时,系统会回调DeathRecipient的binderDied()
方法,所以需要在此方法中移除之前绑定的binder代理(调用unlinkToDeath
)并重新绑定远程服务
unlinkToDeath(DeathRecipient recipient, int flags)
//移除死亡代理
isBinderAlive()
//判断Binder是否死亡
DeathRecipient是IBinder的一个内部接口,里面只有一个方法binderDied()
5、补充说明
AIDL文件支持的数据类型
1)基本数据类型(int、long、char、boolean等)
2)String和CharSequence
3)List:只支持ArrayList,里面的每个元素必须被AIDL所支持
4)Map:只支持HashMap,里面的每个元素必须被AIDL所支持,包括key和value
5)Parcelable:所有实现了Parcelable接口的对象
6)AIDL:所有AIDL接口本身也可以在AIDL文件中使用
- 自定义的Parcelable对象(如上例中的Book类),必须新建一个和它同名的AIDL文件(Book.aidl),并添加相应的内容。
- 自定义Parcelable对象和AIDL对象必须要显式import进来。
- 除了基本数据类型的其他类型参数,都需要标上方向:in、out或者inout。
- AIDL接口中只支持方法,不支持声明静态常量。
- 建议把所有和AIDL相关的类和文件全部放在同一个包中,好处是,若客户端在另一应用(模块),复制整个包即可。
- AIDL的包结构在服务端和客户端必须保持一致,否则运行出错。
DeathRecipient是IBinder的一个内部接口,里面只有一个方法binderDied()
三、Android中的IPC方式
1、使用Intent
启动另一个进程的Activity、Service和Receiver的时候,在Bundle(实现了Parcelable接口)中附加信息,并通过Intent进行传递
2、使用文件共享
序列化一个对象到文件系统中的同时从另一个进程中恢复这个对象,适合对数据同步要求不高的进程之间进行通信,并且要妥善处理并发读写问题。
SharedPreferences底层实现采用XML文件来存储键值对。系统对它的读/写有一定的缓存策略,即在内存中会有一份 SharedPreferences文件的缓存,因此在多进程模式下,系统对它的读/写变得不可靠,面对高并发读/写时SharedPreferences 有很大几率丢失数据,因此不建议在IPC中使用SharedPreferences。
3、使用Messenger(信使)
一种轻量级的IPC方案,底层实现是AIDL。服务端以串行的方式处理客户端发来的Message对象。
举个例子
//MessengerService.java
//服务端代码
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 Constants.MSG_FROM_CLIENT:
//收到客户端的消息
Log.d(TAG, "收到客户端发来的信息:" + msg.getData().getString("msg"));
Messenger client = msg.replyTo;
Message replyMessage = Message.obtain(null, Constants.MSG_FROM_SERVER);
Bundle bundle = new Bundle();
bundle.putString("reply","你的消息我已经收到,稍后回复你。");
try {
client.send(replyMessage);
} catch (RemoteException e) {
e.printStackTrace();
}
break;
}
}
}
private final Messenger mMessenger = new Messenger(new MessengerHandler());
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
//注册Service
<service
android:name="com.example.service.MessengerService"
android:process=":remote" />
//MainActivity.java
//客户端实现
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
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, Constants.MSG_FROM_CLIENT);
Bundle data = new Bundle();
data.putString("msg", "hello, this is client");
msg.setData(data);
//注意这一句
msg.replyTo = mGetReplyMessenger;
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());
private class MessengerHandler extends Handler {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case Constants.MSG_FROM_SERVER:
Log.d(TAG, "收到服务端的回复" + msg.getData().getString("reply"));
break;
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Intent intent = new Intent(this, MessengerService.class);
findViewById(R.id.btn_bind).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
});
}
@Override
protected void onDestroy() {
unbindService(connection);
super.onDestroy();
}
}
Messenger中传递的数据必须将数据放入Message,Message和Messenger都实现了Parcelable接口(放入Message中的对象也要实现Parcelable接口才能传递)。工作原理如图所示:
[站外图片上传中...(image-7382db-1516020836113)]
Messenger常用方法
Messenger(Handler target)
Messenger(IBinder target)
send(Message message)
getBinder() IBinder
Message相关属性
replyTo
//返回一个可以答复的Messenger
4、使用AIDL
见上面的例子
网友评论