您的位置:首页 > 大数据 > 人工智能

服务(Service)全解析(四)--AIDL

2013-08-18 12:12 309 查看
在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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐