Android多進程

前言

很久沒有寫學習心得了,最近看了Android多進程相關的知識,是時候總結一下了,也方便之後本身溫習,我主要圍繞如下幾點展開:java

  • 爲什麼使用ipc
  • 兩種序列化的區別
  • Binder簡單瞭解
  • 實現多進程的方式有哪些

IPC的必要性

若是要用到ipc,那麼必須有多進程的存在,爲什麼要使用多進程呢,這裏給出兩點:android

  • 防止oom,考慮增長應用的使用內存,一個應用分得的內存是有限的,咱們爲了增長應用的內存,將一些模塊單獨放在進程中去,這樣系統就會給這些模塊單獨分配內存,下降應用oom的機率。
  • 跨應用獲取數據,有時候想獲取其餘應用中的一些數據,好比聯繫人的信息,這些信息在其餘模塊,咱們確定不能直接獲取到,這就須要ipc了,事實上ContentProvier也是多進程通訊的方式,只是一些細節被屏蔽掉了,咱們沒法感知而已。

序列化

當使用多進程傳遞數據的時候,爲了數據的一致性,咱們就必需要將對象序列化和反序列化,序列化不是Android中的新概念,在Java中就有,序列化有兩種方式Serializable和Parcelable接口。web

1.Serializable接口

java序列化就是將實現了Serializable的對象轉換成字符序列形式保存在文件中,在須要的時候再將字符序列轉換爲對象恢復原來的結構,即反序列化。這樣咱們既然是字符序列,咱們就能夠很方便的傳輸了,好比intent,多進程,甚至網絡通訊等等。bash

//序列化操做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,這個玩意咱們常常看到,它是幹嗎的,既然系統加入這個確定是有它的做用的啦,這個很重要,咱們進行序列化的時候也會寫入這個值,當在反序列化的時候咱們會對這個值作個比較,若是一致就能夠成功反序列化操做,不然就會拋異常。app

Parcelable

在Android中,出現了一種新的序列化方法,那就是使用Parcelable,使用它咱們就能夠很方便在intent,binder傳遞數據啦,使用方式很簡單。ide

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文件,這個接口文件中去聲明幾個咱們須要的方法。ui

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 服務,由於遠程進程在幹啥,咱們是不知道的
相關文章
相關標籤/搜索