一、Service 开启和停止
二、Service 执行耗时工作
三、IPC常用方式
四、AIDL(一)同一应用中使用AIDL及原理
五、AIDL(二)不同应用中使用、自定义数据类型及定向Tag
六、AIDL(三)实现回调
七、AIDL(四)获取服务及绑定和Binder传递流程
四、AIDL(一)同一应用中使用AIDL及原理
4.1 AIDL 概述
借助Binder
可以实现跨进程的通信,但是,在使用的过程中,我们可以发现:
- 编写冗余:在
onTransact
方法中,面对不同运算要求,使用switch
和code
进行区分,同样的,在客户端也要对code
进行区分; - 重复序列化数据:在客户端发送数据前,先将数据序列化,在服务端接收数据处理前,先将数据反序列化。这个过程也是个重复的体力活,实际上双方都不关心具体的序列化细节,只知道丢个参数进去,弄个参数出来即可;
- 面向对象:客户端先要调用
transact(xx)
,服务端在onTransact(xx)
里调用对应的操作方法。 这样看起来有点绕,实际上比较好的方式是客户端直接调用服务端的操作方法
上面的缺点,都可以使用AIDL ( Android Interface Definition Language )
克服,但是并不是任何场景都推荐使用AIDL
,只有在需要不同应用的客户端通过IPC
方式访问服务,并且希望在服务中进行多线程处理时,您才有必要使用AIDL
。如果您无需跨不同应用执行并发IPC
,则应通过[实现 Binder
来创建接口;或者,如果您想执行 IPC
,但不需要处理多线程,请使用Messenger
来实现接口。
4.2 AIDL 语法
AIDL
基本上它的语法和 Java
是一样的,只是在一些细微处有些许差别——毕竟它只是被创造出来简化Android
程序员工作的,太复杂不好。跟Java
相比,主要有下面这些点:
-
文件类型:用
AIDL
书写的文件的后缀是.aidl
,而不是.java
。 -
数据类型:
AIDL
默认支持一些数据类型,在使用这些数据类型的时候是不需要导包的,但是除了这些类型之外的数据类型,在使用之前必须导包,就算目标文件与当前正在编写的.aidl
文件在同一个包下——在Java
中,这种情况是不需要导包的。比如,现在我们编写了两个文件,一个叫做Book.java
,另一个叫做BookManager.aidl
,它们都在com.ieening.aidldemo
包下 ,现在我们需要在.aidl
文件里使用Book
对象,那么我们就必须在.aidl
文件里面写上import com.ieening.aidldemo.Book
;哪怕.java
文件和.aidl
文件就在一个包下。默认支持的数据类型包括:Java
中的八种基本数据类型,包括byte
,short
,int
,long
,float
,double
,boolean
,char
。String
类型。CharSequence
类型。List
类型:List
中的所有元素必须是AIDL
支持的类型之一,或者是一个其他AIDL
生成的接口,或者是定义的parcelable
。List
可以使用泛型。Map
类型:Map
中的所有元素必须是AIDL
支持的类型之一,或者是一个其他AIDL
生成的接口,或者是定义的parcelable
。Map
是不支持泛型的。
-
定向
tag
:定向tag
是这样的:AIDL
中的定向tag
表示了在跨进程通信中数据的流向,其中in
表示数据只能由客户端流向服务端,out
表示数据只能由服务端流向客户端,而inout
则表示数据可在服务端与客户端之间双向流通。其中,数据流向是针对在客户端中的那个传入方法的对象而言的。in
为定向tag
的话表现为服务端将会接收到一个那个对象的完整数据,但是客户端的那个对象不会因为服务端对传参的修改而发生变动;out
的话表现为服务端将会接收到那个对象的的空对象,但是在服务端对接收到的空对象有任何修改之后客户端将会同步变动;inout
为定向tag
的情况下,服务端将会接收到客户端传来对象的完整信息,并且客户端将会同步服务端对该对象的任何变动。Java
中的基本类型和String
,CharSequence
的定向tag
默认且只能是in
。 -
两种
AIDL
文件:所有的AIDL
文件大致可以分为两类。一类是用来定义parcelable
对象,以供其他AIDL
文件使用AIDL
中非默认支持的数据类型的。一类是用来定义方法接口,以供系统使用来完成跨进程通信的。可以看到,两类文件都是在“定义”些什么,而不涉及具体的实现,这就是为什么它叫做“接口定义语言”。第一类
AIDL
文件:// Book.aidl // 第一类 AIDL 文件的例子 // 这个文件的作用是引入了一个序列化对象 Book 供其他的 AIDL 文件使用 //注意:Book.aidl 与 Book.java 的包名应当是一样的 package com.ieening.ipcclient; //注意parcelable是小写 parcelable Book;
第二类
AIDL
文件:// BookManager.aidl // 第二类 AIDL 文件的例子 package com.ieening.ipcclient; // 导入所需要使用的非默认支持数据类型的包 import com.ieening.ipcclient.Book; interface BookManager { // 所有的返回值前都不需要加任何东西,不管是什么数据类型 List<Book> getBooks(); Book getBook(); int getBookCount(); // 传参时除了Java 基本类型以及 String,CharSequence 之外的类型,都需要在前面加上定向 tag ,具体加什么量需而定,而不是全部使用 inout ,等参数变多时,维护参数的开销就会很庞大 void setBookPrice(in Book book , int price) void setBookName(in Book book , String name) void addBookIn(in Book book); void addBookOut(out Book book); void addBookInout(inout Book book); }
4.3 同一 应用下使用 AIDL
新建ICalculatorAidlInterface.aidl
文件,定义完文件后,需要重新编译处理,编译后在项目的gen
目录下会生成一个ICalculatorAidlInterface.java
文件,暂时不贴这个文件的代码了,后面会详细说明。
// ICalculatorAidlInterface.aidl
package com.ieening.server;
// Declare any non-default types here with import statements
interface ICalculatorAidlInterface {
float twoNumberAdd(float firstNumber, float secondNumber);
float twoNumberSubtract(float firstNumber, float secondNumber);
float twoNumberMultiply(float firstNumber, float secondNumber);
float twoNumberDivide(float firstNumber, float secondNumber);
}
新建服务端Service
,服务端Service
比较简单,在onBind
函数中返回了一个Binder
。
package com.ieening.server.services;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import androidx.annotation.Nullable;
import com.ieening.server.ICalculatorAidlInterface;
public class CalculatorAidlService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return calculatorAidlBinder;
}
private final ICalculatorAidlInterface.Stub calculatorAidlBinder = new ICalculatorAidlInterface.Stub() {
@Override
public float twoNumberAdd(float firstNumber, float secondNumber) throws RemoteException {
return firstNumber + secondNumber;
}
@Override
public float twoNumberSubtract(float firstNumber, float secondNumber) throws RemoteException {
return firstNumber - secondNumber;
}
@Override
public float twoNumberMultiply(float firstNumber, float secondNumber) throws RemoteException {
return firstNumber * secondNumber;
}
@Override
public float twoNumberDivide(float firstNumber, float secondNumber) throws RemoteException {
return firstNumber / secondNumber;
}
};
}
客户端主要代码如下。客户端主要完成两件事,一:绑定和解绑Service
,二是与Service
通信。绑定和解绑和以前一样,使用ServiceConnection
和bindService
和unBindService
函数;通信使用Binder
,调用相关逻辑获取结果。
public class MainActivity extends AppCompatActivity {
......
@Override
protected void onCreate(Bundle savedInstanceState) {
......
calculatorAidlServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iCalculatorAidlBinder = ICalculatorAidlInterface.Stub.asInterface(service);
Log.d(TAG, "executing calculatorAidlServiceConnection onServiceConnected");
changeCalculatorCalculateButtonStatus();
}
@Override
public void onServiceDisconnected(ComponentName name) {
iCalculatorAidlBinder = null;
Log.d(TAG, "executing calculatorAidlServiceConnection onServiceDisconnected");
changeCalculatorCalculateButtonStatus();
}
};
......
setCalculatorAidlServiceBindUnbindButtonOnClickListener(calculatorAidlServiceConnection);
......
}
......
private void setCalculatorAidlServiceBindUnbindButtonOnClickListener(ServiceConnection calculatorAidlServiceConnection) {
binding.bindCalculatorAidlServiceButton.setOnClickListener(v -> {
Intent bindIntent = new Intent(getApplicationContext(), CalculatorAidlService.class);
bindIntent.setAction("com.ieening.server.services.action.CalculatorAidlService");
Log.d(TAG, "click bind calculator aidl service button to bind service");
boolean bindResult = bindService(bindIntent, calculatorAidlServiceConnection, Context.BIND_AUTO_CREATE);
if (bindResult) {
Toast.makeText(this, "bind service CalculatorAidlService successes", Toast.LENGTH_SHORT).show();
binding.unbindCalculatorAidlServiceButton.setEnabled(true);
binding.bindCalculatorAidlServiceButton.setEnabled(false);
} else {
Toast.makeText(this, "bind service CalculatorAidlService failed", Toast.LENGTH_SHORT).show();
}
changeCalculatorCalculateButtonStatus();
});
binding.unbindCalculatorAidlServiceButton.setOnClickListener(v -> {
unbindService(calculatorAidlServiceConnection);
binding.unbindCalculatorAidlServiceButton.setEnabled(false);
binding.bindCalculatorAidlServiceButton.setEnabled(true);
iCalculatorAidlBinder = null;
changeCalculatorCalculateButtonStatus();
});
}
......
private void setOperationButtonsOnClickListener() {
binding.addButton.setOnClickListener(v -> {
......
if (Objects.isNull(iCalculatorAidlBinder)) {
Toast.makeText(this, "not bind CalculatorBinderService", Toast.LENGTH_SHORT).show();
} else {
try {
float result = iCalculatorAidlBinder.twoNumberAdd(firstNumber, secondNumber);
binding.calculatorResultTextView.setText(Float.toString(result));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
});
binding.subtractButton.setOnClickListener(v -> {
......
if (Objects.isNull(iCalculatorAidlBinder)) {
Toast.makeText(this, "not bind CalculatorBinderService", Toast.LENGTH_SHORT).show();
} else {
try {
float result = iCalculatorAidlBinder.twoNumberSubtract(firstNumber, secondNumber);
binding.calculatorResultTextView.setText(Float.toString(result));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
});
binding.multiplyButton.setOnClickListener(v -> {
......
if (Objects.isNull(iCalculatorAidlBinder)) {
Toast.makeText(this, "not bind CalculatorBinderService", Toast.LENGTH_SHORT).show();
} else {
try {
float result = iCalculatorAidlBinder.twoNumberMultiply(firstNumber, secondNumber);
binding.calculatorResultTextView.setText(Float.toString(result));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
});
binding.divideButton.setOnClickListener(v -> {
......
if (Objects.isNull(iCalculatorAidlBinder)) {
Toast.makeText(this, "not bind CalculatorBinderService", Toast.LENGTH_SHORT).show();
} else {
try {
float result = iCalculatorAidlBinder.twoNumberDivide(firstNumber, secondNumber);
binding.calculatorResultTextView.setText(Float.toString(result));
} catch (RemoteException e) {
throw new RuntimeException(e);
}
}
});
}
......
}
结果展示:
4.4 分析原理
首先来看ICalculatorAidlInterface.aidl
生成的代码。
- 注释1:
ICalculatorAidlInterface
继承了android.os.IInterface
接口,该接口是Binder
接口的基类,里面定义了一个方法public IBinder asBinder()
; - 注释2:定义了
Default
静态类,实现了我们在ICalculatorAidlInterface.aidl
文件中定义的四个方法; - 注释3:接下来是继承了
Binder
并实现了ICalculatorAidlInterface
的抽象类Stub
- 注释4:
asInterface
静态方法是供Client
端使用的,客户端在bindService()
时会获取到一个服务的IBinder
对象,然后通过此方法就可以获取到一个代表服务的引用(有可能是服务的直接引用,也可能是服务的代理对象); - 注释5:
Binder
中onTransact
方法,解析来自客户端的调用请求,然后调用实现了的逻辑方法; - 注释6:
Proxy
实现了ICalculatorAidlInterface
接口,重点是,里面重写了ICalculatorAidlInterface.aidl
中定义的接口方法;
- 注释4:
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.ieening.server;
// Declare any non-default types here with import statements
public interface ICalculatorAidlInterface extends android.os.IInterface // 注释 1
{
/** Default implementation for ICalculatorAidlInterface. */
public static class Default implements com.ieening.server.ICalculatorAidlInterface // 注释 2
{
@Override public float twoNumberAdd(float firstNumber, float secondNumber) throws android.os.RemoteException
{
return 0.0f;
}
@Override public float twoNumberSubtract(float firstNumber, float secondNumber) throws android.os.RemoteException
{
return 0.0f;
}
@Override public float twoNumberMultiply(float firstNumber, float secondNumber) throws android.os.RemoteException
{
return 0.0f;
}
@Override public float twoNumberDivide(float firstNumber, float secondNumber) throws android.os.RemoteException
{
return 0.0f;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.ieening.server.ICalculatorAidlInterface // 注释 3
{
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.ieening.server.ICalculatorAidlInterface interface,
* generating a proxy if needed.
*/
public static com.ieening.server.ICalculatorAidlInterface asInterface(android.os.IBinder obj) // 注释 4
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.ieening.server.ICalculatorAidlInterface))) { // obj 和 ICalculatorAidlInterface同进程
return ((com.ieening.server.ICalculatorAidlInterface)iin);
}
return new com.ieening.server.ICalculatorAidlInterface.Stub.Proxy(obj); // obj 和 ICalculatorAidlInterface 不同进程,IBinder 参数包装成一个 Proxy 对象
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException // 注释 5
{
java.lang.String descriptor = DESCRIPTOR;
if (code >= android.os.IBinder.FIRST_CALL_TRANSACTION && code <= android.os.IBinder.LAST_CALL_TRANSACTION) {
data.enforceInterface(descriptor);
}
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
}
switch (code)
{
case TRANSACTION_twoNumberAdd:
{
float _arg0;
_arg0 = data.readFloat();
float _arg1;
_arg1 = data.readFloat();
float _result = this.twoNumberAdd(_arg0, _arg1);
reply.writeNoException();
reply.writeFloat(_result);
break;
}
case TRANSACTION_twoNumberSubtract:
{
float _arg0;
_arg0 = data.readFloat();
float _arg1;
_arg1 = data.readFloat();
float _result = this.twoNumberSubtract(_arg0, _arg1);
reply.writeNoException();
reply.writeFloat(_result);
break;
}
case TRANSACTION_twoNumberMultiply:
{
float _arg0;
_arg0 = data.readFloat();
float _arg1;
_arg1 = data.readFloat();
float _result = this.twoNumberMultiply(_arg0, _arg1);
reply.writeNoException();
reply.writeFloat(_result);
break;
}
case TRANSACTION_twoNumberDivide:
{
float _arg0;
_arg0 = data.readFloat();
float _arg1;
_arg1 = data.readFloat();
float _result = this.twoNumberDivide(_arg0, _arg1);
reply.writeNoException();
reply.writeFloat(_result);
break;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
return true;
}
private static class Proxy implements com.ieening.server.ICalculatorAidlInterface
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
@Override public float twoNumberAdd(float firstNumber, float secondNumber) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
float _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeFloat(firstNumber);
_data.writeFloat(secondNumber);
boolean _status = mRemote.transact(Stub.TRANSACTION_twoNumberAdd, _data, _reply, 0);
_reply.readException();
_result = _reply.readFloat();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public float twoNumberSubtract(float firstNumber, float secondNumber) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
float _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeFloat(firstNumber);
_data.writeFloat(secondNumber);
boolean _status = mRemote.transact(Stub.TRANSACTION_twoNumberSubtract, _data, _reply, 0);
_reply.readException();
_result = _reply.readFloat();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public float twoNumberMultiply(float firstNumber, float secondNumber) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
float _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeFloat(firstNumber);
_data.writeFloat(secondNumber);
boolean _status = mRemote.transact(Stub.TRANSACTION_twoNumberMultiply, _data, _reply, 0);
_reply.readException();
_result = _reply.readFloat();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public float twoNumberDivide(float firstNumber, float secondNumber) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
float _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeFloat(firstNumber);
_data.writeFloat(secondNumber);
boolean _status = mRemote.transact(Stub.TRANSACTION_twoNumberDivide, _data, _reply, 0);
_reply.readException();
_result = _reply.readFloat();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_twoNumberAdd = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_twoNumberSubtract = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_twoNumberMultiply = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_twoNumberDivide = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
}
// 服务的 Binder 标示符,在 RPC 调用服务方式时就是通过这个标示符来找到我们实现的服务
public static final java.lang.String DESCRIPTOR = "com.ieening.server.ICalculatorAidlInterface";
public float twoNumberAdd(float firstNumber, float secondNumber) throws android.os.RemoteException;
public float twoNumberSubtract(float firstNumber, float secondNumber) throws android.os.RemoteException;
public float twoNumberMultiply(float firstNumber, float secondNumber) throws android.os.RemoteException;
public float twoNumberDivide(float firstNumber, float secondNumber) throws android.os.RemoteException;
}
结合ICalculatorAidlInterface
、服务端CalculatorAidlService.java
和客户端MainActivity.java
,整理出下面的UML
类图:
整个通信的流程如下:
- 首先,
Server
在SM
容器中注册。 - 其次,
Client
若要调用Server
的方法,就需要先获取Server
对象,但是SM
不会把真正的Server
对象返回给Client
,而是把Server
的一个代理对象,也就是Proxy
,返回给Client
。□ - 再次,
Client
调用Proxy
的方法,Binder
和ServiceManager
通过transact
和onTransact
会帮它在Service
中调用对应的方法,并把结果返回给Client
。