笔记摘要:
在Android中,每个应用都有自己的进程,当需要在不同的进程之间传递对象时,由于java不支持跨进程内存共享,因此要传递对象,需要把对象解析成操作系统能够理解的数据格式,以达到跨界对象访问的目的。在javaEE中,采用RMI通过序列化传递对象,在Android中,则采用AIDL(Android Interface Definition Language:接口定义语言)方式实现。
一、AIDL介绍
AIDL是一种接口定义语言,用于约束两个进程间的通讯规则,供编译器生成代码,实现 Android设备上的两个进程间通信(IPC),AIDL的IPC机制和EJB所采用的CORBA很类似,进程之间的通信信息,首先会被转换成AIDL协议消息,然后发送给对方,对方收到AIDL协议消息后再转换成相应的对象。由于进程之间的通信信息需要双向转换,所以Android采用代理类在背后实现了信息的双向转换,代理类由Android编译器生成,对开发人员来说是透明的。
所以我们在定义服务端的时候需要定义一个AIDL类型的文件指定一种协议来确保服务端和客户端的通信。
二、创建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来进行通信
4.1 服务端
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);
}
}
}
4.2 客户端
同样在客户端也需要相同的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();
}
}
}
}