Android IPC进程间通信(从应用层源码分)《一》

在Android系统中,里面涉及到大量的进程间通信(IPC),现在我想自己写一篇关于我对IPC的理解,理解有不对的地方还希望大家给指点出来,IPC我打算分两篇来写,第一篇主要介绍怎么自己写一个进程间通信及AIDL,第二篇就从应用层源码分析,进程间是怎么绑定Binder进行进程间通信的。
首先我们要知道Binder这个概念,到底什么事Binder,其实说简单点,它就是进程与进程间进行通信的一个桥梁。
在通常的情况下,我们写一个AIDL,首先会定义一个AIDL接口,我们就叫他CalculateInterface.aidl类,里面定义了

package com.example.aidl.calculate;

interface CalculateInterface {
     double doCalculate(double a, double b);
 }

然后他就会在工程目录的gen下面产生一个同样类的名词.Java文件,点进去如下:

package com.example.aidl.calculate;

public interface CalculateInterface extends android.os.IInterface {
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements
            com.example.aidl.calculate.CalculateInterface {
        private static final java.lang.String DESCRIPTOR = "com.example.aidl.calculate.CalculateInterface";

        /** Construct the stub at attach it to the interface. */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an
         * com.example.aidl.calculate.CalculateInterface interface, generating a
         * proxy if needed.
         */
        public static com.example.aidl.calculate.CalculateInterface asInterface(
                android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.example.aidl.calculate.CalculateInterface))) {
                return ((com.example.aidl.calculate.CalculateInterface) iin);
            }
            return new com.example.aidl.calculate.CalculateInterface.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_doCalculate: {
                data.enforceInterface(DESCRIPTOR);
                double _arg0;
                _arg0 = data.readDouble();
                double _arg1;
                _arg1 = data.readDouble();
                double _result = this.doCalculate(_arg0, _arg1);
                reply.writeNoException();
                reply.writeDouble(_result);
                return true;
            }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements
                com.example.aidl.calculate.CalculateInterface {
            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 double doCalculate(double a, double b)
                    throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                double _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeDouble(a);
                    _data.writeDouble(b);
                    mRemote.transact(Stub.TRANSACTION_doCalculate, _data,
                            _reply, 0);
                    _reply.readException();
                    _result = _reply.readDouble();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_doCalculate = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    public double doCalculate(double a, double b)
            throws android.os.RemoteException;
}

每一个AIDL生成的java文件,都必须有一个纯根(Stud)和一个代理类Proxy,在进程通讯中,Proxy主要负责发送数据,Stub主要负责接收数据,打一个比方,我们客户端发送数据的时候,是通过客户端的Proxy发送数据给服务端,然后服务端在Stub中就会回调onTransact方法,将数据读取出来,调用相应的方法,这样就完成了两个进程的通讯。
接下来我们自己写一个AIDL,不需要写AIDL文件和生成的代码,这样可以更好的看清楚两个进程是怎么发送数据和接受数据的。
我们首先新建一个接口,我取名叫IBookManager,继承IInterface这个接口,和写AIDL这个接口有点像,这里面也是写

public interface IBookManager extends IInterface{

    //这是进程通讯的域名
    static final String DESCRIPTOR = "com.example.aidl.calculate.IBookManager";

    //给接口里面的方法定义一个ID,因为进程通讯里面,服务端是不认识我们所传的方法名,只认识ID,通过ID查找相应的方法。
    static final int TRANSACTION_getBook = IBinder.FIRST_CALL_TRANSACTION+1;

    //接口方法
    public double getBook(double a1,double a2) throws RemoteException;

    //BookManagerImpl相当于AIDL中的纯根(Stub)
    public class BookManagerImplStub extends Binder implements IBookManager{

        //初始化绑定信息
        public BookManagerImplStub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        //这个相当于Stub中的代理类
        //官方解释:一个内部对象到一个com.example.aidl.calculateIBookManager接口,生成一个
        //如果需要代理。
        public static IBookManager asInterface(IBinder iBinder){
            if(iBinder == null){
                return null;
            }
            //这里是判断是否在同一个进程,如果在就把自身给反回,如果不是就交给Prixy代理类
            android.os.IInterface iin = iBinder.queryLocalInterface(DESCRIPTOR);
            if((iin != null) && (iin instanceof IBookManager)){
                return (IBookManager)iin;
            }
            return new Proxy(iBinder);
        }

        @Override
        public IBinder asBinder() {
            return this;
        }

        @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply,
                int flags) throws RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION:{
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                //这个就是在服务端接受到的消息ID,通过消息ID查找相应的方法
                case TRANSACTION_getBook:{
                    data.enforceInterface(DESCRIPTOR);
                    //这些取数据底层都是使用NDK,C/C++写的。
                    double a1;
                    a1 = data.readDouble();
                    double a2;
                    a2 = data.readDouble();
                    //这里是得到值回调getBook方法
                    double result = this.getBook(a1, a2);
                    reply.writeNoException();
                    reply.writeDouble(result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        @Override
        public double getBook(double a1, double a2) throws RemoteException {
            return a1 + a2;
        }

        //这个类部类中很重要的成员,负责发送数据
        public static class Proxy implements IBookManager{

            private IBinder iBinder;
            public Proxy(IBinder iBinder){
                this.iBinder = iBinder;
            }
            @Override
            public IBinder asBinder() {
                return iBinder;
            }
            @Override
            public double getBook(double a1, double a2) throws RemoteException {
                Parcel data = Parcel.obtain();
                Parcel reply = Parcel.obtain();
                double result = 0;
                try {
                    //验证通讯的Token,为了安全
                    data.writeInterfaceToken(DESCRIPTOR);
                    //写入数据
                    data.writeDouble(a1);
                    data.writeDouble(a2);
                    iBinder.transact(TRANSACTION_getBook, data, reply, 0);
                    reply.readException();
                    result = reply.readDouble();
                } catch (Exception e) {
                    reply.recycle();
                    data.recycle();
                }

                return result;
            }

        }
    }

}

这代码咋一看,怎么和自动生成的代码一摸一样,确实是一摸一样,这里只是为了让大家看得更清楚一点,代码里面的注释还是写的比较详细,相信大家也能看懂,现在我说下怎么使用吧,把IBookManager这个接口类在客户端和服务端要保证一摸一样。
客户端使用的方法。

public class MainActivity extends Activity {
     private Button btnCalculate;
     private EditText etNum1;
     private EditText etNum2;
     private TextView tvResult;
     private IBookManager mService;
     private ServiceConnection mServiceConnection = new ServiceConnection() {

         @Override
         public void onServiceDisconnected(ComponentName name) {
             mService = null;
         }

         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
             mService = IBookManager.BookManagerImplStub.asInterface(service);
             try {
                 //在客户端链接服务端成功了,设置一个死亡代理,就是当服务端死亡之后会回调这么方法
                service.linkToDeath(recipient, 0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
         }
   };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent("com.example.aidldemoserver.BookService");
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
         etNum1 = (EditText) findViewById(R.id.et_num_one);
         etNum2 = (EditText) findViewById(R.id.et_num_two);

         tvResult = (TextView) findViewById(R.id.tv_result);
         btnCalculate = (Button) findViewById(R.id.btn_cal);

         btnCalculate.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 logE("开始远程运算");
                 try {
                     double num1 = Double.parseDouble(etNum1.getText().toString());
                     double num2 = Double.parseDouble(etNum2.getText().toString());
                     String answer = "计算结果:" + mService.getBook(num1, num2);
                     tvResult.setText(answer);
                 } catch (RemoteException e) {
                 }
             }
         });
    }
}

服务端的使用方法:

public class BookService extends Service {

     private static final String TAG = "CalculateService";

     @Override
     public IBinder onBind(Intent arg0) {
         return implStub;
     }

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


     @Override
     public boolean onUnbind(Intent intent) {
         return super.onUnbind(intent);
     }

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

     public IBookManager.BookManagerImplStub implStub = new IBookManager.BookManagerImplStub();
 }

大致的流程就介绍到这里,有说得不对的地方还希望大家给指点出来,我会继续努力的,下篇我将从源码中介绍进程是怎么通信的。
Android IPC进程间通信(从应用层源码分)《二》

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值