Android进程间通信(一) Binder的简单使用

一、为什么要进行进程间的通信?

进程通信指的是两个不同的进程之间进行数据交换的过程,这个进程和线程是两种完全不同的概念。

线程指的是CPU调度的最小单元,是一种有限的系统资源;而进程是一种执行单元,应用至少要有一个进程才可以运行(一个应用也可以有多个进程)。

众所周知,Java是在JVM中运行的,在Android中,系统会为每个进程分配一个虚拟机,因此,在不同的进程中,内存里数据是不能直接进行交换的,所以就有了IPC机制(Inter-Process Communication)

除了不同应用之间需要进行进程间通信外,有时候一个应用本身内部也需要进行多进程的数据交换,比如应用和后台服务的数据交换。


二、IPC要解决的问题

Android进程间通信需要解决以下几个问题:

  1. 通信目标:当前进程要调用的是哪个进程及该进程的哪个方法功能
  2. 数据交换:当前进程怎么把调用目标方法所需要的参数传递给目标进程,以及如何取回处理结果
  3. 简化操作:怎么屏蔽进程间的底层通信细节,使得当前进程调用远程方法像调用本地方法一样方便

解决方案:

  1. 使用远程通信类的唯一标识符(包名+类名)确定一个进程及其服务
  2. 使用Parcelable序列化对象进行数据交换
  3. 在被调用进程(服务端)定义一个类专门用于处理跨进程请求,这个类就是Binder类



三、Binder的使用

如Messenger,AIDL等几种进程间通信方式其实都是对Binder的封装和实现,而Binder本身也是可以直接使用的。

本文章不涉及Binder具体的理论知识(因为太难懂了...),仅示范Binder是如何使用的。

首先看Binder的运行过程


请求远程服务的进程为客户端,被请求的进程为服务端,进程之间通过Binder进行数据交换。

客户端发起远程请求并挂起(耗时操作),直至服务端返回处理结果唤醒。

Server进程:

首先来实现服务端进程

1、声明一个接口,在接口中定义服务要对外提供的方法

public interface IReport{
    //对外提供的报告方法
    int report(String values, int type);
    }

2、创建Binder,实现上面的接口

public class Report extends Binder implements IReport{
        @Override
        protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
            //接到调用,解包
            switch (code) {
                case REPORT_CODE:
                    data.enforceInterface("reporter");
                    String values = data.readString();
                    Log.i("IReporter", "data is '" + values + "'");

                    //调用对外提供的方法处理客户端传入的参数
                    int type = data.readInt();
                    int result = report(values, type);

                    //处理结果回传
                    reply.writeInterfaceToken("reporter");
                    reply.writeInt(result);
                    return true;
            }
            return super.onTransact(code, data, reply, flags);
        }

        @Override
        public int report(String values, int type) {
            return type;
        }
}

3、创建服务端,在服务端声明一个Binder,并在onBind方法中返回它

服务端完整代码如下(此处把接口和Binder都写在服务端代码中):

public class BindService extends Service {

    public static final int REPORT_CODE = 0;

    private Report mReport;

    public interface IReport{
        //对外提供的报告方法
        int report(String values, int type);
    }

    //Binder
    public final class Report extends Binder implements IReport{
        @Override
        protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
            //接到调用,解包
            switch (code) {
                case REPORT_CODE:
                    data.enforceInterface("reporter");
                    String values = data.readString();
                    Log.i("IReporter", "data is '" + values + "'");

                    //调用对外提供的方法处理客户端传入的参数
                    int type = data.readInt();
                    int result = report(values, type);

                    //处理结果回传
                    reply.writeInterfaceToken("reporter");
                    reply.writeInt(result);
                    return true;
            }
            return super.onTransact(code, data, reply, flags);
        }

        @Override
        public int report(String values, int type) {
            return type;
        }
    }

    public BindService(){
        mReport = new Report();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mReport;
    }
}


Client进程:

Client的代码比较简单,创建一个ServiceConnection,在onCreate中用这个连接绑定服务就可以进行数据交换了

private class BindConnection implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //Client组包:通过obtain获取发送包对象和应答包对象,写入数据
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            data.writeInterfaceToken("reporter");
            data.writeString("this is a test String");
            data.writeInt(0);
            try {
                //调用IBinder的transact接口按序发送数据包
                service.transact(BindService.REPORT_CODE, data, reply, 0);

                //接收处理结果
                reply.enforceInterface("reporter");//接口验证
                int result = reply.readInt();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            //资源回收
            data.recycle();
            reply.recycle();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            
        }
    }
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //绑定服务
        Intent intent = new Intent(this, BindService.class);
        bindService(intent, new BindConnection(), BIND_AUTO_CREATE);
    }


四、传输自定义对象

Binder传输的对象必须是可序列化的,如基本类型String,int等基本数据类型,以及Bundle所支持的数据类型

因此,要传输用户自定义的对象,必须在对象类中实现Parcelable接口,让对象可序列化,Parcelable实现如下所示:

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

public class Book implements Parcelable {
    public int code;
    public String name;

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.code);
        dest.writeString(this.name);
    }

    public Book() {
    }

    protected Book(Parcel in) {
        this.code = in.readInt();
        this.name = in.readString();
    }

    public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel source) {
            return new Book(source);
        }

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

之后就可以使用Bundle来进行数据交换了

Bundle bundle = new Bundle();

bundle.putParcelable("book",book);

data.writeBundle("book",bundle);

猜你喜欢

转载自blog.csdn.net/lllx9464/article/details/79797798