Introduction

由于每个应用程序都运行在自己的进程空间,并且可以从应用程序UI运行另一个服务进程,而且经常会在不同的进程间传递对象。在Android平台,一个进程通常不能访问另一个进程的内存空间,所以要想对话,需要将对象分解成操作系统可以理解的基本单元,并且有序的通过进程边界。通过代码来实现这个数据传输过程是冗长乏味的,Android提供了AIDL工具来处理这项工作。

AIDL(AndroidInterfaceDefinitionLanguage)是一种IDL语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocesscommunication,IPC)的代码。如果在一个进程中(例如Activity)要调用另一个进程中(例如Service)对象的操作,就可以使用AIDL生成可序列化的参数。AIDLIPC机制是面向接口的,像COMCorba一样,但是更加轻量级。它是使用代理类在客户端和实现端传递数据。

AIDL适用场景

官方文档特别提醒我们何时使用AIDL是必要的:只有你允许客户端从不同的应用程序为了进程间的通信而去访问你的service,以及想在你的service处理多线程。具体的不同场景下的AIDL设计规则如下:

如果不需要进行不同应用程序间的并发通信(IPC)通过实现一个Binder对象来创建接口;或者你想进行IPC,但不需要处理多线程的,则使用Messenger对象来创建接口。无论如何,在使用AIDL前,必须要理解如何绑定service——bindService

AIDL开发步骤

AIDL接口文件,和普通的接口内容没有什么特别,只是它的扩展名为.aidl,保存在src目录下。如果其他应用程序需要IPC,则那些应用程序的src也要带有这个文件。AndroidSDKtools就会在gen目录自动生成一个IBinder接口文件。service必须适当地实现这个IBinder接口。那么客户端程序就能绑定这个service并在IPC时从IBinder调用方法。每个aidl文件只能定义一个接口,而且只能是接口的声明和方法的声明。

换言之,AIDL文件的作用即是在

1创建.aidl文件

任何需要跨进程调用的方法都需要声明在涉及的每一个服务或者进程中。
AIDL使用简单的语法来声明接口,描述其方法以及方法的参数和返回值。这些参数和返回值可以是任何类型,甚至是其他AIDL生成的接口。其中对于Java编程语言的基本数据类型(int,long,char,boolean),StringCharSequence,集合接口类型ListMap,不需要import语句。而如果需要在AIDL中使用其他AIDL接口类型,需要import,即使是在相同包结构下。AIDL允许传递实现Parcelable接口的类,需要import。需要特别注意的是,对于非基本数据类型,也不是StringCharSequence类型的,需要有方向指示,包括inoutinoutin表示由客户端设置,out表示由服务端设置,inout是两者均可设置。AIDL只支持接口方法,不能公开static变量。

例如

lAIDLService.aidl这个AIDL用于声明需要在客户端中调用的服务中定义的方法。

注意,在某个aidl文件中可以调用其他aidl文件。

package com.kevin.android.demos.binder.aidl;  import com.kevin.android.demos.binder.aidl.AIDLActivity;interface AIDLService {       void registerTestCall(AIDLActivity cb);       void invokCallBack();}  


lAIDLActivity.aidl:这个aidl主要声明在服务中所需要调用的客户端的方法。

packagecom.kevin.android.demos.binder.aidl;

importcom.kevin.android.demos.binder.aidl.Rect1;

interfaceAIDLActivity{

voidperformAction(inRect1rect);

}

lRect1.aidl:这个aidl主要声明服务与客户端之间公用的数据结构。

packagecom.kevin.android.demos.binder.aidl;

parcelableRect1;

2实现服务端接口

创建一个类实现刚才那个aidl的接口:

/*****************Copyright (C), 2010-2015, FORYOU Tech. Co., Ltd.********************/package com.kevin.android.demos.binder;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.RemoteException;import android.util.Log;import com.kevin.android.demos.binder.aidl.AIDLActivity;import com.kevin.android.demos.binder.aidl.AIDLService;import com.kevin.android.demos.binder.aidl.Rect1;public class AIDLTestService extends Service {private AIDLActivity callback;private void Log(String str) {Log.d(Constant.TAG, "------ " + str + "------");}@Overridepublic void onCreate() {Log("service create");}@Overridepublic void onStart(Intent intent, int startId) {Log("service start id=" + startId);}@Overridepublic IBinder onBind(Intent t) {Log("service on bind");return mBinder;}@Overridepublic void onDestroy() {Log("service on destroy");super.onDestroy();}@Overridepublic boolean onUnbind(Intent intent) {Log("service on unbind");return super.onUnbind(intent);}public void onRebind(Intent intent) {Log("service on rebind");super.onRebind(intent);}private final AIDLService.Stub mBinder = new AIDLService.Stub() {@Overridepublic void invokCallBack() throws RemoteException {Log("AIDLService.invokCallBack");Rect1 rect = new Rect1();rect.bottom=-1;rect.left=-1;rect.right=1;rect.top=1;callback.performAction(rect);}@Overridepublic void registerTestCall(AIDLActivity cb) throws RemoteException {Log("AIDLService.registerTestCall");callback = cb;}};}


这里会看到有一个名为AIDLService.Stub类,查看aidl文件生成的Java文件源代码就能发现有这么一段代码:

/**Local-sideIPCimplementationstubclass.*/

publicstaticabstractclassStubextendsandroid.os.Binderimplementscom.kevin.android.demos.binder.aidl.AIDLService

原来Stub类就是继承于Binder类,也就是说RemoteService类和普通的Service类没什么不同,只是所返回的IBinder对象比较特别,是一个实现了AIDL接口的Binder

数据Bean实现

接下来就是关于所传递的数据Bean——Rect1类,是一个序列化的类,这里使用Parcelable接口来序列化,是Android提供的一个比Serializable效率更高的序列化类。

Parcelable需要实现三个函数:
1)voidwriteToParcel(Parceldest,intflags)将需要序列化存储的数据写入外部提供的Parcel对象dest。读取Parcel数据的次序要和这里的write次序一致,否则可能会读错数据。
2)describeContents():简单地返回0即可。
3)staticfinalParcelable.Creator对象CREATOR这个CREATOR命名是固定的,而它对应的接口有两个方法:

lcreateFromParcel(Parcelsource)实现从source创建出JavaBean实例的功能

lnewArray(intsize)创建一个类型为T,长度为size的数组,仅一句话(returnnewT[size])即可。估计本方法是供外部类反序列化本类数组使用。

/*****************Copyright (C), 2010-2015, FORYOU Tech. Co., Ltd.********************/package com.kevin.android.demos.binder.aidl;import android.os.Parcel;import android.os.Parcelable; public class Rect1 implements Parcelable {public int left;public int top;public int right;public int bottom;public static final Parcelable.Creator<Rect1> CREATOR = new Parcelable.Creator<Rect1>() {public Rect1 createFromParcel(Parcel in) {return new Rect1(in);}public Rect1[] newArray(int size) {return new Rect1[size];}};public Rect1() {}private Rect1(Parcel in) {readFromParcel(in);}public void readFromParcel(Parcel in) {left = in.readInt();top = in.readInt();right = in.readInt();bottom = in.readInt();}@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(Parcel out, int flags) {out.writeInt(left);out.writeInt(top);out.writeInt(right);out.writeInt(bottom);}}


对于实现AIDL接口,官方还提醒我们:

1.调用者是不能保证在主线程执行的,所以从一调用的开始就需要考虑多线程处理,以及确保线程安全;

2.IPC调用是同步的。如果你知道一个IPC服务需要超过几毫秒的时间才能完成地话,你应该避免在Activity的主线程中调用。也就是IPC调用会挂起应用程序导致界面失去响应,这种情况应该考虑单独开启一个线程来处理。
3.抛出的异常是不能返回给调用者(跨进程抛异常处理是不可取的)。

3客户端获取接口

客户端如何获取AIDL接口呢?通过AIDLService.Stub.asInterface(service)来得到IMyService对象:

/*****************Copyright (C), 2010-2015, FORYOU Tech. Co., Ltd.********************/package com.kevin.android.demos.binder;import java.text.MessageFormat;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;import com.kevin.android.demos.binder.aidl.AIDLActivity;import com.kevin.android.demos.binder.aidl.AIDLService;import com.kevin.android.demos.binder.aidl.Rect1;public class AIDLTestActivity extends Activity {private Button btnOk;private Button btnCancel;private Button btnCallBack;private void Log(String str) {Log.d(Constant.TAG, "------ " + str + "------");} /*Activity中也要实现一个AIDL接口,本质上也为一个bind类*/private AIDLActivity.Stub mCallback = new AIDLActivity.Stub() {@Overridepublic void performAction(Rect1 rect) throws RemoteException {Log("AIDLActivity.performAction");String str = MessageFormat.format("rect[bottom={0},top={1},left={2},right={3}]", rect.bottom,rect.top, rect.left, rect.right);Toast.makeText(AIDLTestActivity.this,"this toast is called from service\n" + str, 1).show();}};/*以下是获取到远端的Service连接的方法,声明一个匿名类*/AIDLService mService;private ServiceConnection mConnection = new ServiceConnection() {public void onServiceConnected(ComponentName className, IBinder service) {Log("connect service");mService = AIDLService.Stub.asInterface(service);try {mService.registerTestCall(mCallback);} catch (RemoteException e) {}}public void onServiceDisconnected(ComponentName className) {Log("disconnect service");mService = null;}};@Overridepublic void onCreate(Bundle icicle) {Log("AIDLTestActivity.onCreate");super.onCreate(icicle);setContentView(R.layout.aidl_activity);btnOk = (Button) findViewById(R.id.btn_ok);btnCancel = (Button) findViewById(R.id.btn_cancel);btnCallBack = (Button) findViewById(R.id.btn_call_back);btnOk.setOnClickListener(new OnClickListener() {public void onClick(View v) {Log("AIDLTestActivity.btnOk");Bundle args = new Bundle();Intent intent = new Intent();intent.setAction("com.kevin.android.demos.binder.AIDLTestService");intent.putExtras(args);/*Activity中绑定服务的方式还是类似传统中的*/bindService(intent, mConnection, Context.BIND_AUTO_CREATE);startService(intent);}});btnCancel.setOnClickListener(new OnClickListener() {public void onClick(View v) {Log("AIDLTestActivity.btnCancel");unbindService(mConnection);// stopService(intent);}});btnCallBack.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Log("AIDLTestActivity.btnCallBack");try {mService.invokCallBack();} catch (RemoteException e) {// TODO Auto-generated catch blocke.printStackTrace();}}});}}


4效果

Permission权限

如果ServiceAndroidManifest.xml中声明了全局的强制的访问权限,其他引用必须声明权限才能来startstopbind这个service.
另外,service可以通过权限来保护她的IPC方法调用,通过调用checkCallingPermission(String)方法来确保可以执行这个操作。

AndroidManifest.xmlService元素

<serviceandroid:name=".RemoteService"android:process=":remote">
<intent-filter>
<actionandroid:name="com.demo.IMyService"/>
</intent-filter>
</service>

android:process=":remote",代表在应用程序里,当需要该service时,会自动创建新的进程。而如果是android:process="remote",没有“:”分号的,则创建全局进程,不同的应用程序共享该进程。

常见问题:

无法由aidl文件生成java类文件

javase的等级由1.5改为1.6

更多相关文章

  1. 一款常用的 Squid 日志分析工具
  2. GitHub 标星 8K+!一款开源替代 ls 的工具你值得拥有!
  3. RHEL 6 下 DHCP+TFTP+FTP+PXE+Kickstart 实现无人值守安装
  4. Linux 环境下实战 Rsync 备份工具及配置 rsync+inotify 实时同步
  5. 一款霸榜 GitHub 的开源 Linux 资源监视器!
  6. 用android ksoap2 上传、下载数据
  7. Java采用JNI调用VC++生成的dll(Java与C++交互)
  8. 学习文件读写的相关知识
  9. Android下使用JNI笔记

随机推荐

  1. MySQL基础入门学习【7】查询表达式解析 S
  2. sql 设置主键自增从1开始
  3. 用sql语句如何实现小数位数四舍六入,小数
  4. Mysql查询优化——Explain使用
  5. 检索SQL语句的输出参数
  6. 抱SQL SERVER大腿之我爱用视图(对大数据
  7. LINQ : 谈谈LINQ TO SQL中的直接加载和延
  8. 创建视图时ORA-01031
  9. 老鸟帮帮忙:能用字数最少的SQL语句来实现
  10. Mysql Thread stack 说明【hiberntae集成