AIDL:是 Android Interface definition language的缩写,它是一种android内部进程通信接口的描述语言,通过它我们可以用来不同进程间的通信。
最初接触aidl的时候,只是敲了一下教材上的代码,没有静下来思考过,感觉挺复杂的,由于畏难情绪在作祟,且目前还没有需要用到aidl的项目,所以一直就没有好好的研究过。今天仔细的看了一下,简单的梳理了一下流程,感觉其实不难的,所以在这里,我会用自己的方式描述一下它的整个的工作流程(可能原理上不是这样的)。
一、服务端部分
首先我们要定义服务器端的aidl文件,在我们写好aidl文件时,然后 Android Eclipse 插件会自动帮你 根据这个 AIDL 文件生成 Java文件,在gen目录下,并且和其包名对应。
package com.duke.aidl;
interface IService {
String setValue(String value);
String getValue(String value);
}
当然客户端程序程序所用到的aidl文件和服务器端是一样的,并且双方aidl文件所在的包名也要相同。
aidl里面声明的方法也就是客户端程序能够调用的服务器端的方法。
然后就是在服务类中实现aidl文件里声明的方法
package com.duke.aidl;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class ServiceRunning extends Service{
String mvValue="duke";
public class ServiceRunningImpl extends IService.Stub{
//实现方法就是通过继承aidl文件自动生成的java文件里的内部类实现的,我们不需要深究它是如何自动生成的,也不需要知道它里面有什么东西
@Override
public String setValue(String value) throws RemoteException {
// TODO Auto-generated method stub
mvValue=value;
return mvValue;
}
@Override
public String getValue(String value) throws RemoteException {
// TODO Auto-generated method stub
return value+":"+mvValue;
}
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return new ServiceRunningImpl();
}
}
在这个服务类中大家可以看到我们实现了 onBind() 方法。从AIDL文件生成的 Stub类是抽象类并且它实现了 IService接口。在我们的服务实现中,有一个扩展了 Stub类得内部类,名为 ServiceRunningImpl。此类充当着远程服务实现,而且 onBind()方法会返回此类的实例。到此,我们有了一个有效的 ADIL服务,但是外部的客户端还无法连接到它。
最后呢要将服务向客户端公开,需要在AndroidManifest.xml文件中添加服务声明,而这一次我们需要一个Intent 过滤器来公开服务,如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.duke.aidl"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="8" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<service android:name=".ServiceRunning">
<intent-filter>
<action android:name="com.duke.aidl.ServiceRunning"/>
</intent-filter>
</service>
</application>
</manifest>
二、客户端程序
当客户端与服务通信时,它们之间必须有一个协议或契约(AIDL)。所以,使用服务的第一步是,获取服务的 AIDL文件并将其复制到客户端项目中。当将AIDL文件复制到客户端项目时,AIDL 编译器将创建一个接口定义文件,这个文件与我们在服务端定义的文件相同。这会向客户端公开所有的方法、参数并返回服务的类型。
首先新建一个客户端工程,当然了包名不能和服务器端相同。
其次根据服务器端的aidl文件所在的包名,新建一个相同的包,让后将服务器端的aidl文件复制到客户端工程新建的包下。
最后是获取服务的引用,以便调用服务器端实现的方法。对于远程服务,必须调用 bindService()方法,而不是 startService()方法。
package com.duke.aidl.client;
import com.duke.aidl.IService;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
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.Toast;
public class AidlServiceClientActivity extends Activity {
/** Called when the activity is first created. */
private IService mService=null;
private Button btn,btn1,btn2;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btn=(Button) findViewById(R.id.button);
btn1=(Button) findViewById(R.id.button1);
btn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
bindService(new Intent("com.duke.aidl.ServiceRunning"), sercon, Context.BIND_AUTO_CREATE);
}
});
btn1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
callService();
}
});
btn2=(Button) findViewById(R.id.button2);
btn2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
unbindService(sercon);
}
});
}
private ServiceConnection sercon=new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
mService=null;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
mService=IService.Stub.asInterface(service);
// callService();
}
};
private void callService(){
try {
// String str=mService.setValue("value");
String val=mService.getValue("hello world");
Toast.makeText(this, "Value from service is " + val,
Toast.LENGTH_LONG).show();
} catch (RemoteException e) {
Log.e("MainActivity", e.getMessage(), e);
}
}
}
对于AIDL 服务,需要提供ServiceConnection接口的实现。此接口定义了两个方法:一个供系统建立服务连接时调用,另一个在销毁服务连接时调用。
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/tv"
android:text="@string/hello" />
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/button"
android:text="bindService" />
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/button1"
android:text="callService" />
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@+id/button2"
android:text="unbindService" />
</LinearLayout>
第一个按钮是绑定启动服务,第二个是调用一下方法来验证是否实现了和服务器端的通信,第三个是解绑服务。
为了避免大家把aidl文件放错位置,来2张我们程序结构的截图吧,一个是服务端得,一个是 客户端的。
服务器端:
客户端:
总结:服务器端的aidl文件声明方法,通过服务实现这些方法
客户端的aidl文件是告诉客户端它所能调用到的服务器端的哪些方法,然后通过绑定启动服务,通过ServiceConnection和服务器建立连接通信,进而调用服务器端实现的方法体。