接触Android久的朋友,就会在面试或者实际开发中遇到AIDL,那么今天我们就来看看AIDL。AIDL是用于进程间通信的。
一、官方描述
1.1 文档说明
AIDL-Android Interface Definition Language,android接口定义语言,它与你可能已经使用过的其他接口定义语言很类似,它允许你定义接口规则,在客户端和服务器约定为了相互之间能使用IPC(进程间通信)通信。在Android中,正常情况下,一个进程无法访问另外一个进程的内存。所以,它们需要将对象分解成操作系统可以识别的原始的类型,这些有序的对象可以穿过边界为你。代码做编组,是比较繁琐的,但是如果你使用AIDL,那么Android将会自己处理。
PS:1. 使用AIDL的条件是,允许不同的客户端应用访问你的服务,并且在服务端还需要处理多进程。(多客户端、多线程、IPC)
2. 如果你不需要并发处理不同应用的IPC,你应当创建一个接口实现Binder。
3. 如果你只执行IPC,不需要处理对线程,那么使用Messenger实现一个接口。有关Messenger,可以参考 Android IPC之Messenger浅谈。
补:在实现AIDL时,要先绑定一个服务。
1.2 数据类型。
AIDL支持的数据类型有以下几种,
1.支持Java原始数据类型。支持基本数据类型有byte,int,long,float,double,boolean,char,注意没有‘short’。
2.支持String和CharSequence。
3.支持java.util.List和java.util.Map。集合中项的允许数据类型包括Java原始类型、String、CharSequence或是android.os.Parcelable。无需为List和Map提供import语句,但需 要为Parcelable提供import语句。
4.支持传递实现了Android.os.Parcelable接口的复杂类型,同样在引用这些类型时也需要import语句。Parcelable 是Android提供的实现序列化方式。
二、创建AIDL。
为了模拟使用AIDL,需要创建两个应用,一个是服务端,一个客户端。打算实现一个简单的加法计算功能。首先看看服务端的具体实现,
2.1 服务端的具体实现。
1. 打开Android Studio,创建一个Moudle,命名为AidlServer,然后创建一个CalculateAidl的AIDL文件,具体代码如下;
package xinxing.aidltest;
interface CalculateAidl {
int add(int num1,int num2);
}
代码比较简单,就是定义了一个接口,里面有一个方法。
2. 需要创建一个继承Service的服务,这个服务是用来实现AIDL接口以及提供远程调用,具体看实现,
package xinxing.aidltest;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class CalculateService extends Service {
public CalculateService() {
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
private IBinder binder= new CalculateAidl.Stub(){
@Override
public int add(int num1, int num2) throws RemoteException {
int result=num1+num2;
return result;
}
};
}
代码比较少、也简单,了解Service的应该很容易看懂。还是再次解释下,在该Service中通过stub(存根) 返回了IBinder对象,并且在该过程中实现了AIDL接口的方法。
至此,服务端的开发就结束了!就是这么简约!
2.2 客户端实现。
1. 打开Android Studio,创建一个Moudle,命名为AidlClient,接着把服务端的AIDL文件直接复制拷贝到该项目中(客户端和服务端的AIDL文件以及包名称一定要一样)。
2. 实现布局编码。此处就不罗列了!
3. 主要功能。在调用AIDL接口时,我们首先需要绑定一个远程服务,
/**
* 绑定服务
*/
private void bindService() {
//先检查服务端应用是否安装
if (AppUtils.checkPackage(MainActivity.this, "xinxing.aidltest")) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("xinxing.aidltest", "xinxing.aidltest.CalculateService"));
bindService(intent, conn, Context.BIND_AUTO_CREATE);
} else {
isBindSuccess=false;
Toast.makeText(MainActivity.this, "第三方应用可能未安装成功,请检查后,再试!", Toast.LENGTH_LONG).show();
}
}
绑定远程服务,首先判断服务应用是否安装,安装了,才去绑定服务,没有安装,提示‘未安装’。
//远程连接
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
calculateAidl = CalculateAidl.Stub.asInterface(service);
isBindSuccess=true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
calculateAidl = null;
isBindSuccess=false;
}
};
远程连接成功,通过IBinder对象获取到AIDL实例,下面就是如何调用接口了,
if (!isBindSuccess) {
Toast.makeText(MainActivity.this, "第三方应用可能未安装成功,请检查后,再试!", Toast.LENGTH_LONG).show();
return;
}
if (TextUtils.isEmpty(etNum1.getText().toString())) {
Toast.makeText(MainActivity.this, "请输入第一个数字", Toast.LENGTH_LONG).show();
return;
}
int num1 = Integer.parseInt(etNum1.getText().toString());
if (TextUtils.isEmpty(etNum2.getText().toString())) {
Toast.makeText(MainActivity.this, "请输入第二个数字", Toast.LENGTH_LONG).show();
return;
}
int num2 = Integer.parseInt(etNum2.getText().toString());
try {
String result = String.valueOf(calculateAidl.add(num1, num2));
tvResult.setText(result);
} catch (RemoteException e) {
e.printStackTrace();
}
代码比较简单,就不多说了!至此,客户端的代码也ok 了!
这是当服务端应用没有安装时,点击计算按钮的效果截图,
这是当服务端应用已经安装,点击计算按钮的效果截图,
下面展示MainActivity的全部代码,
package xinxing.aidlclient;
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.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import xinxing.aidltest.CalculateAidl;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private EditText etNum1;
private EditText etNum2;
private TextView tvResult;
private Button btn;
private CalculateAidl calculateAidl;//aidl对象
private boolean isBindSuccess=false;//是否绑定成功
//远程连接
private ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
calculateAidl = CalculateAidl.Stub.asInterface(service);
isBindSuccess=true;
}
@Override
public void onServiceDisconnected(ComponentName name) {
calculateAidl = null;
isBindSuccess=false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
etNum1 = (EditText) findViewById(R.id.et_num1);
etNum2 = (EditText) findViewById(R.id.et_num2);
tvResult = (TextView) findViewById(R.id.tv_result);
btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(this);
bindService();
}
@Override
public void onClick(View v) {
if (!isBindSuccess) {
Toast.makeText(MainActivity.this, "第三方应用可能未安装成功,请检查后,再试!", Toast.LENGTH_LONG).show();
return;
}
if (TextUtils.isEmpty(etNum1.getText().toString())) {
Toast.makeText(MainActivity.this, "请输入第一个数字", Toast.LENGTH_LONG).show();
return;
}
int num1 = Integer.parseInt(etNum1.getText().toString());
if (TextUtils.isEmpty(etNum2.getText().toString())) {
Toast.makeText(MainActivity.this, "请输入第二个数字", Toast.LENGTH_LONG).show();
return;
}
int num2 = Integer.parseInt(etNum2.getText().toString());
try {
String result = String.valueOf(calculateAidl.add(num1, num2));
tvResult.setText(result);
} catch (RemoteException e) {
e.printStackTrace();
}
}
/**
* 绑定服务
*/
private void bindService() {
//先检查服务端应用是否安装
if (AppUtils.checkPackage(MainActivity.this, "xinxing.aidltest")) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("xinxing.aidltest", "xinxing.aidltest.CalculateService"));
bindService(intent, conn, Context.BIND_AUTO_CREATE);
} else {
isBindSuccess=false;
Toast.makeText(MainActivity.this, "第三方应用可能未安装成功,请检查后,再试!", Toast.LENGTH_LONG).show();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (isBindSuccess) {
unbindService(conn);
}
}
}
记得在Activity销毁的时候,解除绑定。
三、总结。
使用AIDL,首先需要定义接口以及接口使用到的数据类型,如果是比复杂的数据类型,需要自己通过Parcelable去实现序列化。继承一个Service实现AIDL接口定义的方法。调用该AIDL方法是,最好能加入判断(被调用的应用是否安装的判断)。
1. 使用AIDL的情况是多线程、 多应用 、IPC。
2. 使用AIDL时,客户端和服务端的AIDL文件以及包名都要一样。
推荐一篇IPC文章, Android IPC之Messenger浅谈。