详解 Android 中的 IPC 机制:基础篇

前言

本文主要介绍 Android 中的 IPC 机制,具体实现的方式有很多,比如可以通过在 Intent 中附加 extras 来传递信息,或者通过共享文件的方式来共享数据。Android 开发中,还经常用到 Binder 方式来实现跨进程通信。四大组件之一 ContentProvider 天生就是支持跨进程访问的,所以我们也可以用它来进行 IPC。通过网络通信也可以实现数据传递,所以 Socket 也可以用来进行 IPC。以上几种方法都能实现 IPC,我们需要根据具体的情况选择不同的方式实现 IPC。

使用 Bundle

Android 开发四大组件中的三大组件(Activity、Service、Receiver)都是支持在 Intent 中传递 Bundle 数据的,因为 Bundle 实现了 Parcelable 接口,所以它可以方便地在不同的进程间传输。我们可以在 Bundle 中附加我们需要跨进程传递的数据并通过 Intent 发送出去。使用 Bundle 传递的数据必须是能够被序列化的,比如基本类型、实现 Parcelable 接口的对象、实现了 Serializable 接口的对象等。这是最简单的进程间通信方式。

使用共享文件

两个进程可以通过对同一个文件进行读写的方式来交换数据,比如进程 A 将想传递的数据写入一个文件,进程 B 通过读取同一个文件来获取数据。这种方式需要注意一个问题就是 Android 中,两个线程可以同时对一个文件进行读写,这样可能会出现问题。但是因为这种 IPC 方式很简单,所以只要在开发时注意一下这种并发操作的情况即可。不仅如此,我们还可以通过一个进程将一个对象序列化之后写入文件,另一个进程读取文件之后恢复对象。尽管这两个对象并不是同一个对象,但是内容是一样的。

使用 Messenger

Messenger 是一种轻量级的 IPC 方案,它的底层是现实 AIDL,这一点我们可以通过观察其源码验证。Messenger 的两个构造方法代码如下,可以很明显地看出来 AIDL 的痕迹。

public Messenger(Handler target) {
        mTarget = target.getIMessenger();
}

public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
}
复制代码

Messenger 对 AIDL 做了封装,我们可以很简单的使用它进行进程间通信。 实现一个Messenger有多个步骤,分为服务端和客户端:

服务端:创建一个Service来处理客户端的连接请求,同时创建一个Handle并通过它来创建一个Messenger对象,然后再Service的onBind中返回这个Messenger对象的底层Binder即可。

public class MessengerService extends Service {
    private static final String TAG = "MessengerService";
    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MyConstants.MSG_FROM_CLIENT:
                Log.i(TAG, "receive msg from Client:" + msg.getData().getString("msg"));
                Messenger client = msg.replyTo;
                Message relpyMessage = Message.obtain(null, MyConstants.MSG_FROM_SERVICE);
                Bundle bundle = new Bundle();
                bundle.putString("reply", "收到消息啦");
                relpyMessage.setData(bundle);
                try {
                    client.send(relpyMessage);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
            default:
                super.handleMessage(msg);
            }
        }
    }

    private final Messenger mMessenger = new Messenger(new MessengerHandler());

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

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

}
复制代码

从服务端的代码中可以看到,MessageHandler用来处理客户端发过来的消息,并从客户端中取出文本信息,而 mMessenger 是和客户端关联在一起的,在 onBind 方法中返回它里面的Binder对象,这里的 Messenger 的作用是将客户端发送的消息转交给 MessengerHandler 处理。

客户端:首先绑定服务端的 Service,用返回的 IBinder 创建一个 Messenger,通过这个 Messenger 像服务器发送 Message 类型的数据,如果需要服务端能够回应客户端,同时还需要创建一个Handle并创建一个新的 Messenger,把这个 Messenger 对象通过 Message 的 replyTo 传递给服务端。

public class MessengerActivity extends Activity {
    private static final String TAG = "MessengerActivity";
    private Messenger mService;
    private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());

    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MyConstants.MSG_FROM_SERVICE:
                Log.i(TAG, "receive msg from Service:" + msg.getData().getString("reply"));
                break;
            default:
                super.handleMessage(msg);
            }
        }
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            mService = new Messenger(service);
            Log.d(TAG, "bind service");
            Message msg = Message.obtain(null, MyConstants.MSG_FROM_CLIENT);
            Bundle data = new Bundle();
            data.putString("msg", "hello, this is client.");
            msg.setData(data);
            msg.replyTo = mGetReplyMessenger;
            try {
                mService.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        public void onServiceDisconnected(ComponentName className) {
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger);
        Intent intent = new Intent("com.ryg.MessengerService.launch");
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        unbindService(mConnection);
        super.onDestroy();
    }
}
复制代码

从例子看来,在 Messenger 中进行数据传递必须是将数据放到 Message 中,实际上,Messenger 来传输 Message,Message 中能使用的载体有 what,arg1,arg2,Bundle 以及 replyTo。Message 的 object 在同一个进程中的使用是很实用的。

使用AIDL

上面介绍的 Messenger 是以串行的方式处理客户端发来的消息,如果有大量消息发送到服务端,服务端仍然只能一个个处理,如果有大量的并发请求,Messenger 就不能胜任了。此外,我们可能需要跨进程调用服务端的方法,这种情形用 Messenger 就无法做到了,这时候就需要使用 AIDL 来实现跨进程的方法调用。

  • 服务端
    服务端首先要创建一个 Service 用来监听客户端的请求连接,然后创建一个 AIDL 文件,将暴露给客户端的接口在这个 AIDL 文件中声明,在 Service 中实现这个接口。

  • 客户端
    首先绑定好服务端的 Service,将服务端返回的 Binder 对象转成 AIDL 接口所属的类型,接着调用 AIDL 中的方法。

使用 ContentProvider

ContentProvider 是 Android 提供的专门用于不同应用间数据共享的方式,所以它天生就能够用于进程间通信。ContentProvider 使用起来要比 AIDL 容易得多。系统预置了很多 ContentProvider,比如通讯录信息、本地媒体库。我们还可以通过实现自定义的 ContentProvider,并在不同进程中进行读写来实现跨进程通信。

使用 Socket

Socket 也叫做“套接字”,是网络通信里的概念。两个进程可以通过 Socket 来实现信息的传输,Socket 本身可以支持传输任意字节流,这样就实现了 IPC。

转载于:https://juejin.im/post/5c8603b6f265da2d96184597

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值