IPC

  • 前言
  • 跨进程通信方式
  • 跨进程通信框架
  • 涉及到的技术
  • 使用Request-Response思想
  • IPCRequest
  • IPCResponse
  • RemoteService
  • 服务端
  • 客户端
  • 附带
  • 项目依赖



android 跨进程通信oom android 跨进程通信框架_AIDL

前言

由于应用中存在多个进程,比如一个主进程,一个消息推送进程及一个应用守护进程,这使得进程间通信的开发提上日程,而为了提高开发效率,需要提炼出一套跨进程通信的基础框架,以本篇博客记录其编写过程

跨进程通信方式

目前在Android里存在哪些跨进程通信方式呢?主要有

  • 文件共享:并发要求和实时性要求低的可以使用文件共享;同时Linux机制下,可以对文件并发写,需要处理同步;Windows下不支持并发读或写
  • Bundle/Intent: 使用简单,不过只能发送Bundle支持的数据类型
  • Messenger:Messenger是基于AIDL实现的,服务端(被动方)提供一个Service来处理客户端(主动方)连接,维护一个Handler来创建Messenger,在onBind时返回Messenger的binder。双方用Messenger来发送数据,用Handler来处理数据。Messenger处理数据依靠Handler,所以是串行的,也就是说,Handler接到多个message时,就要排队依次处理
  • ContentProvider:底层也是Binder实现,主要用来为其他APP提供数据,可以说天生就是为进程通信而生的,比较适合结构化数据集的访问
  • Broadcase:一种单向的通信方式,当一个程序发送广播后,其他应用只能被动地接收,无法向发送者反馈;效率低,安全性低
  • LocalSocket:适合于高并发,实时性要求高的需求,实际使用起来坑比较多,跟你使用Socket进行网络通信一样需要处理很多东西,比如心跳,粘包,断线重连等,还有长连接对资源消耗比较大,不过功能还是挺强大的
  • AIDL:使用成本比较高,但是数据交互能力强大

跨进程通信框架

涉及到的技术

本篇文章搭建跨进程通信框架需要使用到的技术有:

  • AIDL的使用
  • 动态代理的使用
  • 运行时注解的使用
  • 反射的使用

如果大家对这四个技术不熟悉,可以翻看博主以前的文章,这些知识点都有文章进行解析

关于AIDL的使用可以参考博主的
Android四大组件之Service 远程服务解析,通过AIDL达到进程间通信交换基础数据Android四大组件之Service 远程服务 通过AIDL进行进程间复杂类型数据交换

关于动态代理的参考Android开发如何理解Java静态代理 动态代理及动态生成代理对象原理 看这篇就够了

关于反射的参考Android开发-带你玩转Android中的反射机制,再也不怕private修饰了

关于Binder的理解可以参考
从源码解析-Android中进程间通信Binder机制之Linux基础 【一】从源码解析-Android中进程间通信Binder机制之Binder驱动 【二】

使用Request-Response思想

通常情况下我们通过AIDL进行跨进程通信,最少需要编写一个aidl接口文件,比如

android 跨进程通信oom android 跨进程通信框架_IPC_02


你可以在这个文件里定义很多方法,或者将方法定义在不同的aidl文件中

某些时候你可能在方法里传的参数越来越多,然后将这些参数封装成一个对象,这时候还得编写对象对应的aidl文件,如:

parcelable Result;

当你的业务需求越来越复杂,对应的aidl接口文件和aidl序列化对象文件就越来越多,将对后期的管理和维护造成很大的困扰;同时开发人员要处理好数据的序列化操作,同时还需要定义很多的远程Service,这些无疑是我们在使用AIDL进行进程间通信的痛点

本框架的编写思路来源于网络通信,在一个HTTP请求中,有Request,有Response,客户端不需要关注具体通信的细节,只需要知道发出一个Request,最后收到一个Reponse;这两个对象将我们的请求信息和返回信息全部封装起来,这样你也就不需要再定义其它的aidl文件了,只需要三个,如下

android 跨进程通信oom android 跨进程通信框架_AIDL_03


android 跨进程通信oom android 跨进程通信框架_android 跨进程通信oom_04


只需要这三个文件,且IRemoteService里只需要定义一个方法就能搞定我们的跨进程通信

IPCRequest

这个对象封装了我们的请求信息,因为在服务端定义了一个通用的远程Service,将客户端的请求转发给相应的接口处理,这样就与业务解耦;所以客户端需要告诉服务端自己的请求参数,要求某个类型的接口处理(客户端不指定,由服务端匹配决定),接口里的哪个方法具体执行,如下

package com.mango.ipcore;

import android.os.Parcel;
import android.os.Parcelable;

import java.util.Arrays;

/**
 * Author: mango
 * Time: 2019/6/12 20:45
 * Version:
 * Desc: TODO(封装请求信息)
 */
public class IPCRequest implements Parcelable {

    //获取对象
    public static final int LOAD_INSTANCE = 1;
    //执行方法
    public static final int LOAD_METHOD = 2;
    //请求类型
    private int type;
    //服务端处理请求的类名
    private String className;
    //服务端处理请求的方法名
    private String methodName;
    //服务端处理请求的方法的参数
    private IPCParameter[] parameters;

    public IPCRequest() {}

    public IPCRequest(int type, String className, String methodName, IPCParameter[] parameters) {
        this.type = type;
        this.className = className;
        this.methodName = methodName;
        this.parameters = parameters;
    }

    protected IPCRequest(Parcel in) {
        type = in.readInt();
        className = in.readString();
        methodName = in.readString();
        Parcelable[] parcelables = in.readParcelableArray(IPCParameter.class.getClassLoader());
        if (parcelables != null) {
            parameters = Arrays.copyOf(parcelables, parcelables.length, IPCParameter[].class);
        }
    }

    public static final Creator<IPCRequest> CREATOR = new Creator<IPCRequest>() {
        @Override
        public IPCRequest createFromParcel(Parcel in) {
            return new IPCRequest(in);
        }

        @Override
        public IPCRequest[] newArray(int size) {
            return new IPCRequest[size];
        }
    };

    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public IPCParameter[] getParameters() {
        return parameters;
    }

    public void setParameters(IPCParameter[] parameters) {
        this.parameters = parameters;
    }

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(type);
        dest.writeString(className);
        dest.writeString(methodName);
        dest.writeParcelableArray(parameters,flags);
    }

    @Override
    public String toString() {
        return "IPCRequest{" +
                "type=" + type +
                ", className='" + className + '\'' +
                ", methodName='" + methodName + '\'' +
                ", parameters=" + Arrays.toString(parameters) +
                '}';
    }
}

这里面涉及到一个IPCParameter对象,它封装了具体的参数信息,客户端调用方法传入的参数最终会转化成IPCParameter传递给服务端

IPCResponse

这个类就比较简单了,封装了执行情况及返回值

package com.mango.ipcore;

import android.os.Parcel;
import android.os.Parcelable;

/**
 * Author: mango
 * Time: 2019/6/12 20:45
 * Version:
 * Desc: TODO(执行结果封装)
 */
public class IPCResponse implements Parcelable {

    //方法返回值序列化的结果
    private String result;
    //本次请求执行情况的描述信息
    private String message;
    //是否执行成功
    private boolean success;

    public IPCResponse(String result, String message, boolean success) {
        this.result = result;
        this.message = message;
        this.success = success;
    }

    protected IPCResponse(Parcel in) {
        result = in.readString();
        message = in.readString();
        success = in.readByte() != 0;
    }

    public static final Creator<IPCResponse> CREATOR = new Creator<IPCResponse>() {
        @Override
        public IPCResponse createFromParcel(Parcel in) {
            return new IPCResponse(in);
        }

        @Override
        public IPCResponse[] newArray(int size) {
            return new IPCResponse[size];
        }
    };

    public String getResult() {
        return result;
    }

    public void setResult(String result) {
        this.result = result;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public boolean isSuccess() {
        return success;
    }

    public void setSuccess(boolean success) {
        this.success = success;
    }

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(result);
        dest.writeString(message);
        dest.writeByte((byte) (success ? 1 : 0));
    }

    @Override
    public String toString() {
        return "IPCResponse{" +
                "result='" + result + '\'' +
                ", message='" + message + '\'' +
                ", success=" + success +
                '}';
    }
}

RemoteService

接下来就是远程Service了,接收客户端的请求,根据IPCRequest里的信息转发给相应的接口处理

package com.mango.ipcore.handler;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

import com.mango.ipcore.IPCCache;
import com.mango.ipcore.IPCRequest;
import com.mango.ipcore.IPCResponse;
import com.mango.ipcore.IRemoteService;
import com.mango.ipcore.utils.ParamsConvert;

import java.lang.reflect.Method;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import static com.mango.ipcore.IPCRequest.LOAD_INSTANCE;
import static com.mango.ipcore.IPCRequest.LOAD_METHOD;

/**
 * Author:mango
 * Time:2019/6/19 15:59
 * Version:1.0.0
 * Desc:TODO()
 */
public class RemoteService extends Service {

    /**
     * 后续开发 缓存客户端请求
     * 重连后将缓存数据返回
     */
    private final Deque<IPCRequest> runningRequest = new ArrayDeque<>();

    private Lock mLock = new ReentrantLock();

    private IPCCache mIpcCache ;
    {
        mIpcCache = IPCCache.getDefault();
    }

    @Override
    public IBinder onBind(Intent intent) {

        return new IRemoteService.Stub(){

            @Override
            public IPCResponse sendRequest(IPCRequest request) {
                mLock.lock();
                IPCRequest rq = request;
                executed(rq);
                switch (rq.getType()) {
                    case LOAD_INSTANCE:
                        try {
                            /**
                             * 找到对应的处理客户端请求的Class和构造方法
                             * 然后通过反射实例化对象 保存起来
                             */
                            Class<?> clazz = mIpcCache.getClass(rq.getClassName());
                            Method method = mIpcCache.getMethod(clazz, rq.getMethodName());
                            if (mIpcCache.getObject(rq.getClassName())  == null) {
                                Object object = method.invoke(null, ParamsConvert.unSerializationParams(rq.getParameters()));
                                mIpcCache.putObject(rq.getClassName(),object);
                            }
                            return new IPCResponse(null,"初始化处理对象成功",true);
                        } catch (Exception e) {
                            e.printStackTrace();
                            return new IPCResponse(e.getMessage(),"初始化处理对象失败",false);
                        }finally {
                            finished(rq);
                            mLock.unlock();
                        }
                    case LOAD_METHOD:
                        try {
                            /**
                             * 找到对应的处理客户端请求的Class和执行请求的方法
                             */
                            Class<?> cl = mIpcCache.getClass(rq.getClassName());
                            Method me = mIpcCache.getMethod(cl, rq.getMethodName());
                            Object object = mIpcCache.getObject(rq.getClassName());

                            Object[] params = ParamsConvert.unSerializationParams(rq.getParameters());
                            Object result = me.invoke(object, params);
                            String r = ParamsConvert.mGson.toJson(result);
                            return new IPCResponse(r,"执行方法成功",true);
                        } catch (Exception e) {
                            e.printStackTrace();
                            return new IPCResponse(e.getMessage(),"执行方法失败",false);
                        }finally {
                            finished(rq);
                            mLock.unlock();
                        }
                    default:
                }
                return new IPCResponse(null,"未知类型请求,请指定type",false);
            }
        };
    }

    void executed(IPCRequest request){
        runningRequest.offer(request);
    }

    void finished(IPCRequest request){
        runningRequest.remove(request);
    }

}

服务端

从RemoteService 里的处理逻辑可以看出,当客户端有请求过来时,根据其提供的信息从IPCCache缓存中找到对应的类和方法来处理请求,那么服务端就要提供这个处理类,为了后面在客户端能够使用JDK的动态代理,这里使用面向接口的方式

public interface IData {

    List sendData(String params);

    Object getStudent(String name);

}

/**
 * Author:mango
 * Time:2019/6/19 17:00
 * Version:1.0.0
 * Desc:注解值和客户端接口中的注解值保持一致
 * 需要提供一个公共静态且方法名为getDefault的方法 返回实例
 */
@RequestHandler("IData")
public class DataManager implements IData {

    private static final DataManager ourInstance = new DataManager();

    public static DataManager getDefault(String params) {
        return ourInstance;
    }

    private DataManager() {}


    @Override
    public List sendData(String params) {
        List<String> list = new ArrayList<>();
        list.add("1");
        list.add("2");
        list.add("3");
        return list;
    }

    @Override
    public Student getStudent(String name) {
        return new Student(name);
    }
}

实现类上有一个注解,类似于它的一个TAG,客户端传递这个TAG过来,服务端根据这个TAG找到对应的处理类处理请求;同时当业务需求变化时,可以提供不同的实现类,更加灵活

当然了RemoteService是从缓存中找到它的,所以在某个时间,服务端需要将实现类注册到缓存中,同时不要忘记解除注册

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        IPCMango.getDefault().register(DataManager.class);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        IPCMango.getDefault().unRegister(DataManager.class);
    }

客户端

既然服务端准备好了,那客户端要做的第一件事就是绑定服务,别忘了还有解绑

/**
   * 远程服务的应用包名
   */
private String mRemotePackageName = "com.mango.ipc";
IPCMango.getDefault().bind(this,mRemotePackageName);

IPCMango.getDefault().unBind(this);

从RemoteService可以看出来,当客户端想要发送请求时,第一件事是获取服务端对应的处理类的实例,但是因为是跨进程,不可能真的将这个实例传递过来,但是客户端又要模拟得到这个实例,去调用这个处理类提供的方法,所以客户端就需要编写一个跟处理类实现的接口相同的接口

/**
 * 注解值需要和服务端实现该接口的类注解值保持一致
 * 接口名不需要和服务端一致,只需要接口里的方法和服务端一致就行
 */
@RequestHandler("IData")
public interface IData {

    List sendData(String params);

    Object getStudent(String name);

}

这时候就可以通过动态代理技术生成一个代理对象,客户端就可以像调用普通方法一样去调用服务端的处理类,最后服务端根据注解值在缓存中找到对应的处理类处理请求

public void sendData(View v){
        //如果服务端会执行耗时操作,这里需要放在子线程
        //发送请求给框架提供的RemoteService
        if (iData == null) {
            iData = IPCMango.getDefault().loadRequestHandler(IData.class,"伙计");
        }
        if (iData != null) {
            List list = iData.sendData("小米");
            Object student = iData.getStudent("阿菜");
        }
    }
public <T> T loadRequestHandler(String serviceName, Class inter, IPCRequestError listener, Object... params){

        if (!checkService(serviceName)) {
            if (listener != null) {
                listener.sendResult(new IPCResponse("","未绑定远程Service",false));
            }
            return null;
        }

        //获取处理客户端请求的对象的Key,以此在服务端找出对应的处理者
        String className;
        RequestHandler annotation = (RequestHandler) inter.getAnnotation(RequestHandler.class);
        if (annotation == null) {
            className = inter.getName();
        } else {
            className = annotation.value();
        }
        IPCRequest ipcRequest = buildRequest(IPCRequest.LOAD_INSTANCE,className,"getDefault",params);
        /**
         * 发消息给服务端, 来实例化 处理客户端请求 的对象, 要求实例该对象的方法名为 getDefault
         * 如果构造成功 就通过动态代理构造一个实现该接口的对象并返回
         * 以接收客户端后续请求
         */
        IPCResponse ipcResponse = sendRequest(ipcRequest,serviceName);
        if (ipcResponse.isSuccess()) {
            return (T) Proxy.newProxyInstance(getClass().getClassLoader(),
                    new Class[]{inter},
                    new IPCInvocationHandler(listener,serviceName, className));
        } else {
            if (listener != null) {
                listener.sendResult(ipcResponse);
            }
        }
        return null;
    }

到此框架的简要架构就介绍完了,使用该框架,不需要再定义繁杂的aidl文件了,也不用考虑其细节,只需要像普通方法调用那样去使用就可以完成跨进程通信了

附带

当然了,如果你不使用RemoteService,你也可以在服务端自定义一个Service,然后在清单文件中注册

客户端只需要先绑定,再发送请求就可以了

/**
     * 远程服务的包名和类名
     */
    private String mRemotePackageName = "com.mango.ipc";
    private String mRemoteServiceName = "com.mango.ipc.MyRemoteService";
    IPCMango.getDefault().bind(this,mRemotePackageName,mRemoteServiceName);
    public void sendMyData(View view){
        //发送请求给服务端自定义的远程Service
        IPCResponse ipcResponse = IPCMango.getDefault().sendRequest(
                IPCMango.getDefault().buildRequest(0,null,null),
                mRemoteServiceName);
    }

项目依赖

android 跨进程通信oom android 跨进程通信框架_Android_05


android 跨进程通信oom android 跨进程通信框架_跨进程通信_06


android 跨进程通信oom android 跨进程通信框架_Android_07


android 跨进程通信oom android 跨进程通信框架_Android_08


android 跨进程通信oom android 跨进程通信框架_android 跨进程通信oom_09


详细代码和使用步骤可参考

 欢迎star

android 跨进程通信oom android 跨进程通信框架_android 跨进程通信oom_10