笔记摘要:
在Android中,每个应用都有自己的进程,当需要在不同的进程之间传递对象时,由于java不支持跨进程内存共享,因此要传递对象,需要把对象解析成操作系统能够理解的数据格式,以达到跨界对象访问的目的。在javaEE中,采用RMI通过序列化传递对象,在Android中,则采用AIDL(Android Interface Definition Language:接口定义语言)方式实现。
AIDL是一种接口定义语言,用于约束两个进程间的通讯规则,供编译器生成代码,实现 Android设备上的两个进程间通信(IPC),AIDL的IPC机制和EJB所采用的CORBA很类似,进程之间的通信信息,首先会被转换成AIDL协议消息,然后发送给对方,对方收到AIDL协议消息后再转换成相应的对象。由于进程之间的通信信息需要双向转换,所以Android采用代理类在背后实现了信息的双向转换,代理类由Android编译器生成,对开发人员来说是透明的。
所以我们在定义服务端的时候需要定义一个AIDL类型的文件指定一种协议来确保服务端和客户端的通信。
在B应用中创建“aidl文件,aidl文件的定义和接口的定义很类似,所以我们可以新建一个接口StudentQuery,在接口中定义一些方法后,修改该文件类型为aidl,即可完成该aidl文件的创建。
编写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,不能为其它标记。
程序中需要注意:
当完成aidl文件创建后,eclipse会自动在项目中的gen目录中同步生成StudentQuery.java接口文件。接口文件生成一个Stub的抽象类,里面包括aidl定义的方法,还包括一些其他辅助方法。值得注意的是asInterface(Ibinder iBinder)返回接口类型的实例,对于远程服务调用,远程服务返回给客户端的对象为代理对象,客户端在onServiceConnected(ComponentName name,IBinder service)方法引用该对象时,不能直接强转成接口类型的实例,而应该使用asInterface(IBinder iBinder)进行类型转换。
说明:
这里通过在一个Android客户端进程中向Android服务端发送业务请求,服务端进行处理后,返回给客户端结果。和本地服务一样,它们之间同样通过Binder来进行通信
AIDL文件
package cn.xushuai.aidl; //AIDL描述,用于编译器生成通讯协议 interface StudentQuery { String queryStudent(int number); }
注册业务类,同时定义一个激活名,用于客户端调用该业务类
<service android:name=".StudentQueryService"> <!-- 采用隐式激活服务,激活名称为cn.xushuai.student.query --> <intent-filter > <action android:name="cn.xushuai.student.query"/> </intent-filter> </service>
说明:
这里的Binder继承的是gen根目录下,AIDL文件生成的相应的文件中的StudentQuery接口的内部类Stub,它已经继承了Binder对象,
这样做的目的就是为了让该服务具有远程通讯能力。
但是这里的binder对象是一个代理。
package cn.xushuai.remote.service; import cn.xushuai.aidl.StudentQuery.Stub; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; public class StudentQueryService extends Service { private String[] names = {"张三","李四","王五"}; private IBinder binder = new StudentQueryBinder(); @Override public IBinder onBind(Intent intent) { return binder; //返回给客户端一个具有远程通讯能力的binder对象,其实是一个代理 } private String query(int number){ if(number > 0 && number < 4){ return names[number-1]; } return null; } //为了让该服务具有远程通讯的能力,所以继承编译器生成的通讯类中的Stub,它继承自Binder private final class StudentQueryBinder extends Stub{ public String queryStudent(int number) throws RemoteException { return query(number); } } }
同样在客户端也需要相同的AIDL文件
package cn.xushuai.aidl; //AIDL描述,用于编译器生成通讯协议 interface StudentQuery { String queryStudent(int number); }
客户端页面代码
<EditText android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/studentno" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/studentno" android:id="@+id/button" android:text="@string/button" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_below="@id/button" android:id="@+id/resultView" />
package cn.xushuai.remoteservice.client; import cn.xushuai.aidl.StudentQuery; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.app.Activity; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class RemoteServiceClient extends Activity { private EditText studentno; private TextView resultView; private StudentServiceConnection conn = new StudentServiceConnection(); private StudentQuery studentQuery; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_remote_service_client); studentno = (EditText)this.findViewById(R.id.studentno); Button button = (Button) this.findViewById(R.id.button); resultView = (TextView) this.findViewById(R.id.resultView); button.setOnClickListener(new ButtonClickListener()); Intent service = new Intent("cn.xushuai.student.query"); bindService(service, conn, BIND_AUTO_CREATE);//绑定后自动创建服务 } //实现一个服务连接对象 private class StudentServiceConnection implements ServiceConnection{ public void onServiceConnected(ComponentName name, IBinder service) { //将返回的binder代理转换为接口对象 studentQuery = StudentQuery.Stub.asInterface(service); } public void onServiceDisconnected(ComponentName name) { studentQuery = null; } } //当Activity摧毁的时候,解除与服务的绑定 @Override protected void onDestroy() { unbindService(conn); super.onDestroy(); } private final class ButtonClickListener implements View.OnClickListener{ public void onClick(View v) { String number = studentno.getText().toString(); int num = Integer.valueOf(number); try { String name = studentQuery.queryStudent(num); resultView.setText(name); } catch (RemoteException e) { e.printStackTrace(); } } } }