Android中的IPC方式

2019-03-04  本文已影响0人  ana生

使用Bundle

我们知道,四大组件中的三大组件(Activity,Service,Broadcast)都是支持在Intent中传输Bundle数据的,由于Bundle实现了Parcelable接口,所以他可以很方便的在进程间传输,通过Bundle我们不就可以传输基本类型,也可以传输实现了Parcelable接口或者Serializable接口的对象

使用文件共享

接下来简单的举个栗子示范一下在本进程中写数据:

Data data = new Data(10,"这是主进程写的数据");
            try {
                ObjectOutputStream out = new ObjectOutputStream(
                        new FileOutputStream("/storage/emulated/0/1/sina/sdadas.txt"));
                out.writeObject(data);
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
                Log.d(TAG, "ssss: 序列化的时候失败" + e.toString());
            }

在另一进程中读数据

Data data = null;
            try {
                ObjectInputStream in = new ObjectInputStream(
                        new FileInputStream("/storage/emulated/0/1/sina/sdadas.txt"));
                data = (Data) in.readObject();
            }catch (IOException e){
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }

            if(data != null){
                Log.d(TAG, "onClick: 读取成功 , data.id = "  + data.id + "data.name = " + data.name);
            }else {
                Log.d(TAG, "onClick: 错误");
            }

这是正常并且操作成功的,说明这种方式是可行的,不过我们这里虽然说的是传输数据,只不过我们保证的是内容一样,他们实际上还是两个不同的对象,通过这种方式,我们应尽量控制读写时的时序问题

使用Messenger

从上构造方法我们可以很容易想到底层是AIDl的
Messenger的用法非常简单,他对AIDL做了封装,使得我们可以更简单的跨进程通信
同时,由于他一次处理一个请求,因此在服务端我们不同考虑线程同步的问题,这是因为服务端不承诺在并发执行的问题,下面我们来看看怎么实现一个Messenger

我们首先来看看服务端进程中怎么实现
只需要在服务端创建一个Service来处理客户端的连接请求,并通过它来创建一个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 1:
                Log.d(TAG, "handleMessage: receive msg from Client " + msg.getData().getString("msg"));
                break;
        }
        super.handleMessage(msg);
    }
}
private final Messenger mMessenger = new Messenger(new Messengerhandler());

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

可以看到,因为我们在上面的Messenger构造器中发现Messenger的构造器需要传入一个handler 类型的参数,很明显这个Handler就是用来处理客户端发来的Message消息的
接下来看看客户端的代码
客户端首先要绑定Service,然后发送Message类型的消息

void bindMyMessengerService(){
    ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mService = new Messenger(service);
            sendData();
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            mService = null;
        }
    };
    Intent intent = new Intent(this,MessengerService.class);
    bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
}

void sendData(){
    Message msg = Message.obtain(null,1);
    Bundle data = new Bundle();
    data.putString("msg","这是客户端的消息");
    msg.setData(data);
    try{
        mService.send(msg);
    }catch (RemoteException e) {
        e.printStackTrace();
    }
}

这段代码相信没什么难度,不过这里问题又来了?诶?我们只是发送了消息给服务端,可是通信,通信,不是应该能互相发的吗?客户端是怎么接收服务端的呢?
接下来我们看看服务端怎么给客户端发送消息,先来看看服务端怎么修改

private static class Messengerhandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what){
            case 1:
                Log.d(TAG, "handleMessage: receive msg from Client " + msg.getData().getString("msg"));
                Messenger client = msg.replyTo;
                Message clientData = Message.obtain(null,1);
                Bundle bundle = new Bundle();
                bundle.putString("reply","嗯,我收到了你的消息,我是服务端");
                clientData.setData(bundle);
                try {
                    client.send(clientData);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
        }
        super.handleMessage(msg);
    }
}
void sendData(){
    Message msg = Message.obtain(null,1);
    msg.replyTo = mCilentMessenger;
    Bundle data = new Bundle();
    data.putString("msg","这是客户端的消息");
    msg.setData(data);
    try{
        mService.send(msg);
    }catch (RemoteException e) {
        e.printStackTrace();
    }
}
Messenger总结

使用AIDL

基本数据类型

  • String和CharSequence
  • List:只支持ArrayList,里面每个元素都必须能被AIDL支持
  • Map:只支持HashMap,里面每个元素都必须能被AIDL支持
  • Parcelable:所有实现了Parcelable接口的对象,使用时需要手动import
  • AIDL:所有的AIDL接口本身也可以在AIDL文件中使用,使用时需要手动import

这里由于在AIDL接口中使用了Book类,所以必须在前面import,另外如果AIDL文件用到了自定义的Parcelable对象,必须新建一个和他同名的AIDL文件,并在其中声明他为Parcelable类型,因为在上面的接口定义中,我们使用了Book类,所以我们必须定义一个aidl文件去声明他

// Book.aidl
package com.example.learnretrofit.LearnMoreProcess;

parcelable Book;

log信息

  onServiceConnected: query apple list ,list type is java.util.ArrayL
 onServiceConnected: apple.color = 红富士,apple.size = 10
 onServiceConnected: apple.color = 早熟苹果,apple.size = 10
 onServiceConnected: 重新接收
 onServiceConnected: apple.color = 红富士,apple.size = 10
 onServiceConnected: apple.color = 早熟苹果,apple.size = 10
 onServiceConnected: apple.color = 绿色的苹果,apple.size = 10

没毛病,我们接下来看看AIDL更多的用法

AIDL深入探索

当然不止增加这个接口,我们还需要在原来接口做改动

// IBookManager.aidl
package com.example.learnretrofit.LearnMoreProcess;

import com.example.learnretrofit.LearnMoreProcess.Apple;
import com.example.learnretrofit.LearnMoreProcess.InewDataListener;

interface IAppleManager {
List<Apple> getAppleList();
void addApple(in Apple apple);

void registerListener(InewDataListener listener);
void unRegisterListener(InewDataListener listener);    
}

AIDL解决跨进程取消注册问题

它的工作原理很简单,在他的内部有一个Map结构专门用来保存所有的AIDL回调,这个Map的key是IBinder类型,value是Callback类型

ArrayMap<IBinder, Callback> mCallbacks = new ArrayMap<IBinder, Callback>();

其中Callback中封装了真正的远程Listener,当客户端注册listener的时候,他会把这个listener信息存入mCallback中,其中key和value分别通过下面的方式获得


1.png
  • 我们必须清楚什么方法执行在主线程,什么方法执行在Binder线程池,一些耗时的或者不可知的操作尽可能放在多线程中解决
  • 涉及到UI操作的,如果当前线程非主线程,要使用Handler切换到主线程再进行操作
    还有,binder是有可能意外死亡的,为了我们程序的健壮性,我们有必要给Binder设置死亡监听

AIDL拓展

自定义权限简单说明

使用ContentProvider

上一篇 下一篇

猜你喜欢

热点阅读