帶你了解android的IPC機(jī)制

IPC機(jī)制簡介

IPC是Inter-Process Communication的縮寫,含義就是跨進(jìn)程通信。
首先我們要理解什么是進(jìn)程,什么是線程。按操作系統(tǒng)的描述,進(jìn)程是資源分配的最小單位,而線程是CPU調(diào)度的最小單位,一個進(jìn)程可以包含多個線程(主線程、子線程)。多線程需要考慮并發(fā)問題。
Android中的主線程是也叫UI線程,在主線程執(zhí)行耗時操作會ANR

多進(jìn)程的兩種情況
1 某個應(yīng)用由于自身原因需要采用多進(jìn)程模式來實現(xiàn)(如:某些模塊由于特殊原因需要運行在獨立進(jìn)程)
2 為了加大一個應(yīng)用可使用的內(nèi)存通過多進(jìn)程來獲取多份內(nèi)存空間

Android中的多進(jìn)程模式

在正式講解進(jìn)程間通信前我們先了解Android中的多進(jìn)程模式。
通過給四大組件指定android:process屬性可以輕易開啟多進(jìn)程(看起來簡單,但是有許多需要注意的問題)

一般情況下Android中多進(jìn)程是指一個應(yīng)用中存在多個進(jìn)程的情況。首先在Android使用多進(jìn)程只有一中方式就是為四大組件指定android:process屬性(特例:使用JNI在native底層fork一個新進(jìn)程)

1 android:process=”:test” (私有進(jìn)程其他應(yīng)用組件不可在該進(jìn)程)
2 android:process=”com.test.l” (全局進(jìn)程其他組件可用ShareUID方式跑在相同進(jìn)程中)

UID:在Linux中的代表用戶ID,android系統(tǒng)為每個應(yīng)用分配的標(biāo)識,也就是說android中的每個應(yīng)用其實就相當(dāng)于一個用戶。兩個應(yīng)用如果想通過shareUID的方式跑在同一個進(jìn)程中必須保證UID相同,并且簽名一致。那么同進(jìn)程中就可以相互訪問對方的私有數(shù)據(jù)(如data,res資源,組件信息,內(nèi)存數(shù)據(jù)等等)

ShareUID擴(kuò)展
通過shareduserid來獲取系統(tǒng)權(quán)限
(1)在AndroidManifest.xml中添加android:sharedUserId=”android.uid.system”
(2)在Android.mk文件里面添加LOCAL_CERTIFICATE := platform(使用系統(tǒng)簽名)
(3)在源碼下面進(jìn)行mm編譯
這樣生成的apk能夠獲取system權(quán)限,可以在任意system權(quán)限目錄下面進(jìn)行目錄或者文件的創(chuàng)建,以及訪問其他apk資源等(注意創(chuàng)建的文件(夾)只有創(chuàng)建者(比如system,root除外)擁有可讀可寫權(quán)限

運行機(jī)制與IPC基礎(chǔ)

首先我們知道Android系統(tǒng)是基于JVM(Dalvik與ART)
其次系統(tǒng)會為每個進(jìn)程分配一個獨立的虛擬機(jī),意味著進(jìn)程間的內(nèi)存的相互獨立的

一般來說使用多進(jìn)程會有幾個問題
1 靜態(tài)成員與單利模式完全失效
2 線程同步機(jī)制完全失效
3 SP可靠性下降
4 Application創(chuàng)建多次

問:進(jìn)程間的對象傳遞問題怎么解決?
通過IBinder來實現(xiàn)進(jìn)程間對象的轉(zhuǎn)換

IPC基礎(chǔ)概念介紹

主要包含三部分:Serialiazable,Parcelable以及Binder
序列化:將對象轉(zhuǎn)化為字節(jié)的過程
Serialiazable:Java提供的序列化接口(標(biāo)記接口)
Parcelable:android提供的序列化接口
Serialiazable與Parcelable的區(qū)別:Serialiazable使用簡單但是需要大量I/O操作,Parcelable使用較繁瑣,主要用于內(nèi)存序列化,效率高

Binder
直觀的看,Binder是Android中的一個類,實現(xiàn)了IBinder接口
從不同角度理解Binder:
1 從IPC角度,Binder是跨進(jìn)程通信方式
2 從FrameWork角度,Binder是ServiceManager連接各種Manager(如am,wm等)的橋梁
3 從應(yīng)用層角度,Binder是客戶端與服務(wù)端通信的媒介

AIDL的初步了解

1 創(chuàng)建Book.java實體類(實現(xiàn)Parcelable接口,支持內(nèi)存序列化)
2 創(chuàng)建Book類的aidl聲明
3 創(chuàng)建IBookManager的aidl接口

實現(xiàn)Parcelable接口的Book類

public class Book implements Parcelable{
    private int id;
    private String name;
    private double price;

    public Book(int id, String name, double price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }

    public Book() {
    }

    protected Book(Parcel in) {
        id = in.readInt();
        name = in.readString();
        price = in.readDouble();
    }

    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];
        }
    };

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", price=" + price +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int i) {
        parcel.writeInt(id);
        parcel.writeString(name);
        parcel.writeDouble(price);
    }
}

其實內(nèi)部是通過Parcel進(jìn)行對象的轉(zhuǎn)換的,咱們順便看看Parcel類的描述

Container for a message (data and object references) that can
be sent through an IBinder. A Parcel can contain both flattened data
that will be unflattened on the other side of the IPC (using the various
methods here for writing specific types, or the general
{@link Parcelable} interface), and references to live {@link IBinder}
objects that will result in the other side receiving a proxy IBinder
connected with the original IBinder in the Parcel.

簡單的翻譯下,Parcel是IBinder發(fā)送消息(數(shù)據(jù)和對象引用)的容器,Parcel可以數(shù)據(jù)通過各個操作基礎(chǔ)類型的方法將數(shù)據(jù)轉(zhuǎn)化為扁平化傳遞給IPC的另一端,并且通過IBinder使得對方收到的代理IBinder對象中包裹著原始IBinder??偟恼f它的作用就是實現(xiàn)對象數(shù)據(jù)在內(nèi)存中的傳遞

Book的aidl聲明

package com.example.pangyunxiao.ipcdemo.com.itszt.l;

parcelable Book;

我用的IDE是Android Studio,直接右鍵創(chuàng)建aidl文件。會自動幫你創(chuàng)建aidl目錄,并且將你創(chuàng)建的aidl聲明放置到你的項目包名下,如果的eclipse則開發(fā)者要自行注意。

IBookManager的聲明

package com.example.pangyunxiao.ipcdemo.com.itszt.l;

import com.example.pangyunxiao.ipcdemo.com.itszt.l.Book;
import com.example.pangyunxiao.ipcdemo.com.itszt.l.IOnNewBookAddedListener;
// Declare any non-default types here with import statements

interface IBookManager {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

            void addBook(in Book book);

            List<Book> getBooks();

            void addBookAndNotify(in Book book);

            void registerListener(IOnNewBookAddedListener l);

            void unregisterListener(IOnNewBookAddedListener l);
}

注意問題
1 實體類的包名與aidl文件的包名必須一致
2 IBookManager中必須import所需實體類
3 接口中聲明的方法參數(shù)需要使用in/out/inout
4 aidl文件以及實體類在兩端都要有,并且同個包下

IBookManager.java簡析
接著咱們編譯一下項目,會發(fā)現(xiàn)自動生成的IBookManager.java類。路徑:eclipse中位于gen目錄下,as中位于build\generated\source\aidl\debug目錄下,接著咱們來簡要分析下這個類(由于類比長,就不把代碼全貼出來啦)

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.pangyunxiao.ipcdemo.com.itszt.l.IBookManager {
        private static final java.lang.String DESCRIPTOR = "com.example.pangyunxiao.ipcdemo.com.itszt.l.IBookManager";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.example.pangyunxiao.ipcdemo.com.itszt.l.IBookManager interface,
         * generating a proxy if needed.
         */
        public static com.example.pangyunxiao.ipcdemo.com.itszt.l.IBookManager asInterface(android.os.IBinder 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 {
            ...
        }

        private static class Proxy implements com.example.pangyunxiao.ipcdemo.com.itszt.l.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;
            }

            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             */
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
                ...
            }

            @Override
            public void addBook(com.example.pangyunxiao.ipcdemo.com.itszt.l.Book book) throws android.os.RemoteException {
                ...
            }

            @Override
            public java.util.List<com.example.pangyunxiao.ipcdemo.com.itszt.l.Book> getBooks() throws android.os.RemoteException {
                ...
            }

            @Override
            public void addBookAndNotify(com.example.pangyunxiao.ipcdemo.com.itszt.l.Book book) throws android.os.RemoteException {
                ...
            }

            @Override
            public void registerListener(com.example.pangyunxiao.ipcdemo.com.itszt.l.IOnNewBookAddedListener l) throws android.os.RemoteException {
                ...
            }

            @Override
            public void unregisterListener(com.example.pangyunxiao.ipcdemo.com.itszt.l.IOnNewBookAddedListener l) throws android.os.RemoteException {
               ...
            }
        }

        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_getBooks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        static final int TRANSACTION_addBookAndNotify = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
        static final int TRANSACTION_registerListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
        static final int TRANSACTION_unregisterListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 5);
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;

    public void addBook(com.example.pangyunxiao.ipcdemo.com.itszt.l.Book book) throws android.os.RemoteException;

    public java.util.List<com.example.pangyunxiao.ipcdemo.com.itszt.l.Book> getBooks() throws android.os.RemoteException;

    public void addBookAndNotify(com.example.pangyunxiao.ipcdemo.com.itszt.l.Book book) throws android.os.RemoteException;

    public void registerListener(com.example.pangyunxiao.ipcdemo.com.itszt.l.IOnNewBookAddedListener l) throws android.os.RemoteException;

    public void unregisterListener(com.example.pangyunxiao.ipcdemo.com.itszt.l.IOnNewBookAddedListener l) throws android.os.RemoteException;
}

DESCRIPTOR:Binder的唯一標(biāo)識
1 這是個接口類并且集成了IInterface接口,所有可以在Binder中傳輸?shù)慕涌诙夹枰^承IIterface接口
2 Stub內(nèi)部類:是IBookManager類的內(nèi)部類,就是一個Binder類
asInterface 靜態(tài)方法:Binder對象轉(zhuǎn)換為接口類型對象
asBinder 非靜態(tài)方法:接口類型對象轉(zhuǎn)換為Binder對象
onTransact方法:當(dāng)客戶端發(fā)起遠(yuǎn)程調(diào)用時,在服務(wù)端會執(zhí)行該方法;參數(shù)code:標(biāo)識調(diào)用的方法;參數(shù)data:傳遞的參數(shù);參數(shù)reply:用于存儲返回值;該方法返回值是boolean表示遠(yuǎn)程調(diào)用是否成功(可做權(quán)限驗證)
3 Proxy:Stub的內(nèi)部類,作用是作為IBookManager的代理類(asInterface返回的其實就是它);在該類中主要實現(xiàn)了各個方法的執(zhí)行流程。例如addBook:方法運行在客戶端,首先獲取兩個Parcel對象(參數(shù)_data與返回值_reply),然后把參數(shù)信息寫入_data中,調(diào)用IBinder對象的transact方法發(fā)起遠(yuǎn)程調(diào)用,此時當(dāng)前線程被掛起然后服務(wù)器端的onTransact執(zhí)行,當(dāng)執(zhí)行完畢,當(dāng)前線程繼續(xù)執(zhí)行,如果方法有返回值的話通過_reply取得。

注意點
當(dāng)客戶端發(fā)起請求時當(dāng)前線程會被掛起直至服務(wù)端返回數(shù)據(jù)。那么如果一個遠(yuǎn)程方法是耗時的就不能在UI線程發(fā)起遠(yuǎn)程請求
2 AIDL文件并不是必須的,只是為了方便系統(tǒng)根據(jù)aidl文件生存java類,我們可以拋開aidl文件直接寫一個Binder
(1創(chuàng)建IBookManager接口類2IBookManager實現(xiàn)類3 Servic中返回)
3 Binder運行在服務(wù)端進(jìn)程,如果服務(wù)器進(jìn)程由于某種原因終止了,那么遠(yuǎn)程調(diào)用將失敗(稱為Binder死亡),可以通過Binder對象的linkToDeath方法設(shè)置死亡代理

流程

實現(xiàn)IPC的方式

怎么實現(xiàn)IPC?其實只要完成它的本質(zhì)需求,就是在進(jìn)程之間傳遞數(shù)據(jù),那就算實現(xiàn)了IPC。因此我們有很多種方式可以完成這個目標(biāo)。實際開發(fā)過程中可以選擇合適的方式去實現(xiàn)這個數(shù)據(jù)傳遞過程。

1 使用Bundle

這是最簡單的方式,它就是通過Bundle在不同進(jìn)程的組件之間傳遞數(shù)據(jù)

startActivity(new Intent(...).putExtra("data",bundle));

2 使用文件共享

利用多進(jìn)程同時讀寫同個外部文件達(dá)到是數(shù)據(jù)交互的目的
存儲形式?jīng)]有限制:xml,文本,對象序列化等等
缺點:由于Linux系統(tǒng)對文件并發(fā)讀寫沒有限制,會導(dǎo)致數(shù)據(jù)不同步問題,所以該方式只適合于對數(shù)據(jù)同步要求不高的進(jìn)程間通信

3 使用共享參數(shù)

共享參數(shù)是android中一中輕量級存儲方案,底層用實現(xiàn)xml文件,系統(tǒng)對它的讀寫有一定的緩存策略(內(nèi)存中會有一份sp的備份)在多進(jìn)程模式下,系統(tǒng)對它的讀/寫是不可靠的,高并發(fā)讀/寫時有可能會丟失數(shù)據(jù)

4 使用Messenger

Messenger(信使):在不同的進(jìn)程之間傳遞Message對象,是一種輕量級的IPC方案,底層實現(xiàn)就是AIDL
客戶端使用服務(wù)器的messenger向服務(wù)器發(fā)消息
服務(wù)器使用客戶端的messenger向客戶端發(fā)消息
(類似Handler的使用)
注意Service的隱式啟動再5.0+上的問題,需要將隱式Intent轉(zhuǎn)為顯式

服務(wù)端Handler

public class MessengerHandler extends Handler {

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        if(msg.what == 1){
            Log.e("MessengerHandler", msg.getData().getString("msg"));
            Messenger client = msg.replyTo;
            Message message = Message.obtain();
            message.what = 1;
            Bundle bundle = new Bundle();
            bundle.putString("reply","shou dao le.");
            message.setData(bundle);
            try {
                client.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }
}

服務(wù)端Service

public class MessengerService extends Service {

    Messenger messenger = new Messenger(new MessengerHandler());

    @Override
    public IBinder onBind(Intent intent) {
        return messenger.getBinder();
    }
}

        <service android:name=".MessengerService" android:enabled="true" android:exported="true">
            <intent-filter>
                <action android:name="com.itszt.lww"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </service>

客戶端Handler

public class ClientMHandler extends Handler {

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        if(msg.what == 1){
            String str = msg.getData().getString("reply");
            if (BuildConfig.DEBUG) Log.e("ClientMHandler", str);
        }
    }
}

客戶端調(diào)起遠(yuǎn)程服務(wù)

intent = new Intent("com.itszt.lww");
        bindService(createExplicitFromImplicitIntent(this,intent),conn, Service.BIND_AUTO_CREATE);

ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Messenger messenger = new Messenger(iBinder);
            if(messenger == null) return;
            Message message = Message.obtain();
            message.what = 1;
            Bundle bundle = new Bundle();
            bundle.putString("msg","i am a client.");
            message.setData(bundle);
            message.replyTo = new Messenger(new ClientMHandler());
            try {
                messenger.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

使用AIDL

在上述的AIDL初了解的基礎(chǔ)上,咱們來實現(xiàn)簡單的AIDL通信。增加一個Service提供給客戶端調(diào)起,并返回咱們定義IBookManager對應(yīng)的IBinder對象

public class AIDLService extends Service {

    // 保證高并發(fā)問題
    List<Book> books = new CopyOnWriteArrayList<>();
    RemoteCallbackList<IOnNewBookAddedListener> listeners = new RemoteCallbackList<>();

    {
        books.add(new Book(1,"java",66.3));
        books.add(new Book(2,"php",61.3));
        books.add(new Book(3,"c++",65.3));
    }

    IBookManager bookManager = new IBookManager.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public void addBook(Book book) throws RemoteException {
            books.add(book);
        }

        @Override
        public List<Book> getBooks() throws RemoteException {
            // 前面我們有提到AIDL只支持ArrayList
            // 那么為什么我們這邊可以返回CopyOrWriteArrayList
            // 是這樣的,服務(wù)器返回的CopyOrWriteArrayList會按List規(guī)范生成一個ArrayList傳給客戶端
            // 類似的還有ConcurrentHashMap
            return books;
        }

        @Override
        public void addBookAndNotify(Book book) throws RemoteException {
            addBook(book);
            if(listeners == null) return;
            int count = listeners.beginBroadcast();
            for(int i = 0;i<count;i++){
                IOnNewBookAddedListener listener = listeners.getBroadcastItem(i);
                listener.onNewBook();
            }
            listeners.finishBroadcast();
        }

        @Override
        public void registerListener(IOnNewBookAddedListener l) throws RemoteException {
            if(listeners == null)
                listeners = new RemoteCallbackList<>();
            listeners.beginBroadcast();
            listeners.register(l);
            listeners.finishBroadcast();
            if (BuildConfig.DEBUG) Log.e("AIDLServiceAdd", "listeners.size():" + listeners.beginBroadcast());
            listeners.finishBroadcast();
        }

        @Override
        public void unregisterListener(IOnNewBookAddedListener l) throws RemoteException {
            if(listeners != null){
                listeners.beginBroadcast();
                listeners.unregister(l);
                listeners.finishBroadcast();
            }
            if (BuildConfig.DEBUG) Log.e("AIDLServiceRemove", "listeners.size():" + listeners.beginBroadcast());
            listeners.finishBroadcast();
        }

    };

    @Override
    public IBinder onBind(Intent intent) {
        return bookManager.asBinder();
    }
}

        <service android:name=".AIDLService" android:enabled="true" android:exported="true">
            <intent-filter>
                <action android:name="com.itszt.aidl"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </service>

客戶端調(diào)起服務(wù)獲得IBinder

public class MainActivity extends Activity {

    IBookManager bookManager;
    IOnNewBookAddedListener listener = new IOnNewBookAddedListener.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public void onNewBook() throws RemoteException {
            Toast.makeText(MainActivity.this, "收到新書通知啦??!", Toast.LENGTH_SHORT).show();
        }
    };

    Intent intent;
    ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            if(iBinder == null) return;
            bookManager = IBookManager.Stub.asInterface(iBinder);
            if(bookManager == null) return;
            try {
                iBinder.linkToDeath(new IBinder.DeathRecipient() {
                    @Override
                    public void binderDied() {
                        unbindService(conn);
                        bindService(intent,conn,Service.BIND_AUTO_CREATE);
                    }
                },0);
                bookManager.registerListener(listener);
                Toast.makeText(MainActivity.this, bookManager.getBooks().toString(), Toast.LENGTH_SHORT).show();
                bookManager.addBook(new Book(5,"mXx",90));
                bookManager.addBookAndNotify(new Book(5,"mXx",90));
                // Toast.makeText(MainActivity.this, bookManager.getBooks().toString(), Toast.LENGTH_SHORT).show();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        intent = new Intent("com.itszt.aidl");
        bindService(createExplicitFromImplicitIntent(this,intent),conn, Service.BIND_AUTO_CREATE);
    }

    public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
        // Retrieve all services that can match the given intent
        PackageManager pm = context.getPackageManager();
        List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);

        // Make sure only one match was found
        if (resolveInfo == null || resolveInfo.size() != 1) {
            return null;
        }

        // Get component info and create ComponentName
        ResolveInfo serviceInfo = resolveInfo.get(0);
        String packageName = serviceInfo.serviceInfo.packageName;
        String className = serviceInfo.serviceInfo.name;
        ComponentName component = new ComponentName(packageName, className);

        // Create a new intent. Use the old one for extras and such reuse
        Intent explicitIntent = new Intent(implicitIntent);

        // Set the component to be explicit
        explicitIntent.setComponent(component);

        return explicitIntent;
    }

    @Override
    protected void onDestroy() {
        if(bookManager!=null && bookManager.asBinder().isBinderAlive()){
            try {
                bookManager.unregisterListener(listener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        super.onDestroy();
    }
}

AIDL中可以使用的數(shù)據(jù)類型
基本數(shù)據(jù)類型(int char double boolean long)
String和CharSequence
List:只支持ArrayList并且每個元素都被aidl支持
Map:只支持HashMap并且每個元素都被aidl支持
Parcelable:所有實現(xiàn)了Pacelable接口的對象
AIDL:所有aidl接口本身也可以在aidl中使用

注意:
1 在AIDL文件中必須顯式import所使用到的Parcelable子類以及aidl
2 所有用于AIDL的Parcelable類必須創(chuàng)建一個同名的aidl文件(聲明它為Parcelable類型)
3 除了基本數(shù)據(jù)類型外其他類型的方法參數(shù)都必須標(biāo)識方向(in/out/inout)
不要圖方便全部使用inout,因為這在底層實現(xiàn)是有開銷的
Stub是服務(wù)器與Binder的中介
Proxy是客戶端與Binder的中介
多進(jìn)程間是內(nèi)存獨立的,進(jìn)程1傳遞對象A給進(jìn)程2,Binder會將對象A重新轉(zhuǎn)換生成一個新的對象。對象是不能跨進(jìn)程直接傳輸?shù)?,對象的跨進(jìn)程傳輸本質(zhì)是序列化
RemoteCallbackLsit是系統(tǒng)專門提供用于管理跨進(jìn)程Listener的,其內(nèi)部封裝一個Map

6 ContentProvider

ContentProvider 同樣可以實現(xiàn)IPC,或者換個說法。ContentProvider 本質(zhì)就是通過AIDL實現(xiàn)的。只不過它的職責(zé)專一,就是為其他應(yīng)用提供數(shù)據(jù)的

Binder連接池

在前面說到AIDL的使用及原理的時候,我們可以看到在服務(wù)端只是創(chuàng)建了一個Binder然后返回給客戶端使用而已。于是我們可以想到是不是我們可以只有一個Service,對于不同可客戶端我們只是去返回一個不同的Binder即可,這樣就避免了創(chuàng)建了大量的Service。在任玉剛的《android開發(fā)藝術(shù)探索》給出了一個Binder連接池的概念,很巧妙的避免了Service的多次創(chuàng)建。這個Binder連接池類似于設(shè)計模式中的工廠方法模式。為每一個客戶端創(chuàng)建他們所需要的Binder對象。那么下面我們看一下它是如何實現(xiàn)的

首先我定義了多個AIDL,總的就是提供BookManager與StudentManager的功能。注意,這邊還有個IBinderPool,這個的功能是提供用戶選擇具體需要的IBinder對象


這里寫圖片描述

服務(wù)端 BinderPoolService 中返回了binderPool對象,客戶端可以通過該對象的queryBinder方法獲取對應(yīng)的服務(wù)

public class BinderPoolService extends Service {

    public class BookManagerImpl extends IBookManager.Stub{

        List<Book> bs = new CopyOnWriteArrayList<>();
        {
            bs.add(new Book(1,"android",33));
        }

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public List<Book> getBooks() throws RemoteException {
            return bs;
        }
    }

    public class StudentManagerImpl extends IStudentManager.Stub{

        List<Student> ss = new CopyOnWriteArrayList<>();

        {
            ss.add(new Student(1,"lee",19));
        }

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public List<Student> getStudents() throws RemoteException {
            return ss;
        }
    }

    IBookManager bookManager = new BookManagerImpl();
    IStudentManager studentManager = new StudentManagerImpl();

    IBinderPool binderPool = new IBinderPool.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public IBinder queryBinder(int binderCode){
            switch (binderCode){
                case 1:
                    return bookManager.asBinder();
                case 2:
                    return studentManager.asBinder();
            }
            return null;
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        return binderPool.asBinder();
    }
}

客戶端的定義了 BinderPool,相當(dāng)于提供遠(yuǎn)程服務(wù)調(diào)用工具類。這邊可以考慮使用CountDownLatch將遠(yuǎn)程服務(wù)的連接這個異步操作轉(zhuǎn)為同步

public class BinderPool {

    private Context context;
    private CountDownLatch countDownLatch;
    private static BinderPool binderPool;
    private IBinderPool iBinderPool;

    private BinderPool(Context context) {
        this.context = context;
        // 連接遠(yuǎn)程線程池服務(wù)
        connectBinderPoolService();
    }

    public static BinderPool getInstance(Context context) {
        if (binderPool == null) {
            synchronized (String.class) {
                if (binderPool == null)
                    binderPool = new BinderPool(context);
            }
        }
        return binderPool;
    }

    private synchronized void connectBinderPoolService() {
        // countDownLatch = new CountDownLatch(1);
        Intent intent = new Intent("com.itszt.lwwp");
        context.bindService(ServiceIntentUtil.createExplicitFromImplicitIntent(context, intent),
                conn, Service.BIND_AUTO_CREATE);
//        try {
//            // countDownLatch.await();
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
    }

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            iBinderPool = IBinderPool.Stub.asInterface(iBinder);
            // countDownLatch.countDown();
            if (null != iBinderPool) {
                try {
                    iBinderPool.asBinder().linkToDeath(deathRecipient, 0);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

    private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            if (iBinderPool != null) {
                iBinderPool.asBinder().unlinkToDeath(this, 0);
                iBinderPool = null;
                // 重連
                connectBinderPoolService();
            }
        }
    };

    public IBinder queryBinder(int binderCode) {
        try {
            if (iBinderPool != null)
                return iBinderPool.queryBinder(binderCode);
            return null;
        } catch (RemoteException e) {
            e.printStackTrace();
            return null;
        }
    }
}

客戶端通過BinderPool調(diào)起遠(yuǎn)程服務(wù)

    new Thread(){
            @Override
            public void run() {
                binderPool = BinderPool.getInstance(MainActivity.this);
            }
        }.start();

    public void onTest(View view) {
        if(binderPool == null) return;
        IBookManager bookManager = IBookManager.Stub.asInterface(binderPool.queryBinder(1));
        IStudentManager studentManager = IStudentManager.Stub.asInterface(binderPool.queryBinder(2));
        try {
            Toast.makeText(this, "bookManager.getBooks():" + bookManager.getBooks().toString(), Toast.LENGTH_SHORT).show();
            Toast.makeText(this, "studentManager.getBooks():" + studentManager.getStudents().toString(), Toast.LENGTH_SHORT).show();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

總結(jié)

IPC是什么?是跨進(jìn)程通信
AIDL是什么?是IPC方式的一種
IPC為什么會導(dǎo)致那么多問題?內(nèi)存獨立
我們在什么時候需要IPC?某功能需要運行在獨立進(jìn)行;提升應(yīng)用分配內(nèi)存;應(yīng)用間交互

源碼:http://download.csdn.net/download/javonlee/9931448

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容