Android IPC方式

Android IPC簡介

IPC是Inter-Process Communication的縮寫,含義爲進程間通訊,是指兩個進程之間進行數據交換的過程。java

Android中IPC的使用狀況分爲兩種:android

  • 第一種狀況是一個應用由於某些緣由自身須要採用多進程模式來實現,至於緣由,可能有不少,好比有些模塊因爲特殊的緣由須要運行在單獨的進程中,又或者爲了加大一個應用可以使用的內存因此須要經過多進行來獲取多份內存空間。服務器

  • 第二種狀況是當前應用須要向其餘應用獲取數據,因爲是兩個應用,因此必須採用跨進程的方式來獲取所須要的數據,甚至咱們系統提供的ContentProvider去查詢數據的時候,其實也是一種進程間通訊,只不過通訊細節被系統內部屏蔽了。網絡

Android中的多進程模式

開啓多進行模式

正常狀況下,在Android中多進程是指一個應用中存在多個進程的狀況,所以這裏不討論兩個應用之間的多進程狀況。首先,在Android中使用多進程只有一種方法,就是給四大組件在AndroidManifest中指定android:process屬性,除此以外沒有其餘辦法。其實還有另外一種很是規的多進程方法,那就是經過JNI在native層其fork一個新的進程,這種方法屬於特殊狀況,也不是經常使用的建立多進程的方式。下面示例,描述如何在Android中建立多進程多線程

<activity
        android:name="com.example.demo.AActivity"
        android:process=":remote" />
    
    <activity
        android:name="com.example.demo.BActivity"
        android:process="com.example.demo.BaseActivity.remote" />

上面示例分爲爲AActivity和BActivity指定了process屬性,而且他們的屬性值不一樣,意味着當前應用又增長了兩個進程。當AActivity啓動時,系統會爲它建立一個單獨的進程,進程名爲"com.example.demo.BaseActivity:remote";當BActivity啓動時,系統也會爲它建立一個單獨的進程,進程名爲"com.example.demo.BaseActivity.remote"。併發

":remote"和"com.example.demo.BaseActivity.remote"這兩種命名方式的區別?ide

  • 首先「:」的含義是指要在當前的進程名前面附加當前的包名,而"com.example.demo.BaseActivity.remote"是完整的命名方式不會附加包名信息this

  • 其次,進程名以「:」開頭的進程屬於當前應用的私有進程,其餘應用的組件不能夠和它跑在同一個進程中,而進程名不易「:」開頭的進程屬於全局進程,其餘應用經過ShareUID方式能夠和它跑在同一個進程中。spa

多進程模式的運行機制

Android爲你每一個應用分配了一個獨立的虛擬機,或者說爲每一個進程都分配一個獨立的虛擬機,不一樣的虛擬機在內存分配上有不一樣的地址空間,這就會致使在不一樣的虛擬機中訪問同一個類對象會產生多份副本。線程

全部運行在不一樣進程中的四大組件,只要它們之間須要經過內存來共享數據,都會共享失敗,這也是多線程所帶來的主要影響。

通常來講,使用多線程會形成以下四個方面的問題:

  • (1)靜態成員和單例模式徹底失效

Android爲你每一個應用分配了一個獨立的虛擬機,或者說爲每一個進程都分配一個獨立的虛擬機,不一樣的虛擬機在內存分配上有不一樣的地址空間,這就會致使在不一樣的虛擬機中訪問同一個類對象會產生多份副本。

  • (2)線程同步機制徹底失效

本質上和第一個問題是相似的,既然都不是一塊內存了,那麼無論鎖對象仍是鎖全局類都沒法保證線程同步,由於不一樣進程鎖的不是同一個對象。

  • (3)SharedPreference的可靠性降低

是由於SharedPreference不支持兩個進程同步去執行寫操做,不然會致使必定概率的丟失,這是由於SharedPreference底層是經過讀/寫XML文件實現的,併發寫文件顯然是可能出問題的,甚至併發讀/寫都有可能出問題。

  • (4)Application會屢次建立

當一個組件跑在一個新的進程中的時候,因爲系統要在建立新的進程同時分配獨立的虛擬機,因此這個過程其實就是啓動一個新應用的過程。所以,至關於系統又把這個應用從新啓動了一遍,既然從新啓動了,那麼天然會建立新的Application。

在多進程模式中,不一樣進程的組件的確會擁有獨立的虛擬機、Application和內存空間。

爲了解決這個問題,Android系統提供了不少跨進程通訊方法實現數據交互。如Intent來傳遞數據,共享文件和SharedPreference,基於Binder的Messenger和AIDL,Socket等。

IPC基礎概念介紹

Serializable接口

Serializable是Java所提供的一個序列化接口,它是一個空接口,爲對象提供標準的序列化和反序列化操做。使用Serializable來實現序列化至關簡單隻須要在類的聲明中指定一個相似下面的標誌便可自動實現默認的序列化過程。

private static final long serialVersionUID = 5123020951483359287L; //系統生成的hash值
    private static final long serialVersionUID = 1L; //指定爲1L

    public class User implements Serializable {
    
        /**
         * 
         */
        private static final long serialVersionUID = 5123020951483359287L; //系統生成的hash值
        
    
        public int         userId;
        public String     userName;
        public boolean     isMale;
        
        @Override
        public String toString() {
            return "User [userId=" + userId + ", userName=" + userName
                    + ", isMale=" + isMale + "]";
        }
    
    }

經過Serializable接口來實現對象的序列化過程很是簡單,幾乎全部的工做都被系統自動完成了。

//序列化存儲
    User user = new User(2, "liuguoquan", true);
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(Environment.getExternalStorageDirectory()+"/cache.txt"));
        out.writeObject(user);
        out.close();

    //反序列化
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(Environment.getExternalStorageDirectory()+"/cache.txt"));
        User newUser = (User) in.readObject();
        in.close();

上述代碼描述了採用Serializable方式序列化對象的典型過程,很簡單,只須要把實現了Serializable接口的User對象寫到文件中就能夠快速恢復了,恢復後的對象newUser和user的內容徹底同樣,可是二者並非同一個對象。

其實,不指定serialVersionUID也能夠實現序列化,那到底要不要指定呢?系統既然提供了這個serialVersionUID,那麼它必須是有用的,原則上序列化的數據中的serialVersionUID只有和當前類的serialVersionUID相同時纔可以正常地被反序列化。

serialVersionUID的詳細工做機制是這樣的:序列化的時候系統會把當前類的serialVersionUID寫入序列化的文件中,當反序列化的時候系統會去檢查文件中的serialVersionUID,看它是否和當前類的serialVersionUID一致,若是一致說明序列化的類版本與當前類的版本是相同的則能夠成功反序列化;不然就說明當前類和序列化的類相比發生了某些變化,好比成員變量的數量、類型可能發生了改變,這個時候是沒法正常反序列化的。

給serialVersionUID指定爲1L或者採用系統當前類結構去生成的hash值,這二者並無什麼區別,效果徹底同樣。如下兩點須要注意:

  • 靜態成員變量屬於類不屬於對象,因此不會參與序列化過程

  • 用transient關鍵字標記的成員變量不參與序列化過程

Parcalable接口

Parcelable也是一個也是一個接口,只要實現這個接口,一個類的對象就要就能夠實現序列化並能夠經過Intent和Binder傳遞。下面的示例是一個典型的用法。

public class Person implements Parcelable {
        
        private int id;
        private String name;
        private int sex;
        private User user;
        
        
    
    
        @Override
        public int describeContents() {
            // TODO Auto-generated method stub
            return 0;
        }
    
        //序列化
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            // TODO Auto-generated method stub
            dest.writeInt(id);
            dest.writeString(name);
            dest.writeInt(sex);
            dest.writeSerializable(user);
            
        }
        
        //反序列化
        public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {
    
            @Override
            public Person createFromParcel(Parcel source) {
                // TODO Auto-generated method stub
                
                Person person = new Person();
                //必需要按照成員變量的初始化順序
                person.id = source.readInt();
                person.name = source.readString();
                person.sex = source.readInt();
                person.user = (User) source.readSerializable();
                return person;
            }
    
            @Override
            public Person[] newArray(int size) {
                // TODO Auto-generated method stub
                return new Person[size];
            }
        };
    
    }

系統已經爲咱們提供了許多實現了Parcelable接口的類,它們逗死能夠直接序列化的,好比Intent、Bundle、Bitmap等,同時List和Map也能夠序列化,前提是他們裏面的每一個元素均可以序列化。

既然Parcelable和Serializable都能實現序列化而且均可用於Intent間的數據傳遞,那麼兩者該如何選取呢?Serializable是Java中的序列化接口,其使用起來很是簡單可是開銷很大,序列化和反序列化過程須要大量的I/O操做。而Parcelable是Android中的序列化方式,所以更適合在Android平臺上,它的缺點就是使用起來稍微麻煩點,可是它的效率很高,這是Android推薦的序列化方式。所以首選Parcelable。Parcelable主要用在內存序列化上,經過Parcelable將對象序列化到存儲設備中或者將對象序列化後經過網絡傳輸也都是能夠的,但這個過程顯得複雜,所以這兩種狀況下建議使用Serializable。

Binder是Android中的一個類,它實現了IBinder接口。從IPC角度來講,Binder是Android中的一種跨進程通訊方式,Binder還能夠理解爲一種虛擬的物理設備,它的設備驅動是/dev/binder,該通訊方式在Linux中沒有;從Android Framework角度來講,Binder是ServiceManager鏈接各類Manager(ActivityManager、WindowManager,等待)和相應的ManagerService的橋樑;從Android應用層來講,Binder是客戶端和服務端進行通訊的媒介,當bindservice時候,服務端會返回一個包含了服務端業務調用的Binder對象,經過這個Binder對象,客戶端就能夠獲取服務端提供的服務或數據,這裏的服務包括普通的服務和基於AIDL的服務。

Android開發中,Binder主要用在service中,包括AIDL和Messenger,其中普通Service中的Binder不涉及進程間通訊,而Messenger的底層實際上是AIDL,因此這裏選用AIDL來分析Binder的工做機制。

下面新建一個AIDL示例,新建三個文件Book.java、Book.aidl、IBookManager.aidl,代碼以下所示:

Book.java

package com.ryg.chapter_2.aidl;
    
    
    import android.os.Parcel;
    import android.os.Parcelable;
    
    public class Book implements Parcelable {
        
        public int bookId;
        public String bookName;
    
        
        
        public Book(int bookId, String bookName) {
            this.bookId = bookId;
            this.bookName = bookName;
        }
    
        @Override
        public int describeContents() {
            // TODO Auto-generated method stub
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            // TODO Auto-generated method stub
            dest.writeInt(bookId);
            dest.writeString(bookName);
    
        }
        
        public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
    
            @Override
            public Book createFromParcel(Parcel source) {
                // TODO Auto-generated method stub
                
                return new Book(source);
            }
    
            @Override
            public Book[] newArray(int size) {
                // TODO Auto-generated method stub
                return new Book[size];
            }
        };
        
        
        private Book(Parcel in) {
            
            bookId = in.readInt();
            bookName = in.readString();
        }
    
    }

Book.aidl

    package com.ryg.chapter_2.aidl;
    
    parcelable Book;

IBookManager.aidl

    package com.ryg.chapter_2.aidl;
    
    import com.ryg.chapter_2.aidl.Book;
    
    interface IBookManager {
    
        List<Book> getBookList();
        void addBook(in Book book);
        
    }

Book.java是一個圖書信息的類,它實現了Parcelable接口。Book.aidl是Book類在AIDL的聲明。IBookManager.aidl是咱們定義的一個接口,裏面有兩個方法,其中getBookList用於從遠程服務端獲取圖書列表,而addBook用於向圖書列表中添加一本書。儘管Book類已經和IBookManager位於相同的包中,可是IBookManager中仍然要導入Book類,接下來系統會在gen目錄自動生成一個IBookManager的類。以下

/*
     * This file is auto-generated.  DO NOT MODIFY.
     * Original file: D:\\liuguoquan\\workspace\\chapter_2\\src\\com\\ryg\\chapter_2\\aidl\\IBookManager.aidl
     */
    package com.ryg.chapter_2.aidl;
    
    //在Binder傳輸的接口都要實現IInterface
    public interface IBookManager extends android.os.IInterface {
        /** Local-side IPC implementation stub class. */
        public static abstract class Stub extends android.os.Binder implements
                com.ryg.chapter_2.aidl.IBookManager {
            private static final java.lang.String DESCRIPTOR = "com.ryg.chapter_2.aidl.IBookManager";
    
            //內部類,這個Stub就是一個Binder類
            /** Construct the stub at attach it to the interface. */
            public Stub() {
                this.attachInterface(this, DESCRIPTOR);
            }
    
            /**
             * Cast an IBinder object into an com.ryg.chapter_2.aidl.IBookManager
             * interface, generating a proxy if needed.
             */
            public static com.ryg.chapter_2.aidl.IBookManager asInterface(
                    android.os.IBinder obj) {
                if ((obj == null)) {
                    return null;
                }
                android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
                if (((iin != null) && (iin instanceof com.ryg.chapter_2.aidl.IBookManager))) {
                    return ((com.ryg.chapter_2.aidl.IBookManager) iin);
                }
                return new com.ryg.chapter_2.aidl.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.ryg.chapter_2.aidl.Book> _result = this
                            .getBookList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    com.ryg.chapter_2.aidl.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.ryg.chapter_2.aidl.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.ryg.chapter_2.aidl.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.ryg.chapter_2.aidl.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.ryg.chapter_2.aidl.Book> _result;
                    try {
                        _data.writeInterfaceToken(DESCRIPTOR);
                        mRemote.transact(Stub.TRANSACTION_getBookList, _data,
                                _reply, 0);
                        _reply.readException();
                        _result = _reply
                                .createTypedArrayList(com.ryg.chapter_2.aidl.Book.CREATOR);
                    } finally {
                        _reply.recycle();
                        _data.recycle();
                    }
                    return _result;
                }
    
                @Override
                public void addBook(com.ryg.chapter_2.aidl.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); //標識方法的id
            static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        }
    
        public java.util.List<com.ryg.chapter_2.aidl.Book> getBookList()
                throws android.os.RemoteException;
    
        public void addBook(com.ryg.chapter_2.aidl.Book book)
                throws android.os.RemoteException;
    }

上述代碼實現的功能:
(1)IBookManager類繼承IInterface接口,同時它本身也是個接口,全部能夠在Binder中傳輸的接口都須要繼承IInterface接口
(2)首先,它聲明瞭兩個方法getBookList和addBook,就是咱們自IBookManager.aidl中定義的方法,同時還聲明瞭兩個整形id分別用於標識這兩個方法,這兩個id用於標識在transact中客戶端所請求的究竟是哪一個方法
(3)接着,它聲明瞭一個內部類Stub,這個Stub就是一個Binder類,當客戶端和服務端都位於一個進程時,方法調用不會走跨進程的transact過程,而當二者位於不一樣進程時,方法調用須要走transact過程,這個邏輯由Stub的內部代理類Proxy來完成。這個接口的核心實現就是內部了Stub和Stub的內部代理類Proxy

說明:首先,當客戶端發起遠程請求時,因爲當前線程會被掛起直至服務器進程返回數據,因此若是一個遠程方法是很耗時的,那麼不能在UI線程中發起次遠程請求;其次,因爲服務器的Binder方法運行在Binder線程池中,因此Binder方法不論是否耗時都應該採用同步的方式去實現,由於它已經運行在一個線程中了。

相關文章
相關標籤/搜索