阅读 119

Android多进程

前言

好久没有写学习心得了,最近看了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<FilterEntity> CREATOR = new Creator<FilterEntity>() {
        @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<Book> mBookList = new CopyOnWriteArrayList<Book>();
//    private CopyOnWriteArrayList<IOnNewBookArriveListen> mListenList = new CopyOnWriteArrayList<IOnNewBookArriveListen>();
    private RemoteCallbackList<IOnNewBookArriveListen> mListenList = new RemoteCallbackList<>();
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    private Binder mBinder = new IBookManager.Stub() {
        @Override
        public List<Book> 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文件如下:

    <service android:name=".service.BookManagerService"
            android:process=":remote"
            android:enabled="true"
            android:exported="true"
            />
        <activity android:name=".aidl.BookMnagerActivity"/>
复制代码

这样BookManagerService就运行在远程服务了。接下来就看客户端怎么和远程服务端怎么去通信了,我们新建一个AIDL文件,这个接口文件中去声明几个我们需要的方法。

interface IBookManager {
   List<Book> 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<com.example.wangchao.testproject.data.Book> _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<com.example.wangchao.testproject.data.Book> getBookList() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.wangchao.testproject.data.Book> _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<com.example.wangchao.testproject.data.Book> 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<com.example.wangchao.testproject.data.Book> 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<Book> 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<com.example.wangchao.testproject.data.Book> getBookList() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.wangchao.testproject.data.Book> _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<com.example.wangchao.testproject.data.Book> _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 服务,因为远程进程在干啥,我们是不知道的
关注下面的标签,发现更多相似文章
评论