您的位置:首页 > 移动开发 > Android开发

Android AIDL的使用与理解

2017-05-03 17:02 513 查看
AIDL(Android Interface Definition Language),它是Android接口定义语言。它是一种辅助性语言。使用它来达到自动生成接口的目的。它实际上还是使用binder来达到进程间通讯的一个辅助工具。

我们在使用binder时,实际上需要达到两个目的。一个是通讯逻辑的实现,即要找到谁。二是业务逻辑的实现,即要做什么。

通讯逻辑上,都是一样的,所以代码上都是一样的。业务逻辑则是无法统一的(你要做什么事情,只有你自己知道)。

AIDL就是为了方便的实现这两个目的的。

我们先来实现服务端:

使用eclipse新建一个android项目,然后创建一个基础类Person.java:

package com.zyftest.myipc;

import android.os.Parcel;
import android.os.Parcelable;

public class Person implements Parcelable{
private String mName;
private String mICCard;

public String getName()
{
return this.mName;
}
public void setName(String name)
{
this.mName=name;
}

public String getICCard()
{
return this.mICCard;
}
public void setICCard(String card)
{
this.mICCard=card;
}
@Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
// TODO Auto-generated method stub
dest.writeString(mName);
dest.writeString(mICCard);
}

public void readFromParcel(Parcel dest) {
//注意,此处的读值顺序应当是和writeToParcel()方法中一致的
mName = dest.readString();
mICCard = dest.readString();
}

public Person() {}

public Person(Parcel in) {
mName = in.readString();
mICCard = in.readString();
}

public static final Creator<Person> CREATOR = new Creator<Person>() {
@Override
public Person createFromParcel(Parcel in) {
return new Person(in);
}

@Override
public Person[] newArray(int size) {
return new Person[size];
}
};
}


在Person 这个类中,我们实现于Parcelable接口。这主要是为了让数据能够序列化,通过序列化在进程间(或网络中)传递对象。

然后我们来写aidl文件 Person.aidl:

// Person.aidl
//这个文件的作用是引入了一个序列化对象 Person 供其他的AIDL文件使用
//注意:Person.aidl与Person.java的包名应当是一样的
package com.zyftest.myipc;

//注意parcelable是小写
parcelable Person;


Person.aidl是为了让Person能供其它的aidl文件使用。

下面我们在写一个实现业务逻辑的aidl IPersonManager.aidl:

package com.zyftest.myipc;

import com.zyftest.myipc.Person;

interface IPersonManager {

//所有的返回值前都不需要加任何东西,不管是什么数据类型
List<Person> getPersons();

//传参时除了Java基本类型以及String,CharSequence之外的类型
//都需要在前面加上定向tag,具体加什么量需而定
void addPerson(in Person person);
}


这个aidl有两个业务接口:getPersons() 和 addPerson(in Person person),在这个aidl里,我们看不出有通讯相关的逻辑。

然后我们重新生成一下项目,此时会在gen目录下,产生一个IPersonManager.java的文件:

/*
* This file is auto-generated.  DO NOT MODIFY.
* Original file: /home/zhangyafei/workspace/MyIpc/MyIPC/src/com/zyftest/myipc/IPersonManager.aidl
*/
package com.zyftest.myipc;

public interface IPersonManager extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements
com.zyftest.myipc.IPersonManager {
private static final java.lang.String DESCRIPTOR = "com.zyftest.myipc.IPersonManager";

/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}

/**
* Cast an IBinder object into an com.zyftest.myipc.IPersonManager
* interface, generating a proxy if needed.
*/
public static com.zyftest.myipc.IPersonManager asInterface(
android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.zyftest.myipc.IPersonManager))) {
return ((com.zyftest.myipc.IPersonManager) iin);
}
return new com.zyftest.myipc.IPersonManager.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_getPersons: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.zyftest.myipc.Person> _result = this
.getPersons();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addPerson: {
data.enforceInterface(DESCRIPTOR);
com.zyftest.myipc.Person _arg0;
if ((0 != data.readInt())) {
_arg0 = com.zyftest.myipc.Person.CREATOR
.createFromParcel(data);
} else {
_arg0 = null;
}
this.addPerson(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}

private static class Proxy implements com.zyftest.myipc.IPersonManager {
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.util.List<com.zyftest.myipc.Person> getPersons()
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.zyftest.myipc.Person> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getPersons, _data,
_reply, 0);
_reply.readException();
_result = _reply
.createTypedArrayList(com.zyftest.myipc.Person.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}

// 传参时除了Java基本类型以及String,CharSequence之外的类型
// 都需要在前面加上定向tag,具体加什么量需而定

@Override
public void addPerson(com.zyftest.myipc.Person person)
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((person != null)) {
_data.writeInt(1);
person.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply,
0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}

static final int TRANSACTION_getPersons = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}

// 所有的返回值前都不需要加任何东西,不管是什么数据类型

public java.util.List<com.zyftest.myipc.Person> getPersons()
throws android.os.RemoteException;

// 传参时除了Java基本类型以及String,CharSequence之外的类型
// 都需要在前面加上定向tag,具体加什么量需而定

public void addPerson(com.zyftest.myipc.Person person)
throws android.os.RemoteException;
}


到此时,之前的两个aidl文件就可以删除了,它们的目的就是为了生成这个java接口。

这个java接口,最主要的是它的这个抽象类abstract class Stub,这是个抽象类,实现了IPersonManager接口的同时,又继承了android.os.Binder。所以它具有了跨进程通讯的能力,同时又有了实现业务逻辑的能力。

接下来,我们来真正的实现相关的业务逻辑 MyIPC_Service.java:

package com.zyftest.myipc;

import java.util.ArrayList;
import java.util.List;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

public class MyIPC_Service extends Service{

public final String TAG = this.getClass().getSimpleName();

private List<Person> mPersons = new ArrayList<Person>();

private final IPersonManager.Stub mPersonManager=new IPersonManager.Stub() {

@Override
public List<Person> getPersons() throws RemoteException {
// TODO Auto-generated method stub
synchronized (this) {
Log.e(TAG, "invoking getPersons() method , now the list is : " + mPersons.toString());
if (mPersons != null) {
return mPersons;
}
return new ArrayList<Person>();
}
}

@Override
public void addPerson(Person person) throws RemoteException {
// TODO Auto-generated method stub
synchronized (this) {
if (mPersons == null) {
mPersons = new ArrayList<Person>();
}
if (person == null) {
Log.e(TAG, " is null in In");
person = new Person();
}

person.setICCard("1111111111111111");
if (!mPersons.contains(person)) {
mPersons.add(person);
}
//打印Person列表,观察客户端传过来的值
Log.e(TAG, "invoking addPerson() method , now the list is : " + mPersons.toString());
}
}
};

@Override
public void onCreate() {
super.onCreate();
Person person = new Person();
person.setName("刘备");
person.setICCard("0000000000000000");
mPersons.add(person);
}

@Override
public IBinder onBind(Intent intent) {
Log.e(getClass().getSimpleName(), String.format("on bind,intent = %s", intent.toString()));
return mPersonManager;
}

@Override
public boolean onUnbind(Intent intent) {
// TODO Auto-generated method stub
Log.d("zyfipctest","onUnbind...stopSelf");
System.exit(0);
return super.onUnbind(intent);
}

}


它有一个重要的成员private final IPersonManager.Stub mPersonManager,注意到它就是我们使用aidl生成的IPersonManager下的stub这个抽象类。并真正的实现了getPersons和addPerson这两个接口函数。

接下来,我们来实现客户端的访问功能:

新建一个android项目,并将上面服务端产生的Person.java和IPersonManager.java拷贝过去,并建立相应的包。(注意:这个包名是要与服务端的包名一样的)

然后,我们来做相关的访问代码 MainActivity.java:

package com.zyftest.ipcclient;

import java.util.List;

import com.zyftest.myipc.IPersonManager;
import com.zyftest.myipc.Person;

import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends Activity {

private IPersonManager mPersonManager;

private Button setName, getName;
private TextView show;
private EditText input;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//获取各组件
setName = (Button)findViewById(R.id.set);
getName = (Button)findViewById(R.id.get);
show = (TextView)findViewById(R.id.show);
input = (EditText)findViewById(R.id.input);

//为设置按钮绑定监听
setName.setOnClickListener(new OnClickListener()
{

@Override
public void onClick(View v)
{
// TODO Auto-generated method stub
if(input.getText() != null)
{
try
{
//将输入的信息保存到远程服务端
Person person=new Person();
person.setName(input.getText().toString());
person.setICCard("2222222222");
mPersonManager.addPerson(person);
}
catch (RemoteException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});

//获取按钮绑定监听
getName.setOnClickListener(new OnClickListener()
{
//String name = mIMyService.getName();
@Override
public void onClick(View v)
{
// TODO Auto-generated method stub
try {
List<Person> persons= mPersonManager.getPersons();
if(persons!=null&&persons.size()>0)
{
StringBuilder sbuilder=new StringBuilder();
for(Person person : persons)
{
if(person.getName() != null)
{
sbuilder.append(person.getName());
sbuilder.append(" : ");
sbuilder.append(person.getICCard());
sbuilder.append(" | ");
}
}
show.setText(sbuilder.toString());
}
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}

@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
//创建Intent,对应服务端注册的Intent
Log.d("zyfipctest","onResume......bindService");
final Intent intent = new Intent();
intent.setAction("com.zyftest.action.myipcservice");
//绑定连接远程服务
bindService(intent, conn, Service.BIND_AUTO_CREATE);

/*
final Intent intent_explicit = new Intent(this,MyIPC_Service.class);
Log.d("zyfipctest","onCreate......bindService");
bindService(intent_explicit,conn,Service.BIND_AUTO_CREATE);
*/
}

@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
Log.d("zyfipctest","onPause......unbindService");
//解除绑定
unbindService(conn);
}

@Override
protected void onDestroy()
{
// TODO Auto-generated method stub
super.onDestroy();
Log.d("zyfipctest","onDestroy......unbindService");
//解除绑定
//unbindService(conn);
}

//实现客户端与服务端绑定的关键部分
private ServiceConnection conn = new ServiceConnection()
{
//解除连接服务端
@Override
public void onServiceDisconnected(ComponentName name)
{
// TODO Auto-generated method stub
mPersonManager = null;
Log.d("zyfipctest","onServiceDisconnected......");
}
//连接服务端
@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
// TODO Auto-generated method stub
/*此处实现获取通过IpcService的onBind方法返回的mServiceBinder对象的代理。
* 参数service为绑定获得的远程服务IpcService的mServiceBinder对象,
* 而调用mPersonManager.Stub的函数asInterface获取的是mServiceBinder对象的代理。
*/
Log.d("zyfipctest","onServiceConnected......");
mPersonManager = IPersonManager.Stub.asInterface(service);
Log.d("zyfipctest","onServiceConnected......mPersonManager = "+mPersonManager);
}
};

}


这里bindService(intent, conn, Service.BIND_AUTO_CREATE);高版本已经不支持这种匿名绑定了,所以这里我们在AndroidManifest.xml里限制SDK的版本号 ,将

<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="21" />
改为
<uses-sdk android:minSdkVersion="15" />


我们知道系统的服务都会向ServiceManager注册,这样客户端要访问服务时,只要向ServiceManager查询到该服务对应的binder,就能与服务通讯了。

但是我们这里是自定义的匿名服务,并没有向ServiceManager注册,那客户端是如何得到服务的handle或binder引用的呢?答案就在bindService函数。以后我们再来分析bindService,这里我们只要知道无论是客户端还是服务端的进程都是在AMS里启动的,所以AMS持有客户端和服务端双方的进程信息。

所以使用bindService是可以让服务端与客户端通过BINDER通讯的,而使用startService则不行。

在客户端中实现了一个ServiceConnection类,在调用了bindService或unbindService后,会相应的调用到ServiceConnection类中的onServiceConnected和onServiceDisconnected。

我们看到IPersonManager这个接口里有一个抽象类stub,同时stub又有一个内部类Proxy,它也是实现于IPersonManager。为什么要搞的这么复杂呢?

原因在于这个IPersonManager既要在服务端使用,又要在客户端使用。对于客户端来说,使用proxy这个代理类,来调用服务端的函数功能。这个代理类的存在就是为了屏蔽binder的相关信息的。让客户端的调用者像是直接在操作服务端的函数一样。

像在客户端的ServiceConnection类中onServiceConnected函数中:

public void onServiceConnected(ComponentName name, IBinder service)
{
...
mPersonManager = IPersonManager.Stub.asInterface(service);
...
}


返回的就是proxy对象,而Proxy真正的执行者是它的mRemote这个成员对象,

private android.os.IBinder mRemote;可以看出它仅是一个IBinder引用而已,

而此时调用mPersonManager.addPerson时实现上就是调用了Proxy类中的addPerson函数:

@Override
public void addPerson(com.zyftest.myipc.Person person)
throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((person != null)) {
_data.writeInt(1);
person.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply,
0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}


最终就是通过mRemote这个binder引用去传送数据给服务端。

而在服务端中,实现的却是private final IPersonManager.Stub mPersonManager=new IPersonManager.Stub(){…}

这样在客户端使用了mRemote.transact后,服务端会在stub.onTransact函数中处理请求:

case TRANSACTION_getPersons: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.zyftest.myipc.Person> _result = this.getPersons();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}


这里的
java.util.List<com.zyftest.myipc.Person> _result = this.getPersons();
就会调到MyIPC_Service.java 的IPersonManager.Stub mPersonManager中的:

public List<Person> getPersons() throws RemoteException {
// TODO Auto-generated method stub
synchronized (this) {
Log.e(TAG, "invoking getPersons() method , now the list is : " + mPersons.toString());
if (mPersons != null) {
return mPersons;
}
return new ArrayList<Person>();
}
}


然后把结果打包并写回binder驱动中,返回给客户端。

所以,IPersonManager.java既是给客户端用的,也是给服务端用的。客户端主要是使用里边的stub.proxy的抽象类,服务端主要是使用里边的stub抽象类。(这个描述不准确,意思理解了就行)。

在 IPersonManager.java中定义了一些操作:

static final int TRANSACTION_getPersons = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

这些是为了在进程通讯中使用一个统一的协议。

RPC(Remote Procedure Call Protocol)——远程过程调用协议

毕竟,远程通讯或者说跨进程调用中,使用一套统一的协议才能方便通讯。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息