對IPC機制一點認識

什么是IPC機制

IPC為Inter-Process Communication的縮寫,含義為進程間的通信或者跨進程通信尼斧。

為什么使用IPC機制

  • 獲取到更多的內(nèi)存

在Android系統(tǒng)中一個應用默認只有一個進程孤钦,每個進程都有自己獨立的資源和內(nèi)存空間扭勉,其它進程不能任意訪問當前進程的內(nèi)存和資源痘括,系統(tǒng)給每個進程分配的內(nèi)存會有限制姓蜂。如果一個進程占用內(nèi)存超過了這個內(nèi)存限制萎羔,就會報OOM的問題液走,很多涉及到大圖片的頻繁操作或者需要讀取一大段數(shù)據(jù)在內(nèi)存中使用時,很容易報OOM的問題贾陷。

  • 實現(xiàn)數(shù)據(jù)的共享

Android中常見的IPC方式

  • Bundle:使用Intent傳遞Bundle數(shù)據(jù)
  • 文件共享:兩個進程通過讀/寫同一個文件來交換數(shù)據(jù)
  • Messager:在不同進程中傳遞Message對象缘眶,將數(shù)據(jù)存放在Message對象中
  • AIDL:一種IDL語言,用于生成Android設(shè)備上兩個進程之間通信的代碼
  • ContentProvider:Android中提供的專用于不同應用間進行數(shù)據(jù)共享的方式
  • Socket:通過Socket實現(xiàn)進程之間的通信
image.png

如何使用AIDL實現(xiàn)IPC

  1. 創(chuàng)建AIDL接口:
// IMyAidlInterface.aidl
package com.example.lq.ipcdemo;

interface IMyAidlInterface {
    int findFactorialService(int x);
}
  1. 創(chuàng)建客戶端:
private ServiceConnection serviceConnection;
private IMyAidlInterface iMyAidlInterface;

//創(chuàng)建服務(wù)連接
serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        //獲取到IMyAidlInterface實例對象
        iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
        //調(diào)用iMyAidlInterface中的方法
        int result = iMyAidlInterface.findFactorialService(10);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        iMyAidlInterface = null;
    }
};
  1. 創(chuàng)建服務(wù)端:
public class MyService extends Service {
    //創(chuàng)建IBinder對象
    private IBinder binder = new IMyAidlInterface.Stub(){
        @Override
        public int findFactorialService(int x) throws RemoteException {
            int fact = 1;
            for (int i = 1; i <= x; i ++){
                fact = fact * i;
            }
            return fact;
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return binder; //返回IBinder對象
    }
}

IPC通信方式:Binder機制

簡而言之髓废,Binder機制就是Android中的一種跨進程通信方式巷懈。

Binder機制

AIDL自動生成的Java文件類

public interface IMyAidlInterface extends android.os.IInterface {
    
    public static abstract class Stub extends android.os.Binder implements com.example.lq.ipcdemo.IMyAidlInterface {
        private static final java.lang.String DESCRIPTOR = "com.example.lq.ipcdemo.IMyAidlInterface";

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        public static com.example.lq.ipcdemo.IMyAidlInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            //判斷服務(wù)端與客戶端是否在同一進程
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.lq.ipcdemo.IMyAidlInterface))) {
                return ((com.example.lq.ipcdemo.IMyAidlInterface) iin);
            }
            //跨進程通信,交給Proxy代理類處理
            return new com.example.lq.ipcdemo.IMyAidlInterface.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_findFactorialService: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    //解析獲取參數(shù)
                    _arg0 = data.readInt();
                    //調(diào)用實現(xiàn)方法
                    int _result = this.findFactorialService(_arg0);
                    //寫入結(jié)果到reply中
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.example.lq.ipcdemo.IMyAidlInterface {

            //返回一個Proxy對象
            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 int findFactorialService(int x) throws android.os.RemoteException {
                //獲取到Parcel對象
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    //將描述符和參數(shù)寫入_data中
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(x);
                    //調(diào)用底層的transact方法將結(jié)果寫入_reply
                    mRemote.transact(Stub.TRANSACTION_findFactorialService, _data, _reply, 0);
                    //解析并返回結(jié)果
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_findFactorialService = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    public int findFactorialService(int x) throws android.os.RemoteException;
}

實際上慌洪,其內(nèi)部主要含有兩個核心內(nèi)部類Stub和Proxy顶燕。若客戶端和服務(wù)端位于同一進程凑保,則返回服務(wù)端的Stub對象本身,否則返回的是系統(tǒng)封裝后的Stub.Proxy對象涌攻。


image.png

transact:客戶端發(fā)送跨進程請求欧引,將參數(shù)傳遞進去

onTransact:監(jiān)聽到客戶端的請求,服務(wù)端會通過系統(tǒng)封裝后交由方法處理恳谎,傳入data參數(shù)芝此,獲取到reply結(jié)果。

transact與onTransact之間的關(guān)系:


image.png

客戶端調(diào)用服務(wù)端方法流程圖:


image.png
實現(xiàn)AIDL雙向通信:服務(wù)端定時向客戶端發(fā)送消息
  1. 接口類
interface IServiceCallback {
    void notifyClient(String msg);
}

import com.example.lq.ipcdemo.IServiceCallback;
interface IMyAidlInterface {
    int findFactorialService(int x);

    void registerCallback(IServiceCallback callback);
    void unregisterCallback(IServiceCallback callback);

}
  1. 服務(wù)端類
public class MyService extends Service {
    //創(chuàng)建RemoteCallbackList列表
    private RemoteCallbackList<IServiceCallback> mCallbacks = new RemoteCallbackList<>();

    private IBinder binder = new IMyAidlInterface.Stub(){

        @Override
        public int findFactorialService(int x) throws RemoteException {
            int fact = 1;
            for (int i = 1; i <= x; i ++){
                fact = fact * i;
            }
            return fact;
        }
        //注冊
        @Override
        public void registerCallback(IServiceCallback callback) throws RemoteException {
            mCallbacks.register(callback);
        }
        //注銷
        @Override
        public void unregisterCallback(IServiceCallback callback) throws RemoteException {
            mCallbacks.unregister(callback);
        }
    };

    //通知所有連接服務(wù)的客戶端
    private void notifyMessage(String msg){
        final int len = mCallbacks.beginBroadcast();
        for (int i = 0; i < len; i ++){
            try {
                mCallbacks.getBroadcastItem(i).notifyClient(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        mCallbacks.finishBroadcast();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                notifyMessage("Hello,Client!");
            }
        }, 10000, 1000);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
}
  1. 客戶端類
IServiceCallback callback = new IServiceCallback.Stub(){

    @Override
    public void notifyClient(String msg) throws RemoteException {
        showToast(msg);
    }
};

serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
        try {
            iMyAidlInterface.registerCallback(callback);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        try {
            iMyAidlInterface.unregisterCallback(callback);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        iMyAidlInterface = null;

    }
};

ContentProvider的Binder實現(xiàn)

ContentProvider是Android中提供的專門用于不同應用之間進行數(shù)據(jù)共享的方式因痛,系統(tǒng)預制了許多ContentProvider婚苹,比如通信錄信息,日程表信息等鸵膏。

ContentProvider的query操作:

getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);

對應的transact方法:

public Cursor query(String callingPkg, Uri url, String[] projection, String selection,
        String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal)
                throws RemoteException {
    //實例化BulkCursorToCursorAdaptor對象
    BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor();
    Parcel data = Parcel.obtain();
    Parcel reply = Parcel.obtain();
    try {
        data.writeInterfaceToken(IContentProvider.descriptor);
        data.writeString(callingPkg);
        url.writeToParcel(data, 0);
        int length = 0;
        if (projection != null) {
            length = projection.length;
        }
        data.writeInt(length);
        for (int i = 0; i < length; i++) {
            data.writeString(projection[i]);
        }
        data.writeString(selection);
        if (selectionArgs != null) {
            length = selectionArgs.length;
        } else {
            length = 0;
        }
        data.writeInt(length);
        for (int i = 0; i < length; i++) {
            data.writeString(selectionArgs[i]);
        }
        data.writeString(sortOrder);
        data.writeStrongBinder(adaptor.getObserver().asBinder());
        data.writeStrongBinder(cancellationSignal != null ? cancellationSignal.asBinder() : null);
        //發(fā)送給Binder服務(wù)端
        mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0);

        DatabaseUtils.readExceptionFromParcel(reply);
        if (reply.readInt() != 0) {
            BulkCursorDescriptor d = BulkCursorDescriptor.CREATOR.createFromParcel(reply);
            adaptor.initialize(d);
        } else {
            adaptor.close();
            adaptor = null;
        }
        return adaptor;
    } catch (RemoteException ex) {
        adaptor.close();
        throw ex;
    } catch (RuntimeException ex) {
        adaptor.close();
        throw ex;
    } finally {
        data.recycle();
        reply.recycle();
    }
}

對應的onTranct方法:

public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
    throws RemoteException {
    switch (code) {
        case QUERY_TRANSACTION:{
            data.enforceInterface(IContentProvider.descriptor);
            String callingPkg = data.readString();
            Uri url = Uri.CREATOR.createFromParcel(data);

            int num = data.readInt();
            String[] projection = null;
            if (num > 0) {
                projection = new String[num];
                for (int i = 0; i < num; i++) {
                    projection[i] = data.readString();
                }
            }

            String selection = data.readString();
            num = data.readInt();
            String[] selectionArgs = null;
            if (num > 0) {
                selectionArgs = new String[num];
                for (int i = 0; i < num; i++) {
                    selectionArgs[i] = data.readString();
                }
            }

            String sortOrder = data.readString();
            IContentObserver observer = IContentObserver.Stub.asInterface(
                    data.readStrongBinder());
            ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(
                    data.readStrongBinder());
            //調(diào)用服務(wù)端實現(xiàn)的query方法
            Cursor cursor = query(callingPkg, url, projection, selection, selectionArgs,
                    sortOrder, cancellationSignal);
            if (cursor != null) {
                CursorToBulkCursorAdaptor adaptor = null;
                try {
                    //創(chuàng)建CursorToBulkCursorAdaptor對象
                    adaptor = new CursorToBulkCursorAdaptor(cursor, observer,
                            getProviderName());
                    cursor = null;

                    BulkCursorDescriptor d = adaptor.getBulkCursorDescriptor();
                    adaptor = null;

                    reply.writeNoException();
                    reply.writeInt(1);
                    d.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                } finally {
                    if (adaptor != null) {
                        adaptor.close();
                    }
                    if (cursor != null) {
                        cursor.close();
                    }
                }
            } else {
                reply.writeNoException();
                reply.writeInt(0);
            }
            return true;
        }
        ...
    }
}
IPC的優(yōu)缺點與適用場景
image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末租副,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子较性,更是在濱河造成了極大的恐慌,老刑警劉巖结胀,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件赞咙,死亡現(xiàn)場離奇詭異,居然都是意外死亡糟港,警方通過查閱死者的電腦和手機攀操,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來秸抚,“玉大人速和,你說我怎么就攤上這事“溃” “怎么了颠放?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長吭敢。 經(jīng)常有香客問我碰凶,道長,這世上最難降的妖魔是什么鹿驼? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任欲低,我火速辦了婚禮,結(jié)果婚禮上畜晰,老公的妹妹穿的比我還像新娘砾莱。我一直安慰自己,他們只是感情好凄鼻,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布腊瑟。 她就那樣靜靜地躺著聚假,像睡著了一般。 火紅的嫁衣襯著肌膚如雪扫步。 梳的紋絲不亂的頭發(fā)上魔策,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音河胎,去河邊找鬼闯袒。 笑死,一個胖子當著我的面吹牛游岳,可吹牛的內(nèi)容都是我干的政敢。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼胚迫,長吁一口氣:“原來是場噩夢啊……” “哼喷户!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起访锻,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤褪尝,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后期犬,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體河哑,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年龟虎,在試婚紗的時候發(fā)現(xiàn)自己被綠了璃谨。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡鲤妥,死狀恐怖佳吞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情棉安,我是刑警寧澤底扳,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站垂券,受9級特大地震影響花盐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜菇爪,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一算芯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧凳宙,春花似錦熙揍、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽有梆。三九已至,卻和暖如春意系,著一層夾襖步出監(jiān)牢的瞬間泥耀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工蛔添, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留痰催,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓迎瞧,卻偏偏與公主長得像夸溶,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子凶硅,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容