AIDL(Android Interface Definition Language,Android接口描述语言)用于定义Client与service进行跨进程通信的编程接口。AIDL简化了IPC通信时数据交换的流程,而程序员只需关心接口的实现即可。
注意:只有你需要其他应用程序可以通过IPC接口调用service或者需要service可以处理多线程的情况时,才去使用AIDL。若非如此,(1)不需要处理跨进程的并发访问,可通过实现 Binder 接口;(2)需要使用IPC,但无需处理多线程的情况,可以选择使用 Messager
使用AIDL进行IPC通信时,需要注意:
- 当前进程调用时,service与调用者在同一线程执行(通常这种情况,一般不使用AIDL进程IPC,而是通过使用 Binder 接口进行调用)
- 如果是其他进程进行IPC调用(系统维护的一个当前进程的Thread Pool),这时可能同时出现多个IPC调用,因此必须确保AIDL接口是支持多线程(thread-safe)
- oneway(异步,不会阻塞当前进程)关键字用于修改远程调用的具体行为。使用 oneway 时,远程调用不会阻塞,只是发送交易数据(transaction data),然后立即返回,而实现该接口的service最终会将其当作一个来自 Binder 线程池(Thread pool)普通的调用(regular call);如果oneway用于本地调用,则不会有任何影响,该调用始终是同步的
接下来,通过一个具体的示例说明如何用AIDL定义一个service,如何绑定该service,如何进行IPC的调用.
定义AIDL接口
.aidl接口的语法跟JAVA语言一样,其定义的方式与JAVA中的interface基本一致,主要区别是AIDL只能定义methods,不能定义数据或者static的methods。每个.aidl文件只能定义一个接口(interface)。AIDL支持以下数据类型:
- 所有JAVA中的基本数据类型(primitive types),例如int,long,char,boolean
- String/CharSequence
- List
- Map
- android.os.parcelable 用于自定义数据类型
以下示例均基于Android Studio
在AS,右击项目,new –> ADIL,对其进行命名,可以看到在 src 目录下,生成了一个AIDL的目录,里面包含了刚才新建的.aidl文件:
package com.jason.test.systemserviceaddingtest;
// Declare any non-default types here with import statements
interface IMathService {
/**
* get current process PID
*/
int getPID();
/**
* add two value
*/
int add(int x, int y);
/**
* mutiple two value
*/
int multiply(int x, int y);
}*
实现AIDL接口
编译时,Android SDK工具会产生一个与 .aidl 同名的 .java 接口文件,该接口文件包含了一个名为 stub abstract class,其声明了 .aidl 文件里所有的methods,同时也定义一个 asInterface 的方法,它用 IBinder(作为参数传递给调用者的 onServiceConnected() 回调函数)作为参数,并返回一个stub接口的实例。我们可以通过扩展 .Stub 类,定义一个service:
package com.jason.test.systemserviceaddingtest.service;
import android.os.*;
import android.os.Process;
import com.jason.test.systemserviceaddingtest.IMathService;
/**
* Created by Jason on 2016/7/11.
*/
public class MathServiceImpl extends IMathService.Stub {
public static final String TAG = MathServiceImpl.class.getSimpleName();
@Override
public int getPID() throws RemoteException {
return Process.myPid();
}
@Override
public int add(int x, int y) throws RemoteException {
return x + y;
}
@Override
public int multiply(int x, int y) throws RemoteException {
return x * y;
}
}
MathServiceImpl为 service 定义了一个RPC(Remote Process Call),接下来只需要将该实例传递给调用者,即可实现与service的远程通信。
使用AIDL的注意事项
- 调用不一定在Main Thread中执行,因此开始就要考虑如何处理多线程的情况
- RPC调用默认支持同步。如果一个service花比较长的时间完成一个request,不要在主线程(UI thread)中调用,以免发生ANR(Application Not Responding),而是应该在另一个线程中进行调用
- 在service中抛出的任何异常都不会返回给调用者client
将接口提供给Clients
将上述实现了的接口提供给clients,以便clients进行绑定:
package com.jason.test.systemserviceaddingtest.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
/**
* Created by Jason on 2016/7/11.
*/
public class MathService extends Service{
public static final String TAG = MathService.class.getSimpleName();
private final MathServiceImpl mServiceBinder =
new MathServiceImpl();
@Override
public void onCreate(){
Log.v(TAG,"onCreate()");
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
Log.v(TAG,"onBind()");
return mServiceBinder;
}
}
当一个client通过调用 bindService() 连接这个service时,client对应的onServiceConnected() 的回调函数会接收到传自该service onBind() 函数的 mBinder 的实例(如果client与service在不同的应用中,则client所属的应用必须要有之前定义的.aidl文件的拷贝)。
调用IPC接口
调用service的IPC接口前,首先需要通过 ServiceConnection 将client与service进行绑定,具体参考下列代码:首先通过mServiceConnection将client(一个新的HandlerThread)与service进行绑定;作为示例,为避免APP出现ANR,创建了一个新的线程去执行service的调用。
package com.jason.test.systemserviceaddingtest;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.support.v4.app.Fragment;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;
import com.jason.test.systemserviceaddingtest.service.MathService;
/**
* A placeholder fragment containing a simple view.
*/
public class MainActivityFragment extends Fragment {
public static final String TAG = MainActivityFragment.class.getSimpleName();
public static final int EVENT_START_SERVICE = 0x01;
public static final int EVENT_STOP_SERVICE = 0x02;
public static final int EVENT_BIND_SUCCESS = 0x03;
private Context mContext;
private IMathService mMathService;
private Handler mHandler;
private Boolean mIsServiceBound = false;
private Button mStartBtn;
private Button mStopBtn;
public MainActivityFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.v(TAG,"onCreateView()");
mContext = getContext();
View fragment = inflater.inflate(R.layout.fragment_main, container, false);
mStartBtn = (Button)fragment.findViewById(R.id.start_service);
mStopBtn = (Button)fragment.findViewById(R.id.stop_service);
mStartBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// start service thread
mServiceThread.start();
Message msg = mHandler.obtainMessage(EVENT_START_SERVICE);
mHandler.sendMessage(msg);
}
});
mStopBtn.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
Message msg = mHandler.obtainMessage(EVENT_STOP_SERVICE);
mHandler.sendMessage(msg);
// stop the service thread
if(!mIsServiceBound) {
mServiceThread.quitSafely();
}
}
});
return fragment;
}
// 连接service后,回调函数会将IBinder传回给client
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mMathService = IMathService.Stub.asInterface(service);
Log.d(TAG,"onServiceConnected(): success");
}
@Override
public void onServiceDisconnected(ComponentName name) {
mMathService = null;
Log.d(TAG,"onServiceDisconnected(): success");
}
};
private void getMathOperations(){
int pid = 0;
int[] val = {203,230};
try{
pid = mMathService.getPID();
val[0] = mMathService.add(val[0],val[1]);
val[1] = mMathService.multiply(val[0],val[1]);
}catch (RemoteException e){
Log.e(TAG,"remote calling exception");
}
Toast.makeText(mContext,"pid = " + pid + " result is " + val[0] + "," + val[1],
Toast.LENGTH_LONG).show();
}
// 为防止APP出现ANR,启动新的线程调用service接口
private HandlerThread mServiceThread = new HandlerThread("ProcessInfoGettingService"){
@Override
public void start(){
//super.start();
mHandler = new Handler(Looper.myLooper()){
@Override
public void handleMessage(Message msg){
switch (msg.what){
case EVENT_START_SERVICE:
bindService();
break;
case EVENT_STOP_SERVICE:
unbindService();
break;
case EVENT_BIND_SUCCESS:
getMathOperations();
default:
break;
}
}
};
}
@Override
public void run(){
Log.d(TAG,"MathService is running");
}
};
// 绑定service
private void bindService(){
Log.v(TAG, "bindService()");
Intent service = new Intent(getContext(), MathService.class);
getContext().bindService(service, mServiceConnection, Context.BIND_AUTO_CREATE);
mIsServiceBound = true;
Toast.makeText(getContext(),"service is bound successfully",Toast.LENGTH_LONG).show();
// notify to calling
Message msg = mHandler.obtainMessage(EVENT_BIND_SUCCESS, Integer.valueOf(mIsServiceBound ? 1 : 0));
mHandler.sendMessageDelayed(msg,1*1000);
}
// 解绑service
private void unbindService(){
if(mIsServiceBound) {
mContext.unbindService(mServiceConnection);
mIsServiceBound = false;
Toast.makeText(getContext(),"service is unbound successfully",Toast.LENGTH_LONG).show();
}
}
}
更多关于利用IPC进行数据传递的介绍请参考[2];
如何添加如 Telephony/Alarm/Window Manager 系统服务,请参考[1]