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

在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;
}


 

上一篇:OpenAI 和DeepMind开源人工智能训练平台


下一篇:企业实施分布式架构的挑战以及应对建议 | 上海 ATEC 大会实录