前言
好久没有写学习心得了,最近看了Android多进程相关的知识,是时候总结一下了,也方便以后自己温习,我主要围绕以下几点展开:
为何使用ipc
两种序列化的区别
Binder简单了解
实现多进程的方式有哪些
IPC的必要性
如果要用到ipc,那么必须有多进程的存在,为何要使用多进程呢,这里给出两点:
防止oom,考虑增加应用的使用内存,一个应用分得的内存是有限的,我们为了增加应用的内存,将一些模块单独放在进程中去,这样系统就会给这些模块单独分配内存,降低应用oom的概率。
跨应用获取数据,有时候想获取其他应用中的一些数据,比如联系人的信息,这些信息在其他模块,我们肯定不能直接获取到,这就需要ipc了,事实上ContentProvier也是多进程通信的方式,只是一些细节被屏蔽掉了,我们无法感知而已。
序列化
当使用多进程传递数据的时候,为了数据的一致性,我们就必须要将对象序列化和反序列化,序列化不是Android中的新概念,在Java中就有,序列化有两种方式Serializable和Parcelable接口。
1.Serializable接口
java序列化就是将实现了Serializable的对象转换成字符序列形式保存在文件中,在需要的时候再将字符序列转换为对象恢复原来的结构,即反序列化。这样我们既然是字符序列,我们就可以很方便的传输了,比如intent,多进程,甚至网络通信等等。
//序列化操作1--FileOutputStream
ObjectOutputStream oos1 = new ObjectOutputStream(new FileOutputStream("worm.out"));
oos1.writeObject("Worm storage By FileOutputStream ");
oos1.writeObject(w);//必须所有引用的对象都实现序列化(本例终究是Data这个类),否则抛出有java.io.NotSerializableException:这个异常
oos1.close();
//反序列化操作1---FileInputStream
ObjectInputStream ois1 = new ObjectInputStream(new FileInputStream("worm.out"));
String s1 = (String)ois1.readObject();
Worm w1 = (Worm)ois1.readObject();
ois1.close();
System.out.println("反序列化操作1之后");
System.out.println(s1);
System.out.println("w1:"+w1);
复制代码public class Data implements Serializable {
private static final long serialVersionUID = 7247714666080613254L;
public int n;
public Data(int n) {
this.n = n;
}
public String toString(){
return Integer.toString(n);
}
}
复制代码
以上就是对实现了Serializable的对象进行序列化和反序列化,可以看出,序列化和反序列化就是将对象写入文件和对文件进行读取的过程,这也太明了了,就是这么简单,需要注意的是,所有的操作都是针对字节。
序列化操纵之前
w=:a(853):b(119):c(802):d(788):e(199):f(881)
反序列化操作1之后
Worm storage By FileOutputStream
w1::a(853):b(119):c(802):d(788):e(199):f(881)
复制代码
这里说明一下,serialVersionUID,这个玩意我们经常看到,它是干嘛的,既然系统加入这个肯定是有它的作用的啦,这个很重要,我们进行序列化的时候也会写入这个值,当在反序列化的时候我们会对这个值做个比较,如果一致就可以成功反序列化操作,否则就会抛异常。
Parcelable
在Android中,出现了一种新的序列化方法,那就是使用Parcelable,使用它我们就可以很方便在intent,binder传递数据啦,使用方式很简单。
public class FilterEntity implements Parcelable{
private String name;
private String chooseStr;
private boolean hasSelected;
public FilterEntity(String name, String chooseStr, boolean hasSelected) {
this.name = name;
this.chooseStr = chooseStr;
this.hasSelected = hasSelected;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getChooseStr() {
return chooseStr;
}
public void setChooseStr(String chooseStr) {
this.chooseStr = chooseStr;
}
public boolean isHasSelected() {
return hasSelected;
}
public void setHasSelected(boolean hasSelected) {
this.hasSelected = hasSelected;
}
@Override
public String toString() {
return "FilterEntity{" +
"name='" + name + '\'' +
", chooseStr='" + chooseStr + '\'' +
", hasSelected=" + hasSelected +
'}';
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
dest.writeString(this.chooseStr);
dest.writeByte(this.hasSelected ? (byte) 1 : (byte) 0);
}
public FilterEntity() {
}
protected FilterEntity(Parcel in) {
this.name = in.readString();
this.chooseStr = in.readString();
this.hasSelected = in.readByte() != 0;
}
public static final Creator CREATOR = new Creator() {
@Override
public FilterEntity createFromParcel(Parcel source) {
return new FilterEntity(source);
}
@Override
public FilterEntity[] newArray(int size) {
return new FilterEntity[size];
}
};
}复制代码
两种序列化的比较
Serializable是java的序列化方式,使用起来简单,但是开销比较大,在序列化和反序列化的时候会进行大量的读写操作。
Pacelable是android独有的,所以在Android平台下肯定是首选啦,它使用起来稍微复杂一点,但是非常高效。
Parcelable主要是将对象序列化到内存中,而Serializable主要是序列化到存储设备和网络通信中。
Binder
Binder是一个很复杂的概念,我们这里主要是谈谈它的上层应用使用方式,Binder是进程之间通信的桥梁,这里给一个远程service
public class BookManagerService extends Service {
private CopyOnWriteArrayList mBookList = new CopyOnWriteArrayList();
// private CopyOnWriteArrayList mListenList = new CopyOnWriteArrayList();
private RemoteCallbackList mListenList = new RemoteCallbackList<>();
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private Binder mBinder = new IBookManager.Stub() {
@Override
public List getBookList() throws RemoteException {
Log.d("wangchao0000", "getBookList: ");
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
//可以做一些事,比如包验证
return super.onTransact(code, data, reply, flags);
}
};
@Override
public void onCreate() {
super.onCreate();
Log.d("wangchao0000", "onCreate: ");
// mBookList.add(new Book(1, "开发艺术探索"));
// mBookList.add(new Book(2, "Flutter进阶"));
ExecutorService executorService = Executors.newScheduledThreadPool(1);
((ScheduledExecutorService) executorService).scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
Log.d("wangchao0000", "run: ");
int bookId = mBookList.size() + 1;
notifyNewBookArrived(new Book(bookId, "new book--" + bookId));
}
},0, 5, TimeUnit.SECONDS);
}
}
复制代码
Manifest文件如下:
android:process=":remote"
android:enabled="true"
android:exported="true"
/>
复制代码
这样BookManagerService就运行在远程服务了。接下来就看客户端怎么和远程服务端怎么去通信了,我们新建一个AIDL文件,这个接口文件中去声明几个我们需要的方法。
interface IBookManager {
List getBookList();
void addBook(in Book book);
}
复制代码
系统会根据这个AIDL文件自动生成Binder类,这个类就是我们的核心类,利用它客户端和服务端就可以通信了哈,想起来就是兴奋,我们把这个类梳理一下就ok了。先上代码:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /Users/wangchao/code/TestProject/app/src/main/aidl/com/example/wangchao/testproject/IBookManager.aidl
*/
package com.example.wangchao.testproject;
// Declare any non-default types here with import statements
public interface IBookManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.wangchao.testproject.IBookManager
{
private static final java.lang.String DESCRIPTOR = "com.example.wangchao.testproject.IBookManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.wangchao.testproject.IBookManager interface,
* generating a proxy if needed.
*/
public static com.example.wangchao.testproject.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.wangchao.testproject.IBookManager))) {
return ((com.example.wangchao.testproject.IBookManager)iin);
}
return new com.example.wangchao.testproject.IBookManager.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getBookList:
{
data.enforceInterface(DESCRIPTOR);
java.util.List _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(DESCRIPTOR);
com.example.wangchao.testproject.data.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.example.wangchao.testproject.data.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 com.example.wangchao.testproject.IBookManager
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public java.util.List getBookList() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.example.wangchao.testproject.data.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addBook(com.example.wangchao.testproject.data.Book book) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.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 = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
public java.util.List getBookList() throws android.os.RemoteException;
public void addBook(com.example.wangchao.testproject.data.Book book) throws android.os.RemoteException;
}
复制代码
咋一眼看到这个代码是不是很头大,是的,我也很头痛,系统自动生成的,不过我们来梳理一下:这个类继承类IInterface,一旦继承了这个接口,系统就认为它是AIDL:
/**
* Base class for Binder interfaces. When defining a new interface,
* you must derive it from IInterface.
*/
public interface IInterface
{
/**
* Retrieve the Binder object associated with this interface.
* You must use this instead of a plain cast, so that proxy objects
* can return the correct result.
*/
public IBinder asBinder();
}
复制代码
没啥啊,里面就是只有asBinder一个方法,该方法就是返回binder对象的。
接下来看看:
public java.util.List getBookList() throws android.os.RemoteException;
public void addBook(com.example.wangchao.testproject.data.Book book) throws android.os.RemoteException;
复制代码
仔细一看,这不就是咱们在AIDL文件中定义的那两个方法嘛,这里对它进行了声明,看来在这个类中肯定是要用到了。然后定义了几个整型变量,用来告诉服务端目前客户端是调用的哪一个方法,不然服务端怎么知道呢。
接着看,重点来了,就是那个stub,这个概念我们并不陌生,它时常在service中出没:
private Binder mBinder = new IBookManager.Stub() {
@Override
public List getBookList() throws RemoteException {
Log.d("wangchao0000", "getBookList: ");
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
mBookList.add(book);
}
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
//可以做一些事,比如包验证
return super.onTransact(code, data, reply, flags);
}
};
复制代码
是吧,我没说错把。接下来我们就分析分析了。
它是一个内部类,它就是一个Binder类,它实现了IInterface接口。这个类中有几个重要的方法:
asInterface
onTransact
asBinder
Proxy,它是一个内部类
咱们分别来分析他们的实现逻辑:
asInterface
我们先来看看asInterface方法,用来获取服务端对象:
/**
* Cast an IBinder object into an com.example.wangchao.testproject.IBookManager interface,
* generating a proxy if needed.
*/
public static com.example.wangchao.testproject.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.wangchao.testproject.IBookManager))) {
return ((com.example.wangchao.testproject.IBookManager)iin);
}
return new com.example.wangchao.testproject.IBookManager.Stub.Proxy(obj);
}
复制代码
它是一个静态方法,调用的地方是在客户端,注释写得很清楚,如果客户端和服务端在同一进程中,直接返回service对象,否则返回Proxy。事实上,既然写这个类,那肯定是不在一个进程了,那么proxy就有用武之地了。
Proxy
Proxy是stub的内部类,它实现了AIDL接口,Proxy对象是返回给客户端的,客户端拿到后去调用里面
的方法:
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
bookManager = IBookManager.Stub.asInterface(service);
try {
// Log.d("BookMnagerActivity", "query book list: -------" + Arrays.asList(list));
// list.add(new Book(3, "web进阶"));
// Log.d("BookMnagerActivity", "query book list: -------" + Arrays.asList(bookManager.getBookList()));
bookManager.registerListen(onNewBookArriveListen);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
复制代码
这个bookManager就是proxy的对象,这里可以通过断点进去看。
我们选一个方法去分析吧:
private static class Proxy implements com.example.wangchao.testproject.IBookManager
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public java.util.List getBookList() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.example.wangchao.testproject.data.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
复制代码
当客户端去调用getBookList方法的时候,它就执行到这里,这里声明了_data, _reply, _result。调用过程如下:
首先把之前定义好的int标识符写到_data里面去,以便服务端去识别。
调用远程服务的transact,这时候客户端进程挂起,直到服务端执行完ontransact方法,返回结果之后才能唤醒。
返回结果后,唤醒,将数据返回给客户端的调用处。
onTransact
前面说了,客户端调用了服务端的transact方法,该方法属于Binder的方法,在Binder线程池中
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
复制代码
接下来看看,服务端干了啥。
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getBookList:
{
data.enforceInterface(DESCRIPTOR);
java.util.List _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
return super.onTransact(code, data, reply, flags);
}
复制代码
该方法中,它会去根据code去判断调用哪个方法,这个code就是之前声明的那个id,哦,nice,原来这么简单:
根据code去查找需要调用的方法
调用服务端的getBookList
将返回的结果写到reply里面去,然后唤醒客户端
proxy和onTransact不就是两个相互应答的过程嘛。
这里需要说明一下,
客户端发起请求后当前线程就会被挂起,直到服务端进程返回结果,如果远程方法执行很耗时的操作,就会造成ANR,所以发起请求的线程不能是UI线程哈。
Binder方法运行在线程池中,这里要使用线程同步。
其实大家经常说的AIDL,有个啥用,它就是个工具而已,利用这个工具系统可以帮我们生成Binder类,但这个AIDL并不是必须的,我们可以自己去写Binder类。
Android中IPC的几种方式
Bundle
文件共享
Messenger
AIDL
ContentPrider
Bundle
相信大家对这个都不陌生,在Activity,Service使用intent传递数据的时候经常会用到,只要这个对象是已经序列化了的,它就可以放到Bundle里去传输,这不就是ipc了嘛。
文件共享
这个很简单,多个进程或者是应该去读写同一个文件,我们把序列化的对象写进去,另外一个进程从这个文件从去读取,然后再反序列化,就可以很顺利的拿到数据了。
Messenger AIDL
上面将binder的时候已经分析过AIDL了,不懂的大家可以专门去学学AIDL的使用,Messenger就是封装了AIDL而已,没啥好讲的。
ContentPrider
这玩意不就是Android四大组建之一吗,对,就是它,它天然带了IPC属性,比如获取联系人模块里面的数据。
好了,也写了不少了,主要理解原理,至于怎么使用不是这片文章的重点。
总结
序列化和反序列化就是将对象转换为字符序列存储到某个文件,之后就是读写文件获取,方便传输而已。
IPC要抓住那个桥梁,就是Binder,binder里面的方法就是服务端定义的方法,我们作为客户端就是想调用服务端的方法来实现一些功能,如果我们拿到了Binder,还愁不能调用服务端的方法吗,所以。。。。。
不要在UI线程中去bind 服务,因为远程进程在干啥,我们是不知道的