一、AIDL的作用
由于每个应用程序都运行在自己的进程空间,并且可以从应用程序UI运行另一个服务进程,而且经常会在不同的进程间传递对象。在Android平台,一个进程通常不能访问另一个进程的内存空间,所以要想对话,需要将对象分解成操作系统可以理解的基本单元,并且有序的通过进程边界。
AIDL(Android Interface Definition Language)是一种IDL语言,用于成可以在Android设备上两个进程之间进行通信(interprocess communication,IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。
注意:AIDL只支持方法,不能定义静态成员,并且方法也不能有类似public等的修饰符。
二、AIDL的使用步骤
1、服务端代码:
第一步:定义一个*.aidl文件,该文件是符合aidl语言定义规范的接口定义,里面定义了外部应用可以访问的方法。当我们保存该文件的时候,eclipse会自动在gen文件夹下生产一个相应的java接口文件。IPerson.aidl代码如下:
package com.example.server;
interface IPerson{
void setValue(String name);
String getValue();
}
第二步:实现AIDL文件生成的JAVA接口Stub。Person.java实现Stub的代码如下:
package com.example.server;
import com.example.server.IPerson;
import android.os.RemoteException;
public class Person extends IPerson.Stub{
private String name;
@Override
public void setValue(String name) throws RemoteException {
// TODO Auto-generated method stub
this.name = name;
}
@Override
public String getValue() throws RemoteException {
// TODO Auto-generated method stub
return this.name;
}
第三步:定义一个service,并将其注册到androidManifest.xml文件。示例代码如下:
package com.example.server;
import com.example.server.IPerson;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class MyService extends Service{
private IPerson.Stub iPerson = new Person();
@Override
public <span style="font-size:18px;">IBinder</span> onBind(Intent intent) {
// TODO Auto-generated method stub
return iPerson;
}
}
<service android:name="MyService">
<intent-filter>
<action android:name="com.example.server.MyService"/>
</intent-filter>
</service>
第四步:在本应用中的Activity中为自己定义的Service赋值。在实现了自己的Service时,为了其他应用可以通过bindService来和Service进行交互,我们要实现service中的onBind()方法,并且返回一个继承了Binder的内部类。AIDL要求我们,在这里不能再直接去实现Binder类了,而是去实现AIDL提供给我们的Stub类。实现stub类的同时,AIDL还要求我们同时实现我们在接口定义中的各种服务的具体实现。至此,我们的服务端已经和我们的aidl文件绑定到一起了。
package com.example.server;
import com.example.server.IPerson;
import com.example.server.R;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends Activity implements OnClickListener{
private Button bindButton;
private Button unbindButton;
private IPerson iPerson;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bindButton = (Button)findViewById(R.id.button1);
unbindButton = (Button)findViewById(R.id.button2);
bindButton.setOnClickListener(this);
unbindButton.setOnClickListener(this);
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.button1:
Intent intent = new Intent(MainActivity.this,MyService.class);
bindService(intent, conn, Service.BIND_AUTO_CREATE);
break;
case R.id.button2:
unbindService(conn);
break;
default:
break;
}
}
private ServiceConnection conn = new ServiceConnection() {
//连接对象
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
iPerson = IPerson.Stub.asInterface(service);
if(iPerson!=null){
try {
iPerson.setValue("AIDL TEST");
Toast.makeText(MainActivity.this, "赋值成功", Toast.LENGTH_LONG).show();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Toast.makeText(MainActivity.this, "赋值失败", Toast.LENGTH_LONG).show();
}
}
}
};
}
2、客户端代码:
第一步:客户端要想使用该服务,首先要知道我们的服务在aidl文件中到底对外提供了什么服务。所以要将服务端的暴露的接口文件aidl文件拷贝一份到客户端的工程中(注意:aidl文件包的路径和服务端的一定要保持一致,否则会报错,报错见后面分析)。
第二步:想要和service交互,我们要通过bindService方法,该方法有一个ServiceConnection类型的参数,而主要的代码便是在该接口中实现的。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = (Button)findViewById(R.id.button1);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent();
intent.setAction("com.example.server.MyService");
bindService(intent, conn, Service.BIND_AUTO_CREATE);
}
});
}
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
}
@Override
public synchronized void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
person = IPerson.Stub.asInterface(service);
if(person != null){
try {
String name1 = person.getValue();
Toast.makeText(MainActivity.this, "远程调用成功,值为:"+name1, Toast.LENGTH_LONG).show();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
Toast.makeText(MainActivity.this, "远程调用失败", Toast.LENGTH_LONG).show();
}
}
}
};
三、报错的情况:java.lang.SecurityException: Binder invocation to an incorrect interface
在使用AIDL远程服务时,经常会遇到上面的报错。发生这样的报错,最主要的原因是上面要注意的地方。也就是客户端的*.aidl文件的包路径与服务器端的包路径不一致导致的。
完整工程代码:http://download.youkuaiyun.com/detail/yegucheng2618/7802357
参考文章 http://blog.youkuaiyun.com/macdroid/article/details/8448742
http://blog.youkuaiyun.com/myxmu/article/details/7921521