概述

之前我写过一篇文章Android Service全面解析,简单实现了如何通过AIDL实现Service的跨进程通信(IPC),其实是通过Binder机制来实现的,本文我们就重点来看看Binder机制的原理。

Binder可以提供系统中任何程序都可以访问的全局服务。这个功能当然是任何系统都应该提供的,下面我们简单看一下Android的Binder的框架:
Android Binder框架分为服务器接口、Binder驱动、以及客户端接口;简单想一下,需要提供一个全局服务,那么全局服务那端即是服务器接口,任何程序即客户端接口,它们之间通过一个Binder驱动访问。

  • 服务器接口:实际上是Binder类的对象,该对象一旦创建,内部则会启动一个隐藏线程,接收Binder驱动发送的消息,收到消息后,会执行Binder对象中的onTransact()函数,并按照该函数的参数执行不同的服务器端代码。
  • Binder驱动:该对象也为Binder类的实例,客户端通过该对象访问远程服务。
  • 客户端接口:获得Binder驱动,调用其transact()发送消息至服务器。

实例实现

如果你觉得上面的描述太抽象了,没关系,下面我们通过一个具体的例子来看看Binder机制的原理。例子我仍然使用上一篇文章的例子,不过之前我是使用Eclipse创建工程,今天我们使用Studio来创建项目,效果都是一样的。

(1)Studio创建两个Module,app代表客户端程序,binder_server代表服务器端程序。

(2)创建aidl文件
在app目录上右键,NEW->AIDL->AIDL File,创建一个aidl文件(IMyAidlInterface.aidl),同时必须要指明包名,包名必须和java目录下的包名一致。此aidl文件会默认生成到aidl目录下,aidl目录和java目录同级别。

IMyAidlInterface.aidl文件内容:

interface IMyAidlInterface {    int plus(int a, int b);    String toUpperCase(String str);}

build一下,会自动生成IMyAidlInterface.java文件,不同于Eclipse的gen目录,studio下的java文件目录为:
*(项目名)\app\build\generated\source\aidl\debug\com\hx\binder\IMyAidlInterface.java
关于IMyAidlInterface.java文件内容,我们后面会具体分析,这里先省略。

(3)将aidl文件连同目录一起拷贝到服务器端

(4)服务器端新建服务MyRemoteService

public class MyRemoteService extends Service {    @Override    public void onCreate() {        super.onCreate();        MainActivity.showlog("onCreate()");    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        MainActivity.showlog("onStartCommand()");        return super.onStartCommand(intent, flags, startId);    }    @Override    public void onDestroy() {        super.onDestroy();        MainActivity.showlog("onDestroy()");    }    @Override    public IBinder onBind(Intent intent) {        MainActivity.showlog("onBind()");        return mBinder;    }    @Override    public boolean onUnbind(Intent intent) {        MainActivity.showlog("onUnbind()");        return super.onUnbind(intent);    }    IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {        @Override        public String toUpperCase(String str) throws RemoteException {            if (str != null) {                return str.toUpperCase();            }            return null;        }        @Override        public int plus(int a, int b) throws RemoteException {            return a + b;        }    };}

在Manifest中进行注册:

<service android:name=".MyRemoteService" android:exported="true">     <intent-filter>           <action android:name="com.hx.action.remoteService" />     </intent-filter></service>

(4)编写客户端代码

public class MainActivity extends Activity implements View.OnClickListener {    private Button bindService;    private Button unbindService;    private Button plus;    private Button toUpperCase;    private IMyAidlInterface myAIDLInterface;    private ServiceConnection connection = new ServiceConnection() {        @Override        public void onServiceDisconnected(ComponentName name) {            myAIDLInterface = null;            Toast.makeText(MainActivity.this, "onServiceDisconnected", Toast.LENGTH_SHORT).show();        }        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            myAIDLInterface = IMyAidlInterface.Stub.asInterface(service);            Toast.makeText(MainActivity.this, "onServiceConnected", Toast.LENGTH_SHORT).show();        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        bindService = (Button) findViewById(R.id.bind_service);        unbindService = (Button) findViewById(R.id.unbind_service);        plus = (Button) findViewById(R.id.plus);        toUpperCase = (Button) findViewById(R.id.toUpperCase);        //button点击事件        bindService.setOnClickListener(this);        unbindService.setOnClickListener(this);        plus.setOnClickListener(this);        toUpperCase.setOnClickListener(this);    }    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.bind_service:                Intent intent = new Intent("com.hx.action.remoteService");                //5.0以上安卓设备,service intent必须为显式指出                Intent eintent = new Intent(getExplicitIntent(this,intent));                bindService(eintent, connection, Context.BIND_AUTO_CREATE);// bindService(intent, connection, BIND_AUTO_CREATE);                break;            case R.id.unbind_service:                if(myAIDLInterface != null){                    unbindService(connection);                }                break;            case R.id.plus:                if (myAIDLInterface != null) {                    try {                        int result = myAIDLInterface.plus(13, 19);                        Toast.makeText(this, result + "", Toast.LENGTH_SHORT).show();                    } catch (RemoteException e) {                        e.printStackTrace();                    }                } else {                    Toast.makeText(this, "服务器被异常杀死,请重新绑定服务端", Toast.LENGTH_SHORT).show();                }                break;            case R.id.toUpperCase:                if (myAIDLInterface != null) {                    try {                        String upperStr = myAIDLInterface.toUpperCase("hello aidl service");                        Toast.makeText(this, upperStr + "", Toast.LENGTH_SHORT).show();                    } catch (RemoteException e) {                        e.printStackTrace();                    }                } else {                    Toast.makeText(this, "服务器被异常杀死,请重新绑定服务端", Toast.LENGTH_SHORT).show();                }                break;            default:                break;        }    }    public static Intent getExplicitIntent(Context context, Intent implicitIntent) {        // Retrieve all services that can match the given intent        PackageManager pm = context.getPackageManager();        List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);        // Make sure only one match was found        if (resolveInfo == null || resolveInfo.size() != 1) {            return null;        }        // Get component info and create ComponentName        ResolveInfo serviceInfo = resolveInfo.get(0);        String packageName = serviceInfo.serviceInfo.packageName;        String className = serviceInfo.serviceInfo.name;        ComponentName component = new ComponentName(packageName, className);        // Create a new intent. Use the old one for extras and such reuse        Intent explicitIntent = new Intent(implicitIntent);        // Set the component to be explicit        explicitIntent.setComponent(component);        return explicitIntent;    }}
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:orientation="vertical">    <Button  android:id="@+id/bind_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="bind service" />    <Button  android:id="@+id/unbind_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="unbind service" />    <Button  android:id="@+id/plus" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="13 + 19" />    <Button  android:id="@+id/toUpperCase" android:layout_width="match_parent" android:layout_height="wrap_content" android:textAllCaps="false" android:text="hello aidl service" /></LinearLayout>

运行程序,看效果:

我们首先点击BIND SERVICE按钮,绑定服务,会弹出“onServiceConnected”的Toast,说明服务绑定成功,获取到了服务器端的Binder驱动。
服务端Log:

然后分别点击13+19和hello aidl service按钮,可以通过Binder驱动调用服务端的代码并返回正确的计算结果。
最后点击UNBIND SERVICE按钮,我们的期望是弹出“onServiceDisconnected”的Toast,解除绑定,实际上呢?很遗憾没有弹出。
服务端Log:

由于我们当前只有一个客户端绑定了此Service,所以Service调用了onUnbind和onDestory。当我们继续点击13+19按钮,发现依然可以正确执行得到结果,也就是说即使onUnbind被调用,连接也是不会断开的,那么什么时候会断开连接呢?
即当服务端被异常终止的时候,比如我们现在在手机的正在执行的程序中找到该服务,并强行停止它:

可以看到这时弹出了“onServiceDisconnected”的Toast,说明连接被断开。之后再次点击13+19按钮,则会弹出Toast提示“服务器被异常杀死,请重新绑定服务端”。

原理分析

还记得我们上面根据aidl文件生成的Java文件吗?我们来看看它的结构吧:

public interface IMyAidlInterface extends android.os.IInterface {    /** * Local-side IPC implementation stub class. */    public static abstract class Stub extends android.os.Binder implements com.hx.binder.IMyAidlInterface {        private static final java.lang.String DESCRIPTOR = "com.hx.binder.IMyAidlInterface";        /** * Construct the stub at attach it to the interface. */        public Stub() {            this.attachInterface(this, DESCRIPTOR);        }        /** * Cast an IBinder object into an com.hx.binder.IMyAidlInterface interface, * generating a proxy if needed. */        public static com.hx.binder.IMyAidlInterface asInterface(android.os.IBinder obj) {            if ((obj == null)) {                return null;            }            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);            if (((iin != null) && (iin instanceof com.hx.binder.IMyAidlInterface))) {                return ((com.hx.binder.IMyAidlInterface) iin);            }            return new com.hx.binder.IMyAidlInterface.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_plus: {                    data.enforceInterface(DESCRIPTOR);                    int _arg0;                    _arg0 = data.readInt();                    int _arg1;                    _arg1 = data.readInt();                    int _result = this.plus(_arg0, _arg1);                    reply.writeNoException();                    reply.writeInt(_result);                    return true;                }                case TRANSACTION_toUpperCase: {                    data.enforceInterface(DESCRIPTOR);                    java.lang.String _arg0;                    _arg0 = data.readString();                    java.lang.String _result = this.toUpperCase(_arg0);                    reply.writeNoException();                    reply.writeString(_result);                    return true;                }            }            return super.onTransact(code, data, reply, flags);        }        private static class Proxy implements com.hx.binder.IMyAidlInterface {            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 plus(int a, int b) 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(a);                    _data.writeInt(b);                    mRemote.transact(Stub.TRANSACTION_plus, _data, _reply, 0);                    _reply.readException();                    _result = _reply.readInt();                } finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }            @Override            public java.lang.String toUpperCase(java.lang.String str) 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);                    _data.writeString(str);                    mRemote.transact(Stub.TRANSACTION_toUpperCase, _data, _reply, 0);                    _reply.readException();                    _result = _reply.readString();                } finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }        }        static final int TRANSACTION_plus = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);        static final int TRANSACTION_toUpperCase = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);    }    public int plus(int a, int b) throws android.os.RemoteException;    public java.lang.String toUpperCase(java.lang.String str) throws android.os.RemoteException;}

代码比较长,但思路还是比较清晰的,IMyAidlInterface.java文件包含两个静态内部类—Stub和Proxy(其中Proxy是Stub的内部类)。

public static abstract class Stub extends android.os.Binder implements com.hx.binder.IMyAidlInterface

其中Stub是个抽象类,它继承了Binder,并实现了IMyAidlInterface接口。Stub提供了几个方法:asInterface、asBinder、onTransact,但并没有实现IMyAidlInterface接口的方法,所以需要交给Stub的实现类去实现。

private static class Proxy implements com.hx.binder.IMyAidlInterface

Proxy是Stub的内部类,也实现了IMyAidlInterface接口。并提供了几个方法:asBinder、getInterfaceDescriptor,并实现了IMyAidlInterface接口的方法plus和toUpperCase。

接下来看看服务端和客户端是如何和这个文件建立关联的吧。
服务端:

IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {        @Override        public String toUpperCase(String str) throws RemoteException {            if (str != null) {                return str.toUpperCase();            }            return null;        }        @Override        public int plus(int a, int b) throws RemoteException {            return a + b;        }    };

可以看到我们服务端提供的服务是由IMyAidlInterface.Stub来执行的,上面分析过,Stub这个类是Binder的子类,是不是符合我们文章开头所说的服务端其实是一个Binder类的实例。而且mBinder实现了IMyAidlInterface接口的方法。
接下来看Stub的onTransact()方法:

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_plus: {                    data.enforceInterface(DESCRIPTOR);                    int _arg0;                    _arg0 = data.readInt();                    int _arg1;                    _arg1 = data.readInt();                    int _result = this.plus(_arg0, _arg1);                    reply.writeNoException();                    reply.writeInt(_result);                    return true;                }                case TRANSACTION_toUpperCase: {                    data.enforceInterface(DESCRIPTOR);                    java.lang.String _arg0;                    _arg0 = data.readString();                    java.lang.String _result = this.toUpperCase(_arg0);                    reply.writeNoException();                    reply.writeString(_result);                    return true;                }            }            return super.onTransact(code, data, reply, flags);        }

文章开头也说到服务端的Binder实例会根据客户端依靠Binder驱动发来的消息,执行onTransact方法,然后由其参数决定执行服务端的代码。
可以看到onTransact有四个参数:code , data ,replay , flags

  • code:是一个整形的唯一标识,用于区分执行哪个方法,客户端会传递此参数,告诉服务端执行哪个方法
  • data:客户端传递过来的参数
  • reply:服务器返回回去的值
  • flags:标明是否有返回值,0为有(双向),1为没有(单向)

我们仔细看case TRANSACTION_plus中的代码:

data.enforceInterface(DESCRIPTOR); 

与客户端的writeInterfaceToken对应,标识远程服务的名称

int _arg0;_arg0 = data.readInt();int _arg1;_arg1 = data.readInt();

接下来分别读取了客户端传入的两个参数

int _result = this.plus(_arg0, _arg1);reply.writeNoException();reply.writeInt(_result);

然后执行this.plus,即我们服务端实现的plus方法;返回result由reply写回。
toUpperCase同理,可以看到服务端通过AIDL生成的Stub类,封装了服务端本来需要写的代码。

客户端
客户端主要通过ServiceConnected与服务端连接

private ServiceConnection connection = new ServiceConnection() {        @Override        public void onServiceDisconnected(ComponentName name) {            isConnected = false;            Toast.makeText(MainActivity.this, "onServiceDisconnected", Toast.LENGTH_SHORT).show();        }        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            isConnected = true;            myAIDLInterface = IMyAidlInterface.Stub.asInterface(service);            Toast.makeText(MainActivity.this, "onServiceConnected", Toast.LENGTH_SHORT).show();        }    };

其实这个onServiceConnected中的IBinder实例,其实就是我们文章开头所说的Binder驱动,也是一个Binder实例。
在IMyAidlInterface.Stub.asInterface中最终调用了:

return new com.hx.binder.IMyAidlInterface.Stub.Proxy(obj);

这个Proxy实例传入了我们的Binder驱动,并且封装了我们调用服务端的代码,文章开头说,客户端会通过Binder驱动的transact()方法调用服务端代码。
直接看Proxy中的plus方法

            public int plus(int a, int b) 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(a);                    _data.writeInt(b);                    mRemote.transact(Stub.TRANSACTION_plus, _data, _reply, 0);                    _reply.readException();                    _result = _reply.readInt();                } finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }

首先声明两个Parcel对象,一个用于传递数据,一个用户接收返回的数据

_data.writeInterfaceToken(DESCRIPTOR);

与服务器端的enforceInterfac对应

_data.writeInt(a);_data.writeInt(b);

写入需要传递的参数

mRemote.transact(Stub.TRANSACTION_plus, _data, _reply, 0);

终于看到了我们的transact方法,第一个对应服务端的code,_data,_reply分别对应服务端的data,reply,0表示是双向的

_reply.readException();_result = _reply.readInt();

最后读出我们服务端返回的数据,然后return。可以看到和服务端的onTransact基本是一行一行对应的。
到此,我们已经通过AIDL生成的代码解释了Android Binder框架的工作原理。Service的作用其实就是为我们创建Binder驱动,即服务端与客户端连接的桥梁。
AIDL其实通过我们写的aidl文件,帮助我们生成了一个接口,一个Stub类用于服务端,一个Proxy类用于客户端调用。

不依赖AIDL实现IPC通信

那么我们是否可以不通过写aidl文件来实现远程的通信呢?下面向大家展示如何完全不依赖AIDL来实现客户端与服务端的通信。
服务端代码:

public class MyRemoteService extends Service {    private static final String DESCRIPTOR = "MyRemoteService";    private static final int TRANSACTION_plus = 0x110;    private static final int TRANSACTION_toUpperCase = 0x111;    @Override    public void onCreate() {        super.onCreate();        MainActivity.showlog("onCreate()");    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        MainActivity.showlog("onStartCommand()");        return super.onStartCommand(intent, flags, startId);    }    @Override    public void onDestroy() {        super.onDestroy();        MainActivity.showlog("onDestroy()");    }    @Override    public IBinder onBind(Intent intent) {        MainActivity.showlog("onBind()");        return mBinder;    }    @Override    public boolean onUnbind(Intent intent) {        MainActivity.showlog("onUnbind()");        return super.onUnbind(intent);    }    private MyBinder mBinder = new MyBinder();    private class MyBinder extends Binder {        @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_plus: {                    data.enforceInterface(DESCRIPTOR);                    int _arg0;                    _arg0 = data.readInt();                    int _arg1;                    _arg1 = data.readInt();                    int _result = _arg0 + _arg1;                    reply.writeNoException();                    reply.writeInt(_result);                    return true;                }                case TRANSACTION_toUpperCase: {                    data.enforceInterface(DESCRIPTOR);                    java.lang.String _arg0;                    _arg0 = data.readString();                    java.lang.String _result = _arg0.toUpperCase();                    reply.writeNoException();                    reply.writeString(_result);                    return true;                }            }            return super.onTransact(code, data, reply, flags);        }    };}
<service android:name=".MyRemoteService" android:exported="true">      <intent-filter>           <action android:name="com.hx.action.remoteService" />      </intent-filter></service>

客户端代码:

public class MainActivity extends Activity implements View.OnClickListener {    private Button bindService;    private Button unbindService;    private Button plus;    private Button toUpperCase;    private IBinder myBinder;    private static final String DESCRIPTOR = "MyRemoteService";    private static final int TRANSACTION_plus = 0x110;    private static final int TRANSACTION_toUpperCase = 0x111;    private ServiceConnection connection = new ServiceConnection() {        @Override        public void onServiceDisconnected(ComponentName name) {            myBinder = null;            Toast.makeText(MainActivity.this, "onServiceDisconnected", Toast.LENGTH_SHORT).show();        }        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            myBinder = service;            Toast.makeText(MainActivity.this, "onServiceConnected", Toast.LENGTH_SHORT).show();        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        bindService = (Button) findViewById(R.id.bind_service);        unbindService = (Button) findViewById(R.id.unbind_service);        plus = (Button) findViewById(R.id.plus);        toUpperCase = (Button) findViewById(R.id.toUpperCase);        //button点击事件        bindService.setOnClickListener(this);        unbindService.setOnClickListener(this);        plus.setOnClickListener(this);        toUpperCase.setOnClickListener(this);    }    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.bind_service:                Intent intent = new Intent("com.hx.action.remoteService");                //5.0以上安卓设备,service intent必须为显式指出                Intent eintent = new Intent(getExplicitIntent(this,intent));                bindService(eintent, connection, Context.BIND_AUTO_CREATE);// bindService(intent, connection, BIND_AUTO_CREATE);                break;            case R.id.unbind_service:                if(myBinder != null){                    unbindService(connection);                }                break;            case R.id.plus:                if (myBinder != null) {                    android.os.Parcel _data = android.os.Parcel.obtain();                    android.os.Parcel _reply = android.os.Parcel.obtain();                    int _result;                    try {                        _data.writeInterfaceToken(DESCRIPTOR);                        _data.writeInt(78);                        _data.writeInt(95);                        myBinder.transact(TRANSACTION_plus, _data, _reply, 0);                        _reply.readException();                        _result = _reply.readInt();                        Toast.makeText(this, _result + "", Toast.LENGTH_SHORT).show();                    } catch (RemoteException e) {                        e.printStackTrace();                    } finally {                        _reply.recycle();                        _data.recycle();                    }                } else {                    Toast.makeText(this, "服务器被异常杀死,请重新绑定服务端", Toast.LENGTH_SHORT).show();                }                break;            case R.id.toUpperCase:                if (myBinder != null) {                    android.os.Parcel _data = android.os.Parcel.obtain();                    android.os.Parcel _reply = android.os.Parcel.obtain();                    java.lang.String _result;                    try {                        _data.writeInterfaceToken(DESCRIPTOR);                        _data.writeString("my new programe");                        myBinder.transact(TRANSACTION_toUpperCase, _data, _reply, 0);                        _reply.readException();                        _result = _reply.readString();                        Toast.makeText(this, _result + "", Toast.LENGTH_SHORT).show();                    } catch (RemoteException e) {                        e.printStackTrace();                    } finally {                        _reply.recycle();                        _data.recycle();                    }                } else {                    Toast.makeText(this, "服务器被异常杀死,请重新绑定服务端", Toast.LENGTH_SHORT).show();                }                break;            default:                break;        }    }    public static Intent getExplicitIntent(Context context, Intent implicitIntent) {        // Retrieve all services that can match the given intent        PackageManager pm = context.getPackageManager();        List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);        // Make sure only one match was found        if (resolveInfo == null || resolveInfo.size() != 1) {            return null;        }        // Get component info and create ComponentName        ResolveInfo serviceInfo = resolveInfo.get(0);        String packageName = serviceInfo.serviceInfo.packageName;        String className = serviceInfo.serviceInfo.name;        ComponentName component = new ComponentName(packageName, className);        // Create a new intent. Use the old one for extras and such reuse        Intent explicitIntent = new Intent(implicitIntent);        // Set the component to be explicit        explicitIntent.setComponent(component);        return explicitIntent;    }}
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:orientation="vertical">    <Button  android:id="@+id/bind_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="bind service" />    <Button  android:id="@+id/unbind_service" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="unbind service" />    <Button  android:id="@+id/plus" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="78 + 95" />    <Button  android:id="@+id/toUpperCase" android:layout_width="match_parent" android:layout_height="wrap_content" android:textAllCaps="false" android:text="my new programe" /></LinearLayout>

这里我们并没有写aidl文件,而是将aidl生成的java文件的处理逻辑移动到我们的服务端和客户端来实现。同样会达到和上面一样的效果。

IPC传递自定义类型Bean

不过还有一点需要说明的是,由于这是在不同的进程之间传递数据(参数和返回值),Android对这类数据的格式支持是非常有限的,默认支持的类型包话Java基本类型(int、long、boolean等)和(String、List、Map、CharSequence)。

AIDL对Java类型的支持:

  • AIDL支持Java原始数据类型
  • AIDL支持String和CharSequence
  • AIDL支持传递其他AIDL接口,但你引用的每个AIDL接口都需要一个import语句,即使位于同一个包中
  • AIDL支持传递实现了android.os.Parcelable接口的复杂类型,同样在引用这些类型时也需要import语句
  • AIDL支持java.util.List和java.util.Map,但是有一些限制。集合中项的允许数据类型包括Java原始类型、String、CharSequence或是android.os.Parcelable。无需为List和Map提供import语句,但需要为Parcelable提供import语句
  • 非原始类型中,除了String和CharSequence以外,其余均需要一个方向指示符。方向指示符包括in、out、和inout。in表示由客户端设置,out表示由服务端设置,inout表示客户端和服务端都设置了该值

如果我想传递一个自定义类型的Bean该怎么办呢?这就必须要让这个类去实现Parcelable接口,并且要给这个类也定义一个同名的AIDL文件。本例我们通过客户端传递一个自定义Student对象给服务端,服务端将Student的Age属性加倍,然后回传对象到客户端。具体实现步骤如下:
(1)先操作客户端代码,在aidl文件夹下创建自定义类型Bean文件Student.java,并实现Parcelable接口,使其支持parcelable协议。

public class Student implements Parcelable {    private String name;    private int age;    //必须提供一个名为CREATOR的static final属性 该属性需要实现android.os.Parcelable.Creator<T>接口    public static final Creator<Student> CREATOR = new Creator<Student>() {        //通过source对象,根据writeToParcel()方法序列化的数据,反序列化一个Parcelable对象,注意读取变量和写入变量的顺序应该一致,不然得不到正确的结果        @Override        public Student createFromParcel(Parcel source) {            return new Student(source);        }        //创建一个新的Parcelable对象的数组        @Override        public Student[] newArray(int size) {            return new Student[size];        }    };    public Student() {}    public Student(Parcel pl) {        name = pl.readString();        age = pl.readInt();    }    //返回一个位掩码,表示一组特殊对象类型的Parcelable,一般返回0即可    @Override    public int describeContents() {        return 0;    }    //实现对象的序列化,注意写入变量和读取变量的顺序应该一致,不然得不到正确的结果    @Override    public void writeToParcel(Parcel dest, int flags) {        dest.writeString(name);        dest.writeInt(age);    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }}

在Android中序列化对象主要有两种方式,实现Serializable接口或是实现Parcelable接口。Serializable接口是JavaSE原生支持的,而Parcelable接口是Android所特有的,它的序列化和反序列化的效率均比Serializable接口高(其定位就是轻量级的高效的对象序列化机制与反序列化机制。如果读一下Android的底层代码,会发现Parcel是使用C++实现的,底层直接通过Parcel指针操作内存实现,所以它的才更高效),而AIDL进行在进程间通信(IPC),就是需要实现这个Parcelable接口。

Parcelable接口的作用:实现了Parcelable接口的实例,可以将自身的数据信息写入一个Parcel对象,也可以从parcel中恢复到对象的状态。而Parcel就是完成数据序列化写入的载体。
简单来说,Parcelable通过writeToParcel()方法,对复杂对象的数据写入Parcel的方式进行对象序列化,然后在需要的时候,通过其内定义的静态属性CREATOR.createFromParcel()进行反序列化的操作。Parcelable对Parcel进行了包装,其内部就是通过操作Parcel进行序列化与反序列化的。这里要注意的是两个方法中Parcel对象的writeXxx()和readXxx()方法的顺序必须一致,因为一般序列化数据是以链的形式序列化的,如果顺序不对,反序列化的数据会出错。

(2)同时在aidl文件夹下创建一个与自定义类型同名的aidl文件Student.aidl。

package com.hx.binder;parcelable Student;

(3)在接口aidl文件中使用自定义类型时,需要使用import显式导入(即使在同一个包下):

package com.hx.binder;import com.hx.binder.Student;  //显式导入interface IMyAidlInterface {    int plus(int a, int b);    String toUpperCase(String str);    Student doubleAge(in Student student);}

完成这三步之后,客户端代码结构如下:

(4)在服务器端完成同样的三件事,服务端代码结构如下:

(5)编写服务端与客户端代码
服务端:

... IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() {        @Override        public String toUpperCase(String str) throws RemoteException {            if (str != null) {                return str.toUpperCase();            }            return null;        }        @Override        public int plus(int a, int b) throws RemoteException {            return a + b;        }        @Override        public Student doubleAge(Student student) throws RemoteException {            Student s = student;            int age = s.getAge();            s.setAge(2*age);  //年龄加倍            return s;        }    };

客户端:

        ...        case R.id.doubleAge:                if (myAIDLInterface != null) {                    try {                        Student student_before= new Student();                        student_before.setName("jack");                        student_before.setAge(18);                        Student student_after = myAIDLInterface.doubleAge(student_before);                        Toast.makeText(this, "student age after modify is " + student_after.getAge(), Toast.LENGTH_SHORT).show();                    } catch (RemoteException e) {                        e.printStackTrace();                    }                } else {                    Toast.makeText(this, "服务器被异常杀死,请重新绑定服务端", Toast.LENGTH_SHORT).show();                }                break;

(6)因为这里我们将.java文件也放在aidl文件夹下,所以需要修改客户端和服务端Module的gradle文件,把src/main/aidl文件也作为Java.srcDirs, resources.srcDirs。否则编译的时候会报aidl接口中用到的自定义类型的包找不到。

sourceSets {        main {            manifest.srcFile 'src/main/AndroidManifest.xml'            java.srcDirs = ['src/main/java', 'src/main/aidl']            resources.srcDirs = ['src/main/java', 'src/main/aidl']            aidl.srcDirs = ['src/main/aidl']            res.srcDirs = ['src/main/res']            assets.srcDirs = ['src/main/assets']        }    }

编译运行,效果如下:

Demo下载地址

更多相关文章

  1. android与服务器连接保持
  2. Android(安卓)Intent 传递二进制数值的两种方法
  3. 极光推送完整流程测试
  4. Android中读图片Exif信息的方法
  5. 【转】Android上HDMI介绍(基于高通平台)
  6. 局域网场景下Android客户端实现同数据库连接通信
  7. Android(安卓)4.4(KitKat)窗口管理子系统 - 体系框架
  8. Android下使用Hessian与Java服务端通讯
  9. Android客户端与服务器的数据交互总结

随机推荐

  1. Android初级教程:Android中解析方式之pull
  2. 跟我学Android应用开发 之 如何添加可选
  3. 《Android(安卓)全埋点解决方案》前言篇
  4. Android(安卓)TV开发总结(四)通过RecycleVi
  5. Android将ButtonBar放在屏幕底部
  6. Android(安卓)dalvik虚拟机内部机制详解(2
  7. 【2016.11.29】iOS和Android设计理念的演
  8. Android(安卓)minSDK 和 targetSDK 版本
  9. Android(安卓)实现按钮 跳转到某网页
  10. Android:dkplayer非全屏时候显示返回按钮