浅析绑定远程服务bindService基本原理与AIDL分析

来源:互联网 发布:淘宝怎么运营推广 编辑:程序博客网 时间:2024/06/04 18:44

我们一般绑定远程服务,分为客户端和服务端,并且需要使用AIDL来实现,这里主要从java层面来讲解它的基本过程,没有深入到Binder的C/C++底层的实现。
我们的通常做法是这样的:
1、创建一个客户端项目和一个服务端项目
2、在客户端项目和服务端项目相同的包下创建AIDL文件
3、实现客户端和服务端的代码

这里先给出通常的实现代码:


AIDL文件为:

interface IManager {    int add(int x , int y);    int min(int x , int y );}

客户端代码:
MainActivity.java:

public class MainActivity extends Activity {    private IManager manager;    private ServiceConnection connection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            Log.i("MyService", "onServiceConnected");            //得到本地代理对象            manager = IManager.Stub.asInterface(service);        }        @Override        public void onServiceDisconnected(ComponentName name) {        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }    public void bindService(View view) {        Log.i("MyService", "bindService");        Intent intent = new Intent("com.newintenttest.startservice.MyService");        bindService(intent, connection, Context.BIND_AUTO_CREATE);    }    public void unbindService(View view) {        unbindService(connection);    }    public void caculate(View view){        if (manager == null) {            Toast.makeText(this, "请绑定服务", Toast.LENGTH_SHORT).show();        } else {            int result = 0;            try {                result = manager.add(4, 5);            } catch (Exception e) {                e.printStackTrace();            }            Toast.makeText(this, result + "",Toast.LENGTH_SHORT).show();        }    }}

layout布局文件:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"        xmlns:tools="http://schemas.android.com/tools"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:orientation="vertical"        android:paddingLeft="@dimen/activity_horizontal_margin"        android:paddingRight="@dimen/activity_horizontal_margin"        android:paddingTop="@dimen/activity_vertical_margin"        android:paddingBottom="@dimen/activity_vertical_margin"        tools:context=".MainActivity">        <Button            android:text="绑定服务"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:onClick="bindService"/>        <Button            android:text="解绑服务"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:onClick="unbindService"/>        <Button            android:text="计算"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:onClick="caculate"/>    </LinearLayout>

服务端代码为:
MyService.java

public class MyService extends Service {    private Manager manager = new Manager();    public MyService() {    }    @Override    public IBinder onBind(Intent intent) {        Log.i("MyService", "onBind");        return manager;    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        Log.i("MyService", "onStartCommand");        return super.onStartCommand(intent, flags, startId);    }    @Override    public void onDestroy() {        Log.i("MyService", "onDestroy");        super.onDestroy();    }    class Manager extends IManager.Stub{        @Override        public int add(int x, int y) throws RemoteException {            return x + y;        }        @Override        public int min(int x, int y) throws RemoteException {            return x - y;        }    }}

我们知道,AIDL会自动生成一个java文件,我们这篇文章的重点就是这个文件。
从上面的代码可以指定,当我们调用bindService的时候,会传入一个ServiceConnection对象,当成功绑定后就会回调ServiceConnection对象的onServiceConnected方法,具体这个绑定过程是怎样的以及这个函数是在哪里被回调的,我们这里先不讨论,当这个函数会回调的时候,会带有一个IBinder的参数,这个就是远程服务接口,然后我们会调用IManager.Stub.asInterface(service)得到服务的本地代理对象。

具体代码如下:

public static com.newintenttest.remoteclient.IManager asInterface(android.os.IBinder obj)    {        //obj就是onServiceConnected函数的service参数        //如果没有得到远程服务接口,则返回null        if ((obj==null)) {            return null;        }        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);        if (((iin!=null)&&(iin instanceof com.newintenttest.remoteclient.IManager))) {            return ((com.newintenttest.remoteclient.IManager)iin);        }        //最终返回的是一个代理对象        return new com.newintenttest.remoteclient.IManager.Stub.Proxy(obj);    }

可以看到返回了一个服务的本地代理对象,也就是说我们在使用服务的时候,都是调用的这个代理对象的方法,但实质是通过这个代理对象来调用了远程返回的IBinder接口对象。看看Proxy的构造函数就知道了:

private static class Proxy implements com.newintenttest.remoteclient.IManager        {            private android.os.IBinder mRemote;            Proxy(android.os.IBinder remote)            {                mRemote = remote;            }}

内部定义了一般IBinder对象mRemote,用来存放这个远程服务接口对象。

在上面caculate函数中,我调用了manager.add(4, 5)方法,manager就是这个返回的代理对象,它就是调用代理对象的add方法。
直接看IManager.Stub.Proxy类里面源码是怎样实现的:

@Override public int add(int x, int y) throws android.os.RemoteException{    //首先声明两个Parcel对象,一个用于传递数据,一个用户接收返回的数据    android.os.Parcel _data = android.os.Parcel.obtain();    android.os.Parcel _reply = android.os.Parcel.obtain();    int _result;    try {    //这函数与enforceInterfac对应,下面会讲到    _data.writeInterfaceToken(DESCRIPTOR);    //写入需要传递的参数    _data.writeInt(x);    _data.writeInt(y);    //看到没有,实际调用的函数远程接口对象的方法    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);    //读出我们服务端返回的数据,然后return    _reply.readException();    _result = _reply.readInt();    }    finally {    _reply.recycle();    _data.recycle();    }    return _result;}

这里调用了mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0)方法,
我们进入Binder里面查看,transact函数:

    public final boolean transact(int code, Parcel data, Parcel reply,            int flags) throws RemoteException {        if (false) Log.v("Binder", "Transact: " + code + " to " + this);        if (data != null) {            data.setDataPosition(0);        }        //这个里面调用了onTransact函数        boolean r = onTransact(code, data, reply, flags);        if (reply != null) {            reply.setDataPosition(0);        }        return r;    }

最终会调用到onTransact函数,我还是直接看里面的代码,这个代码在AIDL自动生成的java文件中:

@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_add:        {            //与客户端的writeInterfaceToken对用,标识远程服务的名称            data.enforceInterface(DESCRIPTOR);            //分别读取了客户端传入的两个参数            int _arg0;            _arg0 = data.readInt();            int _arg1;            _arg1 = data.readInt();            //这个地方会执行我们实现的add方法,就是MyService里面实现的            int _result = this.add(_arg0, _arg1);            //返回result由reply写回            reply.writeNoException();            reply.writeInt(_result);            return true;        }        case TRANSACTION_min:        {            data.enforceInterface(DESCRIPTOR);            int _arg0;            _arg0 = data.readInt();            int _arg1;            _arg1 = data.readInt();            int _result = this.min(_arg0, _arg1);            reply.writeNoException();            reply.writeInt(_result);            return true;        }    }    return super.onTransact(code, data, reply, flags);}

我们继续进行梳理,先把两个函数对应一下:
transact(Stub.TRANSACTION_add, _data, _reply, 0)
onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
很清楚的就可以看到传过来的参数:
code 是一个整形的唯一标识,用于区分执行哪个方法,客户端会传递此参数,告诉服务端执行哪个方法
data客户端传递过来的参数
replay服务器返回回去的值
flags标明是否有返回值,0为有(双向),1为没有(单向)

下面就会执行switch里面的case TRANSACTION_add,直接看代码注释。
上面会执行我们在Myservice里面实现的add方法,然后把值返回到客户端,这样在客户端就可以得到计算的值了。

其实AIDL的作用就是对Binder的二个方法:Binder.transact()和Binder.onTransact()进行封装,以供Client端和Server端进行使用。因为实现transact()和onTransact()方法的方式基本上是相同的,所以就可以用模板来生成具体的代码。理论上讲只需要为Client端生成transact()相关代码,为服务端生成onTransact()代码即可,但因为工具无法准确的确定某一个应用到底是Client端还是Server端,所以它就生成所有的代码,放在一个文件中。这就是你看到的自动生成的文件。

在AIDL中生成的代码是将客户端和服务端代码放到了一块,上面将的都是AIDL生成代码里面的执行片段,现在将整个AIDL生成代码贴出来,看看全貌,然后把它拆分一下。

AIDL文件自动生成的IManager.java文件

public interface IManager extends android.os.IInterface{    //服务端代码    public static abstract class Stub extends android.os.Binder implements com.newintenttest.remoteclient.IManager    {        private static final java.lang.String DESCRIPTOR = "com.newintenttest.remoteclient.IManager";        /** Construct the stub at attach it to the interface. */        public Stub()        {            this.attachInterface(this, DESCRIPTOR);        }        /**         * Cast an IBinder object into an com.newintenttest.remoteclient.IManager interface,         * generating a proxy if needed.         */        public static com.newintenttest.remoteclient.IManager asInterface(android.os.IBinder obj)        {            if ((obj==null)) {                return null;            }            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);            if (((iin!=null)&&(iin instanceof com.newintenttest.remoteclient.IManager))) {                return ((com.newintenttest.remoteclient.IManager)iin);            }            return new com.newintenttest.remoteclient.IManager.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_add:                {                    data.enforceInterface(DESCRIPTOR);                    int _arg0;                    _arg0 = data.readInt();                    int _arg1;                    _arg1 = data.readInt();                    int _result = this.add(_arg0, _arg1);                    reply.writeNoException();                    reply.writeInt(_result);                    return true;                }                case TRANSACTION_min:                {                    data.enforceInterface(DESCRIPTOR);                    int _arg0;                    _arg0 = data.readInt();                    int _arg1;                    _arg1 = data.readInt();                    int _result = this.min(_arg0, _arg1);                    reply.writeNoException();                    reply.writeInt(_result);                    return true;                }            }            return super.onTransact(code, data, reply, flags);        }        //客户端代理,主要就是把服务端传过来的IBinder对象进行包装        //如果需要访问远程服务,只需要使用这个代理对象来访问就可以        private static class Proxy implements com.newintenttest.remoteclient.IManager        {            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 int add(int x, int y) throws android.os.RemoteException            {                android.os.Parcel _data = android.os.Parcel.obtain();                android.os.Parcel _reply = android.os.Parcel.obtain();                int _result;                try {                    _data.writeInterfaceToken(DESCRIPTOR);                    _data.writeInt(x);                    _data.writeInt(y);                    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);                    _reply.readException();                    _result = _reply.readInt();                }                finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }            @Override public int min(int x, int y) throws android.os.RemoteException            {                android.os.Parcel _data = android.os.Parcel.obtain();                android.os.Parcel _reply = android.os.Parcel.obtain();                int _result;                try {                _data.writeInterfaceToken(DESCRIPTOR);                _data.writeInt(x);                _data.writeInt(y);                mRemote.transact(Stub.TRANSACTION_min, _data, _reply, 0);                _reply.readException();                _result = _reply.readInt();                }                finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }        }        static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);        static final int TRANSACTION_min = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);    }    public int add(int x, int y) throws android.os.RemoteException;    public int min(int x, int y) throws android.os.RemoteException;}

上面的执行代码都在这个文件里面,下面把这个文件的类图放出来,就可以清楚它们的关系了。
AIDL生成文件类图

下面我们的工作就是把上面IManager.java文件分成IManagerClient.java和IManagerServer文件,重新创建客户端和服务端项目,然后使用这两个文件,不再使用AIDL。


客户端代码:
IManagerClient.java

public interface IManagerClient extends IInterface {    public int add(int x, int y) throws android.os.RemoteException;    public int min(int x, int y) throws android.os.RemoteException;    public static abstract class Stub extends android.os.Binder implements IManagerClient{        private static final String DESCRIPTOR = "com.newintenttest.startservice.IManager";        static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);        static final int TRANSACTION_min = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);        public Stub()        {            this.attachInterface(this, DESCRIPTOR);        }        public static IManagerClient asInterface(android.os.IBinder obj)        {            if ((obj==null)) {                return null;            }            IInterface iin = obj.queryLocalInterface(DESCRIPTOR);            if (((iin!=null)&&(iin instanceof IManagerClient))) {                return ((IManagerClient)iin);            }            return new Stub.Proxy(obj);        }        private static class Proxy implements IManagerClient        {            private android.os.IBinder mRemote;            Proxy(android.os.IBinder remote)            {                mRemote = remote;            }            @Override public android.os.IBinder asBinder()            {                return mRemote;            }            public String getInterfaceDescriptor()            {                return DESCRIPTOR;            }            @Override public int add(int x, int y) throws android.os.RemoteException            {                android.os.Parcel _data = android.os.Parcel.obtain();                android.os.Parcel _reply = android.os.Parcel.obtain();                int _result;                try {                    _data.writeInterfaceToken(DESCRIPTOR);                    _data.writeInt(x);                    _data.writeInt(y);                    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);                    _reply.readException();                    _result = _reply.readInt();                }                finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }            @Override public int min(int x, int y) throws android.os.RemoteException            {                android.os.Parcel _data = android.os.Parcel.obtain();                android.os.Parcel _reply = android.os.Parcel.obtain();                int _result;                try {                    _data.writeInterfaceToken(DESCRIPTOR);                    _data.writeInt(x);                    _data.writeInt(y);                    mRemote.transact(Stub.TRANSACTION_min, _data, _reply, 0);                    _reply.readException();                    _result = _reply.readInt();                }                finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }        }    }}

MainActivity.java

public class MainActivity extends Activity {    private IManagerClient manager;    private ServiceConnection connection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            Log.i("MyService", "onServiceConnected");            manager = IManagerClient.Stub.asInterface(service);        }        @Override        public void onServiceDisconnected(ComponentName name) {        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }    public void bindService(View view) {        Log.i("MyService", "bindService");        Intent intent = new Intent("com.newintenttest.bindservice.MyService");        bindService(intent, connection, Context.BIND_AUTO_CREATE);    }    public void unbindService(View view) {        unbindService(connection);    }    public void caculate(View view){        if (manager == null) {            Toast.makeText(this, "请绑定服务",Toast.LENGTH_SHORT).show();        } else {            int result = 0;            try {                result = manager.add(4, 5);            } catch (Exception e) {                e.printStackTrace();            }            Toast.makeText(this, result + "",Toast.LENGTH_SHORT).show();        }    }}

服务端文件:
IManagerServer.java

public interface IManagerServer extends android.os.IInterface{    public int add(int x, int y) throws android.os.RemoteException;    public int min(int x, int y) throws android.os.RemoteException;    public static abstract class Stub extends android.os.Binder implements IManagerServer{        private static final String DESCRIPTOR = "com.newintenttest.startservice.IManager";        public Stub()        {            this.attachInterface(this, DESCRIPTOR);        }        @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_add:                {                    data.enforceInterface(DESCRIPTOR);                    int _arg0;                    _arg0 = data.readInt();                    int _arg1;                    _arg1 = data.readInt();                    int _result = this.add(_arg0, _arg1);                    reply.writeNoException();                    reply.writeInt(_result);                    return true;                }                case TRANSACTION_min:                {                    data.enforceInterface(DESCRIPTOR);                    int _arg0;                    _arg0 = data.readInt();                    int _arg1;                    _arg1 = data.readInt();                    int _result = this.min(_arg0, _arg1);                    reply.writeNoException();                    reply.writeInt(_result);                    return true;                }            }            return super.onTransact(code, data, reply, flags);        }        static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);        static final int TRANSACTION_min = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);    }}

MyService.java

public class MyService extends Service {    private Manager manager = new Manager();    public MyService() {    }    @Override    public IBinder onBind(Intent intent) {        Log.i("MyService", "onBind");        return manager;    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        Log.i("MyService", "onStartCommand");        return super.onStartCommand(intent, flags, startId);    }    @Override    public void onDestroy() {        Log.i("MyService", "onDestroy");        super.onDestroy();    }    class Manager extends IManagerServer.Stub{        @Override        public int add(int x, int y) throws RemoteException {            return x + y;        }        @Override        public int min(int x, int y) throws RemoteException {            return x - y;        }    }}

上面项目中没有使用AIDL也没有AIDL生成的文件,程序仍然可以正常运行,这也充分的说明了,AIDL生成文件其实做了已经比较懒惰的事情,它无法区分服务端和客户端,所以自动生成的文件中,既包含客户端代码也包含服务端代码,我们在使用的时候,这个文件会分别位于客户端和服务端,这样客户端和服务端各取所需就行,但是其实我们可以通过手动的方式来把服务端程序和客户端程序分开,放在各种需要的位置就行,这样做的目的就是为了加深理解。
下面来把这个执行流程用图来梳理一些。

这里写图片描述

通过上面的代码,我们可以进行进一步的尝试,不使用AIDL,并且把IManagerClient.java和IManagerServer.java里面的代码直接融入到我们的代码中。


客户端代码:

public class MainActivity extends Activity {    static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);    private static final String DESCRIPTOR = "com.newintenttest.startservice.IManager";    private IBinder binder;    private ServiceConnection connection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            Log.i("MyService", "onServiceConnected");            binder = service;        }        @Override        public void onServiceDisconnected(ComponentName name) {        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }    public void bindService(View view) {        Log.i("MyService", "bindService");        Intent intent = new Intent("com.newintenttest.bindservice.MyService");        bindService(intent, connection, Context.BIND_AUTO_CREATE);    }    public void unbindService(View view) {        unbindService(connection);    }    public void caculate(View view){        if (binder == null) {            Toast.makeText(this, "请绑定服务",Toast.LENGTH_SHORT).show();        } else {            int _result = 0;            try {                android.os.Parcel _data = android.os.Parcel.obtain();                android.os.Parcel _reply = android.os.Parcel.obtain();                try {                    _data.writeInterfaceToken(DESCRIPTOR);                    _data.writeInt(4);                    _data.writeInt(5);                    binder.transact(TRANSACTION_add, _data, _reply, 0);                    _reply.readException();                    _result = _reply.readInt();                }                finally {                    _reply.recycle();                    _data.recycle();                }            } catch (Exception e) {                e.printStackTrace();            }            Toast.makeText(this, _result + "",Toast.LENGTH_SHORT).show();        }    }}

服务端代码:

public class MyService extends Service {    private Manager manager = new Manager();    public MyService() {    }    @Override    public IBinder onBind(Intent intent) {        Log.i("MyService", "onBind");        return manager;    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        Log.i("MyService", "onStartCommand");        return super.onStartCommand(intent, flags, startId);    }    @Override    public void onDestroy() {        Log.i("MyService", "onDestroy");        super.onDestroy();    }    class Manager extends Binder {        private static final String DESCRIPTOR = "com.newintenttest.startservice.IManager";        static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);        static final int TRANSACTION_min = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);        @Override        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {            switch (code)            {                case INTERFACE_TRANSACTION:                {                    reply.writeString(DESCRIPTOR);                    return true;                }                case TRANSACTION_add:                {                    data.enforceInterface(DESCRIPTOR);                    int _arg0;                    _arg0 = data.readInt();                    int _arg1;                    _arg1 = data.readInt();                    int _result = this.add(_arg0, _arg1);                    reply.writeNoException();                    reply.writeInt(_result);                    return true;                }                case TRANSACTION_min:                {                    data.enforceInterface(DESCRIPTOR);                    int _arg0;                    _arg0 = data.readInt();                    int _arg1;                    _arg1 = data.readInt();                    int _result = this.min(_arg0, _arg1);                    reply.writeNoException();                    reply.writeInt(_result);                    return true;                }            }            return super.onTransact(code, data, reply, flags);        }        public int min(int x, int y) throws RemoteException {            return x - y;       }        public int add(int x, int y) throws RemoteException {            return x + y;       }    }}

到这里,我们已经把整个AIDL文件生成的java文件代码进行了解析,整个调用过程基本已经很清楚,但是没有深入到底层C/C++的实现,所以只能说是浅析,对于底层跨进程是如何通信的,其实我也不是很清楚,底层代码有些晦涩难懂,还需要时间去整理。另外上面的分析可能有些地方有不足之处,望批评指正。

参考文章:
Android实战技术:深入理解Android的RPC方式与AIDL
Android aidl Binder框架浅析

0 0
原创粉丝点击