对IPC机制一点认识

什么是IPC机制

IPC为Inter-Process Communication的缩写,含义为进程间的通信或者跨进程通信。

为什么使用IPC机制

  • 获取到更多的内存

在Android系统中一个应用默认只有一个进程,每个进程都有自己独立的资源和内存空间,其它进程不能任意访问当前进程的内存和资源,系统给每个进程分配的内存会有限制。如果一个进程占用内存超过了这个内存限制,就会报OOM的问题,很多涉及到大图片的频繁操作或者需要读取一大段数据在内存中使用时,很容易报OOM的问题。

  • 实现数据的共享

Android中常见的IPC方式

  • Bundle:使用Intent传递Bundle数据
  • 文件共享:两个进程通过读/写同一个文件来交换数据
  • Messager:在不同进程中传递Message对象,将数据存放在Message对象中
  • AIDL:一种IDL语言,用于生成Android设备上两个进程之间通信的代码
  • ContentProvider:Android中提供的专用于不同应用间进行数据共享的方式
  • Socket:通过Socket实现进程之间的通信
image.png

如何使用AIDL实现IPC

  1. 创建AIDL接口:
// IMyAidlInterface.aidl
package com.example.lq.ipcdemo;

interface IMyAidlInterface {
    int findFactorialService(int x);
}
  1. 创建客户端:
private ServiceConnection serviceConnection;
private IMyAidlInterface iMyAidlInterface;

//创建服务连接
serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        //获取到IMyAidlInterface实例对象
        iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
        //调用iMyAidlInterface中的方法
        int result = iMyAidlInterface.findFactorialService(10);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        iMyAidlInterface = null;
    }
};
  1. 创建服务端:
public class MyService extends Service {
    //创建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;
            }
            //判断服务端与客户端是否在同一进程
            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;
                    //解析获取参数
                    _arg0 = data.readInt();
                    //调用实现方法
                    int _result = this.findFactorialService(_arg0);
                    //写入结果到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 {
                    //将描述符和参数写入_data中
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(x);
                    //调用底层的transact方法将结果写入_reply
                    mRemote.transact(Stub.TRANSACTION_findFactorialService, _data, _reply, 0);
                    //解析并返回结果
                    _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;
}

实际上,其内部主要含有两个核心内部类Stub和Proxy。若客户端和服务端位于同一进程,则返回服务端的Stub对象本身,否则返回的是系统封装后的Stub.Proxy对象。


image.png

transact:客户端发送跨进程请求,将参数传递进去

onTransact:监听到客户端的请求,服务端会通过系统封装后交由方法处理,传入data参数,获取到reply结果。

transact与onTransact之间的关系:


image.png

客户端调用服务端方法流程图:


image.png
实现AIDL双向通信:服务端定时向客户端发送消息
  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. 服务端类
public class MyService extends Service {
    //创建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);
        }
    };

    //通知所有连接服务的客户端
    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实现

ContentProvider是Android中提供的专门用于不同应用之间进行数据共享的方式,系统预制了许多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);
        //发送给Binder服务端
        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());
            //调用服务端实现的query方法
            Cursor cursor = query(callingPkg, url, projection, selection, selectionArgs,
                    sortOrder, cancellationSignal);
            if (cursor != null) {
                CursorToBulkCursorAdaptor adaptor = null;
                try {
                    //创建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的优缺点与适用场景
image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容