转自:http://blog.youkuaiyun.com/yangzhaomuma/article/details/50576017
AIDL简义
Android中的数据传输、方法调用等,常见的是集中在应用程序内的Activity之间,如Activity-A传递到Activity-B。
这样的数据传输、方法等都是在一个应用程序间调用,也就是在一个进程内。那如果我们要在不同的进程间传递数据,我们要怎么做呢?不在同一个进程间,它们是无法共用内存的,Android为了实现进程间的数据共享,提供了AIDL机制(安卓远程接口定义语言)——(AIDL: Android Interface definition language)。
AIDL的原理以及原理分析,可参考网上其他的解释。
下文将主要介绍AIDL的各种修饰符(in、out、inout)以及类序列化Parcelable的使用示例(网上原理解释很多,但对out、inout的示例很少见到)。
AIDL的实现
AIDL的应用场景,一般情况下是有两个进程,一个进程提供方法,一个进程调用方法。
我们习惯将提供方法的进程定义为Service端、将调用方法的进程定义为Client,就是我们常说的AIDL服务端和AIDL客户端。
AIDL的数据传输支持类型有特殊要求,并非所有的数据类型都能像以往一样传递:
支持数据类型如下:
1. Java 的原生类型
2. String 和CharSequence
3. List 和 Map ,List和Map 对象的元素必须是AIDL支持的数据类型; 以上三种类型都不需要导入(import)
4. AIDL 自动生成的接口 需要导入(import)
5. 实现android.os.Parcelable 接口的类. 需要导入(import)。
那我们接下来演示,如何提供AIDL的服务端和客户端。
这里重点是in、out、inout修饰符以及Parcelable的使用!常见的是in、Parcelable,少用的out、inout。
这几种修饰符,可理解如下:
in:客户端的参数输入;
out:服务端的参数输入;
inout:这个可以叫输入输出参数,客户端可输入、服务端也可输入。客户端输入了参数到服务端后,服务端也可对该参数进行修改等,最后在客户端上得到的是服务端输出的参数。
AIDL的服务端(Service端)实现
常用做法:
1、定义一个AIDL接口,在该接口中写方法;
2、方法中参数修饰符可以是in、out、inout,也有自定义的类,该类需要实现Parcelable接口;
3、实现该接口;
4、开放给客户端一个标志,用于访问服务端接口方法。
按以上的三个步骤,我们来写下示例代码:
1、定义AIDL接口:
新建一个文件,文件名为IBase.aidl,内容为:
- package com.example.aidl;
- import com.example.aidl.UserInfo;//注意引用
- interface IBase
- {
- int add(int i,int j);
- String getUserInfo(in UserInfo userinfo);
- void getaList(out String[] list);
- void setaList(in String[] list);
- void gettList(inout String[] list);
- }
上方的接口中的方法,我们演示了各种修饰符以及Parcelable。
这里有个需要注意的地方,我们在文件头中有import一个类,这个是必要的,虽然UserInfo类和我们定义的接口是在同一个包下。
Parcelable的使用,我们首先要实现这个UserInfo的Parcelable接口实现,然后引用它,如下:
- package com.example.aidl;
-
- import android.os.Parcel;
- import android.os.Parcelable;
-
- public class UserInfo implements Parcelable{
-
-
- private String name;
- private String adress;
- private int age;
-
- /**
- * @return the name
- */
- public String getName() {
- return name;
- }
-
- /**
- * @param name the name to set
- */
- public void setName(String name) {
- this.name = name;
- }
-
- /**
- * @return the adress
- */
- public String getAdress() {
- return adress;
- }
-
- /**
- * @param adress the adress to set
- */
- public void setAdress(String adress) {
- this.adress = adress;
- }
-
- /**
- * @return the age
- */
- public int getAge() {
- return age;
- }
-
- /**
- * @param age the age to set
- */
- public void setAge(int age) {
- this.age = age;
- }
-
- @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(name);
- dest.writeString(adress);
- dest.writeInt(age);
- }
-
- public static final Parcelable.Creator<UserInfo> CREATOR=new Creator<UserInfo>() {
-
- @Override
- public UserInfo createFromParcel(Parcel source) {
- // TODO Auto-generated method stub
- UserInfo userInfo=new UserInfo();
- userInfo.setName(source.readString());
- userInfo.setAdress(source.readString());
- userInfo.setAge(source.readInt());
- return userInfo;
- }
-
- @Override
- public UserInfo[] newArray(int size) {
- // TODO Auto-generated method stub
- return new UserInfo[size];
- }
- };
- }
声明这个自定义类:
在同一个包下,创建一个UserInfo.aidl文件,内容如下:
- package com.example.aidl;
-
- parcelable UserInfo;
综合以上,在使用自定义类时,需要有几个步骤:
(3)在使用该类时,需要在文件头引用(import)。
2、实现接口方法:
新建一个java文件,我们暂命名为:AIDLService.java,该文件是实现AIDL的接口。内容如下:
- package com.example.aidl_server_csdn;
-
-
- import com.example.aidl.IBase;
- import com.example.aidl.UserInfo;
- import android.app.Service;
- import android.content.Intent;
- import android.os.IBinder;
- import android.os.RemoteException;
-
- public class AIDLService extends Service{
-
- String info="";
- @Override
- public IBinder onBind(Intent intent) {
- // TODO Auto-generated method stub
- return stub;
- }
-
- private IBase.Stub stub=new IBase.Stub() {
-
- /**
- * 基本类型的使用示例
- */
- @Override
- public int add(int i, int j) throws RemoteException {
- // TODO Auto-generated method stub
- return i+j;
- }
- /**
- * Parcelable类userinfo的使用示例
- */
- @Override
- public String getUserInfo(UserInfo userinfo) throws RemoteException {
- // TODO Auto-generated method stub
- String resultString="name:"+userinfo.getName()+" "+"adress:"+userinfo.getAdress()+" "+"age:"+userinfo.getAge();
- return resultString;
- }
- /**
- * out修饰类型的使用
- * 表示服务端输入
- */
- @Override
- public void getaList(String[] list) throws RemoteException {
- // TODO Auto-generated method stub
- list[0]="服务端赋值信息:"+info;
- }
-
- /**
- * inout修饰类型的使用示例
- */
- @Override
- public void gettList(String[] list) throws RemoteException {
- // TODO Auto-generated method stub
- String totalString="";
- /**
- * 从客户端取得的信息
- */
- String receviceFromClientString="";
- for(int i=0;i<list.length;i++)
- {
- receviceFromClientString+=list[i];
- }
- /**
- * 从服务端返回的信息
- */
- totalString+="从客户端收到的信息为:"+receviceFromClientString+" \n在此我们新增一段返回信息:good";
- list[0]=totalString;
- }
- /**
- * in修饰类型的使用示例
- */
- @Override
- public void setaList(String[] list) throws RemoteException {
- // TODO Auto-generated method stub
- /**
- * 取得客户端传入的值
- */
- if(list.length>0)
- info=list[0];
- }
-
- };
- }
3、开放一个标志,用于客户端访问
常用的做法,我们可以在AndroidManifest.xml中做如下定义:
- <application
- android:allowBackup="true"
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@style/AppTheme" >
- <service
- android:name="com.example.aidl_server_csdn.AIDLService"
- >
- <intent-filter>
- <action android:name="com.service.use"></action>
- </intent-filter>
- </service>
- </application>
我们设定了一个过滤值:com.service.use,客户端可以通过这个来访问服务端。
至此,我们服务端的代码就写完了。当你运行该服务端在android系统上时,系统会安装一个service.apk并运行。
AIDL客户端(Client端)的实现
服务端已经实现好了,那客户端要如何调用呢?
按我们朴素的思想,应该就是获取服务端的实例,并用这个实例调用相应的方法了。AIDL也是这么想的。但AIDL的做法有点特别。
1、复制服务端的AIDL接口和Parcelable类等到服务端(习惯的做法,将AIDL的整个包都复制到客户端);
2、连接服务端;
3、获取服务端的接口实现的实例;
4、调用方法;
我们也按这步骤来实现我们的客户端。
1、复制整个AIDL包到客户端
这个你复制,黏贴即可。
2、连接服务端
- /**
- * 连接AIDL
- */
- public void Connect()
- {
- bindService(new Intent("com.service.use"), serviceConnection, Context.BIND_AUTO_CREATE);
- }
- /**
- * 连接类实现
- */
- ServiceConnection serviceConnection=new ServiceConnection() {
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- // TODO Auto-generated method stub
- iBase=null;
- Toast.makeText(MainActivity.this, "连接断开", Toast.LENGTH_SHORT).show();
- }
-
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- // TODO Auto-generated method stub
- iBase=IBase.Stub.asInterface(service);
- Toast.makeText(MainActivity.this, "连接成功", Toast.LENGTH_SHORT).show();
- }
- };
-
3、获取服务端的实例
其实,这个一般在连接服务端成功的时候,就已经做了,就如上面代码中的iBase=IBase.Stub.asInterface(service);
4、调用方法
我们在上一步骤中,已经获得了iBase实例,调用方法时,我们用以下方法:
iBase.add(7, 8);
iBase.setaList(new String[]{"战国剑"});
等。
下面,贴出客户端调用的所有代码:
- package com.example.aidl_csdn;
-
- import com.example.aidl.IBase;
- import com.example.aidl.UserInfo;
-
- import android.os.Bundle;
- import android.os.IBinder;
- import android.os.RemoteException;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.Toast;
- import android.R.integer;
- import android.app.Activity;
- import android.content.ComponentName;
- import android.content.Context;
- import android.content.Intent;
- import android.content.ServiceConnection;
-
- public class MainActivity extends Activity implements OnClickListener{
-
- IBase iBase;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- Button btn=(Button)findViewById(R.id.btn);
- btn.setOnClickListener(this);
- Button btn1=(Button)findViewById(R.id.btn1);
- btn1.setOnClickListener(this);
- Button btn2=(Button)findViewById(R.id.btn2);
- btn2.setOnClickListener(this);
- Button btn3=(Button)findViewById(R.id.btn3);
- btn3.setOnClickListener(this);
- Button btn4=(Button)findViewById(R.id.btn4);
- btn4.setOnClickListener(this);
- Button btn5=(Button)findViewById(R.id.btn5);
- btn5.setOnClickListener(this);
- }
-
- /**
- * 连接AIDL
- */
- public void Connect()
- {
- bindService(new Intent("com.service.use"), serviceConnection, Context.BIND_AUTO_CREATE);
- }
- /**
- * 连接类实现
- */
- ServiceConnection serviceConnection=new ServiceConnection() {
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- // TODO Auto-generated method stub
- iBase=null;
- Toast.makeText(MainActivity.this, "连接断开", Toast.LENGTH_SHORT).show();
- }
-
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- // TODO Auto-generated method stub
- iBase=IBase.Stub.asInterface(service);
- Toast.makeText(MainActivity.this, "连接成功", Toast.LENGTH_SHORT).show();
- }
- };
-
- /**
- * 基础类型相加
- * @return
- * @throws RemoteException
- */
- public int sum() {
- if(iBase!=null)
- {
- int result=0;
- try {
- result = iBase.add(7, 8);
- Toast.makeText(getApplicationContext(), "基础类型相加结果:"+result, Toast.LENGTH_SHORT).show();
- } catch (RemoteException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return result;
- }
- return 0;
- }
- /**
- * in型传值到服务端
- */
- public void setaList()
- {
- if(iBase!=null)
- {
- try {
- iBase.setaList(new String[]{"战国剑"});
- Toast.makeText(getApplicationContext(), "传值'战国剑'到服务端", Toast.LENGTH_SHORT).show();
- } catch (RemoteException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- /**
- * out型取服务端返回值
- */
- public void getaList()
- {
- if(iBase!=null)
- {
- String[] list =new String[1];
- try {
- iBase.getaList(list);
- Toast.makeText(getApplicationContext(), "服务端返回内容:"+list[0], Toast.LENGTH_SHORT).show();
- } catch (RemoteException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- /**
- * Parcelable类的传入
- */
- public void ParcelableUse()
- {
- if(iBase==null)
- return;
- UserInfo userInfo=new UserInfo();
- userInfo.setName("战国剑");
- userInfo.setAdress("中国");
- userInfo.setAge(18);
- try {
- String resultString=iBase.getUserInfo(userInfo);
- Toast.makeText(getApplicationContext(), "服务端返回内容:"+resultString, Toast.LENGTH_SHORT).show();
- } catch (RemoteException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- /**
- * inout类型修饰的使用
- */
- public void inoutUse()
- {
- if(iBase==null)
- return;
- try {
- String[] inStrings={"inout中in的传入"};
- iBase.gettList(inStrings);
- Toast.makeText(getApplicationContext(), "inout服务端返回内容:"+inStrings[0], Toast.LENGTH_SHORT).show();
-
- } catch (RemoteException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- switch (v.getId()) {
- case R.id.btn:
- Connect();
- break;
- case R.id.btn1:
- sum();
- break;
- case R.id.btn2:
- ParcelableUse();
- break;
- case R.id.btn3:
- setaList();
- break;
- case R.id.btn4:
- getaList();
- break;
- case R.id.btn5:
- inoutUse();
- break;
-
- default:
- break;
- }
- }
-
- }
至此,我们的客户端实现也完成了。运行后,你就可以看到你想要的效果。
注意信息
这是一个简单的AIDL实例,主要是为了说明AIDL中各种修饰符的使用和自定义类的传递。AIDL中,与我们常用方法不同的也就是这些东西,希望这个例子的分享有助于理解AIDL机制。
源码