在Android中,每个应用程序都有自己的进程,当需要在不同的进程之间传递对象时,该如何实现呢?
显然,Java中是不支持跨进程内存共享的.因此要传递对象,需要把对象解析成操作系统能够理解的数据格式,以达到跨界对象访问的目的.
在JavaEE中,采用RMI通过序列化传递对象.在Android中,则采用AIDL(Android Interface Definition Language:接口定义语言)方式实现.
AIDL是一种接口定义语言,用于约束两个进程间的通讯规则,供编译器生成代码,实现Android设备上的两个进程间通信(IPC),而不是通过共享内存实现!
进程之间的通信信息,首先会被转换成AIDL协议消息,然后发送给对方,对方收到AIDL协议消息后再转换成相应的对象.
由于进程之间的通信信息需要双向转换,所以android采用代理类在背后实现了信息的双向转换,代理类由android编译器生成,对开发人员来说是透明的.
编写Aidl文件时,需要注意下面几点:
1.接口名和aidl文件名必须相同.
2.接口和方法前不用加访问权限修饰符public,private,protected等,也不能用final,static.
3.Aidl默认支持的类型包话java基本类型(int,long,boolean等)和(String,List,Map,CharSequence),使用这些类型时不需要import声明.
对于List和Map中的元素类型必须是Aidl支持的类型.如果使用自定义类型作为参数或返回值,自定义类型必须实现Parcelable接口.
4.自定义类型和AIDL生成的其它接口类型在aidl描述文件中,应该显式import,即便在该类和定义的包在同一个包中.
5.在aidl文件中所有非Java基本类型参数必须加上in,out,inout标记,以指明参数是输入参数,输出参数还是输入输出参数.
6.Java原始类型默认的标记为in,不能为其它标记.
参考资料:
http://www.cnblogs.com/over140/archive/2011/03/08/1976890.html
Thank you very much
以下为客户端:
MainActivity如下:
package cn.com.remoteServiceClient; import com.cn.aidl.IRemoteQuery; import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; //客户端步骤: //1 自定义ServiceConnection类实现ServiceConnection接口 //即RemoteQueryServiceConnection implements ServiceConnection //重写其方法public void onServiceConnected(ComponentName name, IBinder service) //主要目的就是接受服务端传过来的Binder对象,即方法的参数IBinder service. //但是因为存在AIDL协议的转换,所以IBinder service是一个代理对象,我们要强转,将其转换为接口类型对象.代码如下: // public void onServiceConnected(ComponentName name, IBinder service) { // remoteQueryBinder=IRemoteQuery.Stub.asInterface(service);//强转!! // } // //2 利用隐式意图激活远程服务 // //3 利用remoteQueryBinder调用服务里面的方法 // // // //AIDL的客户端的总结: // (1) 采用的是隐式意图去激活服务service,因为服务和客户端不在同一个应用! // 彼此看不见,所以是"隐"的,那么就要采用隐式意图去激活,所以在文件清单里 // 声明服务组件的时候,还要给其配置意图过滤器! // (2) 在onServiceConnected()方法里接收服务端返回来的Binder对象时一定要注意!!! // 这里和本地服务的情况不一样!!!因为存在AIDL协议的转换,所以IBinder service是一个代理对象 // 我们要强转将其转换为接口类型对象remoteQueryBinder // 即remoteQueryBinder=IRemoteQuery.Stub.asInterface(service);实现转换 // (3) 核心总结: // 现在把两个例子都写完了,再往回看,那么可以发现其实本地服务和远程服务 // 都在围绕一个核心:得到一个Binder对象--->这个Binder对象有一个显著的 // 特点:可以实现与服务的绑定,且可以完成一些业务 // // 在本地服务里面的实现是:Binder对象的类继承自Binder且实现了业务的接口 // 那么在接收此Binder对象的时候,当然可以用此接口来接收(父类引用指向子类对象嘛) // // 在远程服务里面实现是:我们要写一个aidl文件. // 然后由AIDL自动生成了一个很大的接口,在此接口中最核心是Stub类!它继承自Binder而且 // 实现了我们定义的业务接口!所以返回来的Binder代理对象(注意是代理对象!) // 既有"绑定"到服务的能力,也有完成业务方法的能力 // // 所以在本地和远程中我们都可以用业务接口来接受返回来的Binder对象或Binder代理对象 public class MainActivity extends Activity { TextView numberTextView; TextView resultTextView; Button button; RemoteQueryServiceConnection conn=new RemoteQueryServiceConnection(); IRemoteQuery remoteQueryBinder; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); numberTextView=(TextView) findViewById(R.id.number); resultTextView=(TextView) findViewById(R.id.result); button=(Button) findViewById(R.id.button); button.setOnClickListener(new ButtonOnClickListener()); Intent service=new Intent(); service.setAction("com.cn.remoteService.RemoteQueryService"); bindService(service, conn, this.BIND_AUTO_CREATE);//绑定服务 } private class ButtonOnClickListener implements OnClickListener{ public void onClick(View v) { String number=numberTextView.getText().toString(); String result; try { result = remoteQueryBinder.queryByNum(Integer.valueOf(number)); resultTextView.setText(result); } catch (Exception e) { e.printStackTrace(); } } } //接收绑定的服务和解除服务 private final class RemoteQueryServiceConnection implements ServiceConnection{ public void onServiceConnected(ComponentName name, IBinder service) { remoteQueryBinder=IRemoteQuery.Stub.asInterface(service); } public void onServiceDisconnected(ComponentName name) { remoteQueryBinder=null; } } protected void onDestroy() { unbindService(conn); super.onDestroy(); } }
main.xml如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/number" /> <EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/number" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/button" android:id="@+id/button" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/result" /> </LinearLayout>
以下为服务端:
RemoteQueryService如下:
package com.cn.remoteService; import com.cn.aidl.IRemoteQuery; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; //服务端步骤: //1 生成aidl文件 //建立一个接口,里面可以有各种方法. //将此接口稍作修改,让"接口名"和"方法名"都没有修饰(如public)!!代码如下: //interface IRemoteQuery { // String queryByNum(int number); //} // 然后找到这个被修改后接口的文件路径,按照此路径在硬盘上将其后缀改为.aidl //这样就生成了aidl文件.在此过程中注意:接口名和aidl文件名必须相同. //2 刷新工程,系统会自动生成用于用于远程通信的IRemoteQuery.java // 分析此IRemoteQuery.java //(1)IRemoteQuery.java内部实际是一个名叫IRemoteQuery的接口 //(2)该IRemoteQuery接口内部最重要的是一个Stub类(即IRemoteQuery.Stub),此类继承自Binder类且实现了IRemoteQuery业务接口 // 所以该类的对象具有远程访问的能力 // //3在客户端建立一个包,包的名称与aidl文件所在的包名一致!然后将服务端的aidl文件拷贝到此包下,然后刷新 //发现在客户端的gen下生成了IRemoteQuery.java // //4 自定义远程服务类(RemoteQueryService),其继承自service // //5 在RemoteQueryService里面写一个内部类RemoteQueryBinder继承自IRemoteQuery.Stub //即RemoteQueryBinder extends IRemoteQuery.Stub //6 重写服务的public IBinder onBind(Intent intent)方法,返回一个Binder对象即RemoteQueryBinder类对象给客户端 // // //关于AIDL的服务端的总结: //(1) 自动生成的Stub是核心重点,从生成的代码可以看出:它继承自Binder而且实现了我们定义的业务接口 // 所以它既可以有绑定的能力也有调用业务的能力(这点和刚才写的调用本地服务的例子有异曲同工之妙) //(2) AIDL的定义和接口很类似,但是"接口名"和"方法名"都没有修饰!!!!比如public //(3) 在客户端和服务端都要此包!!因为这相当于一个通信协议!!!双方都必须遵守,所以一式两份!!! public class RemoteQueryService extends Service { @Override public IBinder onBind(Intent intent) { return remoteQueryBinder; } RemoteQueryBinder remoteQueryBinder=new RemoteQueryBinder(); private String [] names=new String [] {"小明","小王","小杨","小李"}; private final class RemoteQueryBinder extends IRemoteQuery.Stub{ @Override public String queryByNum(int number) throws RemoteException { return query(number); } } public String query(int i){ if(i>0&&i<5){ return names[i-1]; } return "查询错误,请再次输入"; } }
以下为AIDL文件:
IRemoteQuery如下:
/* * This file is auto-generated. DO NOT MODIFY. * Original file: D:\\workspace\\queryByRemoteService\\src\\com\\cn\\aidl\\IRemoteQuery.aidl */ package com.cn.aidl; /** *注意: *此AIDL的定义和接口很类似,但是"接口名"和"方法名"都没有修饰!!!!比如public */ public interface IRemoteQuery extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.cn.aidl.IRemoteQuery { private static final java.lang.String DESCRIPTOR = "com.cn.aidl.IRemoteQuery"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.cn.aidl.IRemoteQuery interface, * generating a proxy if needed. */ public static com.cn.aidl.IRemoteQuery asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.cn.aidl.IRemoteQuery))) { return ((com.cn.aidl.IRemoteQuery)iin); } return new com.cn.aidl.IRemoteQuery.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_queryByNum: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); java.lang.String _result = this.queryByNum(_arg0); reply.writeNoException(); reply.writeString(_result); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.cn.aidl.IRemoteQuery { 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 java.lang.String queryByNum(int number) 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.writeInt(number); mRemote.transact(Stub.TRANSACTION_queryByNum, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_queryByNum = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } public java.lang.String queryByNum(int number) throws android.os.RemoteException; }