前言:在还没有做任何一件事情之前,千万不要觉得这件事情很难,因为还没有开始做内心就已经对这件事情产生了恐惧,这将会阻止你的进步,也许当你动手开始做了这件事后发现其实并不是很难。
一、 AIDL概述
含义:AIDL(Android Interface Definition Language),是android接口定义语言,这种语言定义了一个客户端和服务器通讯接口的一个标准、规范。
为什么要有AIDL?
我们都知道android中的四大组件Activity,Broadcast,Content Provider,Service,前面我们应该都接触过除了Service的其他三个组件的进程间通讯的例子,比如:一个应用可以通过显示意图启动另外一个Activity,一个应用发送一个广播,然后被其他应用所接受,一个应用对外提供一个Content Provider,然后其他应用使用ContentResolver获取它提供的数据。这些都是进程间通讯的例子(通常情况下每个应用运行在一个独立的Linux进程中),那么Service这个组件也同样也可以实现垮进程通讯,这就是本篇文章要介绍的AIDL服务。AIDL的出现除了让Service实现垮进程提供服务外,还有一个重要的原因就是:
Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL
这是google官方文档对AIDL的一个提示,意思是说“只有当你允许来自不同应用通过你的service实现进程通讯,并且需要在你的service中处理多线程的情况下才用AIDL,如果你不需要实现不同应用间即时的进程通讯,那么,你应该创建一个接口实现Binder,或者,如果你想实现进程通讯但是不需要处理多线程,那么用一个Messenger实现你的接口,但是,无论如何,你都得先理解本地的服务在你实现AIDL之前“
通过上面的这句话我们就非常清楚了AIDL的作用就是让两个不同的应用间通过Service进行通信(进程通讯IPC),并且远程的Service可以处理多线程。简单来讲就是,两个应用,一个应用对外提供一个远程Service,其他的应用可以并发地访问这个Service,即:C/S模式。
二、 AIDL简单示例
实现步骤:
- 创建一个AIDL文件(扩展名为.aidl);
- 服务端实现该AIDL文件生成的Java接口(系统会自动生成对应的Java接口);
- 暴露一个接口给客户端(通过建立一个Service,在onBind()方法中返回一个Stub类的实例);
- 客户端连接绑定该远程服务。
按照这个步骤,咋们通过代码理解AIDL的使用(这里基于Android Studio这个工具,eclipse里面也类似(更简单))。
1. 创建一个AIDL文件
首先我们需要新建一个Module,这个Module是一个服务端应用,可以直接通过as提供的直接新建AIDL文件,这样它会自动生成aidl文件夹和默认的一个AIDL文件(生成的AIDL默认的包名是应用的包名),这个改包名有点麻烦,因为客户端和服务端都必须要有相同的AIDL文件(包名也必须相同),所以,下面我们通过全手动的方式建立AIDL文件,在main文件夹下(project视图)下面建立一个aidl文件夹,然后在这个文件夹下面建一个包名,包名可以随意,但是客户端和服务器端的AIDL文件包名必须一致,接下来在这个包名下面建一个文件,这里叫IRemoteService.aidl
。
aidl文件有它自己的语法(aidl:接口定义语言):
- 每个aidl文件只能定义一个接口(单一接口);
- 默认Java中的基本数据类型都支持,如:int, long, char, boolean;
- 默认支持String和CharSequence;
- 默认支持List(可以选择加泛型,需要引入List所在的包),但是List中存储的数据也只能是Java基本数据类型,而且另外一边(客户端或服务端)接受的是ArrayList类型;
- 默认支持Map(可以选择加泛型,但泛型只能是基本数据类型或String和CharSequence,需要引入Map所在的包),但同样,Map中存储的数据也只能是Java基本数据类型,而且另外一边(客户端或服务端)接受的是HashMap类型;
- 所有aidl文件中的注释都会出现在自动生成的IBinder接口java文件中(除了导入包语句之前的注释);
- aidl接口只支持方法,不支持变量。
aidl接口文件写法和Java接口的写法非常类似,就是不要Public修饰符,所以,我们这样写:
IRemoteService.aidl
// IRemoteService.aidl
package com.lt.aidl;
// Declare any non-default types here with import statements
interface IRemoteService {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
/** Request the process ID of this service, to do evil things with it. */
int getPid();
/** get name by id */
String getName(int id);
}
Ctrl + S保存后我们可以看到编译器会自动给我们生成(如果没自动生成,可以重新构建一下这个项目)一个对应的IRemoteService.java
文件,这个文件在Packages视图下可以看到:
这个自动生成的java文件我们不需要看懂,所以,不管它,接下来我们进行第二步和第三步,也就是服务端实现该AIDL文件生成的Java接口(系统会自动生成对应的Java接口)。
2(3). 服务端实现该AIDL文件生成的Java接口
打开这个AIDL生成的Java接口,我们可以发现,里面有一个内部静态抽象类Stub,这个类继承了Binder并实现了这个接口,所以,我们可以直接使用这个Stub类完成远程服务的搭建。
新建一个Sevice,在onBind方法中返回实现了AIDL Java接口的那个Binder类(new一个Stub类正好),这个类将作为这个Service代理类:
package com.lt.remoteservice.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import com.lt.aidl.IRemoteService;
/**
* Created by lt on 2016/3/6.
*/
public class RemoteService extends Service{
private String[] names = {
"吕布","关羽"