从AIDL看Android跨进程通信

来源:互联网 发布:linux有什么用途 编辑:程序博客网 时间:2024/05/18 08:39

  AIDL是Android实现IPC的一种重要的方式,理解它的原理对理解Android进程间通信有很大的帮助。AIDL的定义,已经有很多介绍的文章了,这里就不做详解了。我们直接从实例入手来分析AIDL实现原理。
  AIDL的使用
  首先需要定义AIDL接口IMyService.aidl:
  

// IMyService.aidlpackage com.chuck.aidldemo;// Declare any non-default types here with import statementsinterface IMyService {    /**     * Demonstrates some basic types that you can use as parameters     * and return values in AIDL.     */     String getValue();}

  定义了getValue()方法,返回String。我们知道定义好aidl文件后,IDE在编译项目时会自动帮我们生成一个文件IMyService.java文件,稍后会详细介绍。
  接下来需要定义一个service,这里定义MyService.java
  

public class MyService extends Service {    @Override    public void onCreate() {        super.onCreate();        Log.e("myService","onCreate"    );    }    @Nullable    @Override    public IBinder onBind(Intent intent) {        return ims;    }    IMyService.Stub ims=new IMyService.Stub() {        @Override        public String getValue() throws RemoteException {            return   "hello AIDL";        }    };    @Override    public void onDestroy() {        super.onDestroy();        Log.e("myService","onDestroy");    }}

  重写onBind方法,返回IMyService.Stub对象ims,IMyService.Stub是定义在IMyService.java中,其实现了IBinder,所以这里可以在OnBinder中作为返回对象。同时在AIDL中定义getValue方法的真正实现,就是在这里。我们仅仅是返回一个”hello AIDL”字符串。
  为了实现跨进程,我们还需要在AndroidMenifast.xml文件中设置process=”:Remote”,这样service就和client不在同一个进程中了:
  

<service            android:name=".MyService"            android:process=":Remote" />

  最后在定义client,为了方便我们就直接在MainActivity.java实现了:
  

public class MainActivity extends AppCompatActivity {    private IMyService ims;    private String text;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }    public void onClick(View view) {      if (view.getId() == R.id.btn_bind) {            Intent intent = new Intent(this, MyService.class);            bindService(intent, sc, BIND_AUTO_CREATE);        }else if (view.getId()==R.id.btn_unbind){            unbindService(sc);        }    }    private ServiceConnection sc = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            ims = IMyService.Stub.asInterface(service);            try {                text = ims.getValue();                Log.e("text",text);            } catch (RemoteException e) {                e.printStackTrace();            }        }        @Override        public void onServiceDisconnected(ComponentName name) {        }    };}

  这和平时bindService使用是一样的,需要一个ServiceConnection实例,将该实例作为bindService参数传入。这样client就可以启动并绑定MyService了。
  一般我们使用AIDL的基本步骤就是这些,现在我们需要着重分析自动生成的IMyService.java了,如果不知道文件位置,直接在IDE搜索一下就可以了。先把代码贴出来:
  

/* * This file is auto-generated.  DO NOT MODIFY. * Original file: /Users/chenkai/Android/codeExcise/aidl/AidlDemo/app/src/main/aidl/com/chuck/aidldemo/IMyService.aidl */package com.chuck.aidldemo;// Declare any non-default types here with import statementspublic interface IMyService extends android.os.IInterface {    /**     * Local-side IPC implementation stub class.     */    public static abstract class Stub extends android.os.Binder implements com.chuck.aidldemo.IMyService {        private static final java.lang.String DESCRIPTOR = "com.chuck.aidldemo.IMyService";        /**         * Construct the stub at attach it to the interface.         */        public Stub() {            this.attachInterface(this, DESCRIPTOR);        }        /**         * Cast an IBinder object into an com.chuck.aidldemo.IMyService interface,         * generating a proxy if needed.         */        public static com.chuck.aidldemo.IMyService asInterface(android.os.IBinder obj) {            if ((obj == null)) {                return null;            }            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);            if (((iin != null) && (iin instanceof com.chuck.aidldemo.IMyService))) {                return ((com.chuck.aidldemo.IMyService) iin);            }            return new com.chuck.aidldemo.IMyService.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_getValue: {                    data.enforceInterface(DESCRIPTOR);                    java.lang.String _result = this.getValue();                    reply.writeNoException();                    reply.writeString(_result);                    return true;                }            }            return super.onTransact(code, data, reply, flags);        }        private static class Proxy implements com.chuck.aidldemo.IMyService {            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;            }            /**             * Demonstrates some basic types that you can use as parameters             * and return values in AIDL.             */            @Override            public java.lang.String getValue() throws android.os.RemoteException {                android.os.Parcel _data = android.os.Parcel.obtain();                android.os.Parcel _reply = android.os.Parcel.obtain();                java.lang.String _result;                try {                    _data.writeInterfaceToken(DESCRIPTOR);                    mRemote.transact(Stub.TRANSACTION_getValue, _data, _reply, 0);                    _reply.readException();                    _result = _reply.readString();                } finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }        }        static final int TRANSACTION_getValue = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);    }    /**     * Demonstrates some basic types that you can use as parameters     * and return values in AIDL.     */    public java.lang.String getValue() throws android.os.RemoteException;}

  代码结构很清晰,先来看一下类图:
  
  
  1.IInterface,我们定义的Binder接口都需要继承自它。
  2.我们定义的IMyService接口继承自接口IInterface。
  3.IBinder 远程对象的接口。
  4.Binder实现了IBinder,Android通过Binder进行IPC
  5.BinderProxy是在定义在Binder.java中的,也是实现了IBinder接口,这里只要知道它是Binder的代理就可以了。我们Client中拿到的就是这个类的对象,之后会有讲。
  6.IMyService.Stub继承自Binder并且实现了IMyService。也就是说Stub其实是个Binder。还记得吗?在前文MyService中我们生成了一个Stub的实例ims,Stub的本意是存根,这里的用意就是Service的存根,通过它,我们在Service端就拥有了一个Binder。这样我们就可以通过底层的Binder驱动进行跨进程通信了。
  7.IMyService.Proxy,顾名思义它是一个代理,主要是实现了IMyService接口,客户端通过它发起远程请求。
  简单的介绍了涉及到的类,下面来分析一下请求的流程。
  我们知道,在client bindService(这是一个比较复杂的过程,涉及到AMS,本身也是一个跨进程的通信)之后,如果远程服务,这里是MyService,处理完请求之后,通过一系列复杂的操作,client中的onServiceConnected方法将会回调,为了方便我把前边相关的部分代码放在这:
  

private ServiceConnection sc = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            ims = IMyService.Stub.asInterface(service);            try {                text = ims.getValue();                Log.e("text",text);            } catch (RemoteException e) {                e.printStackTrace();            }        }        @Override        public void onServiceDisconnected(ComponentName name) {        }    };

  第3行当回调onServiceConnected时,会传过来一个IBinder对象service,client就是需要通过它来发起远程请求的,那么service的具体实现到底是Binder还是BinderProxy呢,如果对Binder的机制比较清楚的都会知道其实是BinderProxy的,为了验证这个我们可以调试一下:
  这里写图片描述

  我们得到BinderProxy后会将其传给IMyService.Stub.asInterface(service),asInterface接受一个IBinder对象作为参数,这里就是刚才说的BinderProxy对象。具体看看asInterface做了什么:
  

 public static com.chuck.aidldemo.IMyService asInterface(android.os.IBinder obj) {            if ((obj == null)) {                return null;            }            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);            if (((iin != null) && (iin instanceof com.chuck.aidldemo.IMyService))) {                return ((com.chuck.aidldemo.IMyService) iin);            }            return new com.chuck.aidldemo.IMyService.Stub.Proxy(obj);        }

  看第5行,因为我们设置了MyService的process属性,也就是说他和client是不在同一个进程的,所以obj.queryLocalInterface(DESCRIPTOR)这个方法返回为空,也就是iin为null,那么将会执行第9行代码,对没错new了一个IMyService.Proxy对象。回到前边onServiceConnected方法中第4行ims就是刚刚生成的IMyService.Proxy对象。第6行ims.getValue()方法执行的就是IMyService.Proxy中的getValue方法:
  

/**             * Demonstrates some basic types that you can use as parameters             * and return values in AIDL.             */            @Override            public java.lang.String getValue() throws android.os.RemoteException {                android.os.Parcel _data = android.os.Parcel.obtain();                android.os.Parcel _reply = android.os.Parcel.obtain();                java.lang.String _result;                try {                    _data.writeInterfaceToken(DESCRIPTOR);                    mRemote.transact(Stub.TRANSACTION_getValue, _data, _reply, 0);                    _reply.readException();                    _result = _reply.readString();                } finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }

  第7.8行_data, _reply分别是进行远程调用时的的请求和相应参数。第11行将DESCRIPTOR = “com.chuck.aidldemo.IMyService”作为Token写入 _data.第12行mRemote.transact(Stub.TRANSACTION_getValue, _data, _reply, 0);mRemote是在IMyService.Proxy构造方法中被赋值的,也就是我们在new IMyService.Proxy对象时传进来的BinderProxy对象。所以mRemote.transact(Stub.TRANSACTION_getValue, _data, _reply, 0)的实现应该是在BinderProxy中,这里需要注意一下第一个参数Stub.TRANSACTION_getValue,他是标识需要调用方法的code,在后边会有用到。我们看看他的源码:
  

 public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {        Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");        return transactNative(code, data, reply, flags);    }

  最终会调用transactNative()方法,这是一个native方法,真正的实现是用c实现,他是实现在Android源码对应/frameworks/base/core/jni/android_util_Binder.cpp中的static jboolean android_os_BinderProxy_transact函数,将java层的transact转换成c层transact,有兴趣的可以去研究一下。
  通过底层Binder的一系列操作,最终会回调到远程Stub的onTransact方法,至于怎么调用的,需要去了解Binder机制。这里只要知道会回调onTransact()方法就好了。不过还是要注意的是,这里的Stub是在远程服务端的,他和client不是在同一个进程中的。他是在远程服务的Binder线程池中。跟进去看onTransact()方法做了什么处理:
  

 @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_getValue: {                    data.enforceInterface(DESCRIPTOR);                    java.lang.String _result = this.getValue();                    reply.writeNoException();                    reply.writeString(_result);                    return true;                }            }            return super.onTransact(code, data, reply, flags);        }

  还记得在transact方法时提到的第一个参数Stub.TRANSACTION_getValue吗,它就是这里onTransact的第一个参数code。所以会执行8-13行代码,第10行java.lang.String _result = this.getValue();很关键这个this是Stub,还记得我们在哪儿有生成它的实例吗?没错就是在MyService中,我们把它作为onBind方法的返回参数。所以这里的this.getValue就是MyService中我们写的那个getValue方法,会返回一个”hello AIDL”的字符串。并把它赋值给_result。在第12行将其写入_reply中。最后在通过Binder的一系列操作,我们将在client中得到这个字符串。
  总的来说AIDL本身使用比较简单,理解起来也比较简单,但是其内部的Binder机制还是比较复杂的,我们先理解Java层的AIDL对学习Binder也是有帮助的。并且Messager通信是基于AIDL的,理解了AIDL也就理解了Messager。

0 0
原创粉丝点击