为了让远程Service与多个应用程序的组件(四大组件)进行跨进程通信(IPC),需要使用AIDL
IPC:Inter-Process Communication,即跨进程通信
AIDL:Android Interface Definition Language,即Android接口定义语言;用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。
在多进程通信中,存在两个进程角色(以最简单的为例):服务器端和客户端
以下是两个进程角色的具体使用步骤:
服务器端(Service)
步骤1:新建定义AIDL文件,并声明该服务需要向客户端提供的接口
步骤2:在Service子类中实现AIDL中定义的接口方法,并定义生命周期的方法(onCreat、onBind()、blabla)
步骤3:在AndroidMainfest.xml中注册服务 & 声明为远程服务
客户端(Client)
步骤1:拷贝服务端的AIDL文件到目录下
步骤2:使用Stub.asInterface接口获取服务器的Binder,根据需要调用服务提供的接口方法
步骤3:通过Intent指定服务端的服务名称和所在包,绑定远程Service
接下来,我将用一个具体实例来介绍远程Service的使用
一、服务端:
1、新建aidl文件
// IOrderManagerInterface.aidl
package com.htkj.service.aidl;
import com.htkj.service.bean.OrderBean;
// Declare any non-default types here with import statements
interface IOrderManagerInterface {
/**
* 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);
List<OrderBean> getAll();
void add(in OrderBean bean);
}
添加aidl文件后,build工程,在build文件中自动生成代码:
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.htkj.service.aidl;
// Declare any non-default types here with import statements
public interface IOrderManagerInterface extends android.os.IInterface
{
/** Default implementation for IOrderManagerInterface. */
public static class Default implements com.htkj.service.aidl.IOrderManagerInterface
{
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
{
}
@Override public java.util.List<com.htkj.service.bean.OrderBean> getAll() throws android.os.RemoteException
{
return null;
}
@Override public void add(com.htkj.service.bean.OrderBean bean) throws android.os.RemoteException
{
}
@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.htkj.service.aidl.IOrderManagerInterface
{
private static final java.lang.String DESCRIPTOR = "com.htkj.service.aidl.IOrderManagerInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.htkj.service.aidl.IOrderManagerInterface interface,
* generating a proxy if needed.
*/
public static com.htkj.service.aidl.IOrderManagerInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.htkj.service.aidl.IOrderManagerInterface))) {
return ((com.htkj.service.aidl.IOrderManagerInterface)iin);
}
return new com.htkj.service.aidl.IOrderManagerInterface.Stub.Proxy(obj);
}
@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
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_basicTypes:
{
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0!=data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
return true;
}
case TRANSACTION_getAll:
{
data.enforceInterface(descriptor);
java.util.List<com.htkj.service.bean.OrderBean> _result = this.getAll();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_add:
{
data.enforceInterface(descriptor);
com.htkj.service.bean.OrderBean _arg0;
if ((0!=data.readInt())) {
_arg0 = com.htkj.service.bean.OrderBean.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.add(_arg0);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.htkj.service.aidl.IOrderManagerInterface
{
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;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean)?(1):(0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
boolean _status = mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().basicTypes(anInt, aLong, aBoolean, aFloat, aDouble, aString);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public java.util.List<com.htkj.service.bean.OrderBean> getAll() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.htkj.service.bean.OrderBean> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getAll, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getAll();
}
_reply.readException();
_result = _reply.createTypedArrayList(com.htkj.service.bean.OrderBean.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void add(com.htkj.service.bean.OrderBean bean) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((bean!=null)) {
_data.writeInt(1);
bean.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
boolean _status = mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().add(bean);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
public static com.htkj.service.aidl.IOrderManagerInterface sDefaultImpl;
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getAll = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
public static boolean setDefaultImpl(com.htkj.service.aidl.IOrderManagerInterface impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.htkj.service.aidl.IOrderManagerInterface getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public java.util.List<com.htkj.service.bean.OrderBean> getAll() throws android.os.RemoteException;
public void add(com.htkj.service.bean.OrderBean bean) throws android.os.RemoteException;
}
上述代码是系统自动生成的,在build目录下,可以到根据IOrderManagerInterface.aidl系统为我们生成了IOrderManagerInterface.java这个类,它继承了IInterface这个接口,同时它自己也还是个接口,所有可以在Binder中传输的接口都需要继承IInterface接口。这个类刚开始看起来逻辑混乱,但是实际上还是很清晰的,通过它我们可以清楚地了解到Binder的工作机制。这个类的结构其实很简单,首先,它声明了两个方法getAll()和add(),显然这就是我们在IOrderManagerInterface.aidl中所生命的方法,同时它还声明 了两个整型的id分别用于标识这两个方法,这个两个id用于标识在transact过程中客户端所请求的到底是哪个方法。接着,它声明了一个内部类Stub,这个Stub就是一个Binder类,当客户端和服务端都位于同一个进程时,方法调用不会走跨进程的transact过程,而当两者位于不同进程时,方法调用需要走transact过程,这个逻辑由Stub的内部代理类Proxy来完成。这么来看,IOrderManagerInterface这个接口的确很简单,但是我们也应该认识到,这个接口的核心实现就是它内部类Stub和Stub的内部代理类Proxy,下面详细介绍针对这两个类的每个方法的含义。
DESCRIPTOR
private static final java.lang.String DESCRIPTOR = "com.htkj.service.aidl.IOrderManagerInterface";
Binder的唯一标识,一般用当前Binder的类名表示,比如本例中“com.htkj.service.aidl.IOrderManagerInterface”。
asInterface(android.os.IBinder obj)
用于讲服务端的BInder对象转成客户端所需要的AIDL接口类型对象,这种转换过程时区分进程的,如果客户端和服务端位于同一个进程,那么此方法返回的就是服务端的Stub对象本身,否则返回的是系统封装后的Stub.proxy对象。
asBinder
此方法用于返回当前Binder对象
onTransact
这个方法运行在服务端中Binder线程池中,当客户端发起跨进程请求时,云端请求会通过系统底层封装后交由此方法来处理。该方法的原型为public Boolean onTransact(int code,android.os.Parcel data,android.os.Parcel reply,int flags)。服务端通过code可以确定客户端所请求的目标方法时什么,接着从data中取出目标方法所需的参数(如果目标方法有参数的话),然后执行目标方法。当目标方法执行完毕后,就向reply中写入返回值(如果目标方法有返回值的话),onTransact方法的执行过程就是这样的。需要注意的是,如果此方法返回false,那么客户端的请求会失败,因此我们可以利用这个特性来做权限验证,毕竟我们也不希望随便一个进程都能远程调用我们的服务。
Proxy#getAll()
这个方法运行在客户端,当客户端远程调用此方法时,它的内部实现是这样的:首先创建该方法所需要的输入型Parcel对象_data、输出型Parcel对象_reply和返回值对象List;然后把该方法的参数信息写入_data中(如果有参数的话);接着调用transact方法来发起RPC(远程过程调用)请求,同时当前线程挂起;然后服务端的onTransact方法会被调用,直到RPC过程返回,当前线程继续执行,并从_reply中取出RPC过程的返回结果;最后返回_reply中数据。
Proxy#add()
这个方法运行在客户端,它的执行过程和getall()是一样的,add()没有返回值,所以它不需要从_reply中取出返回值。
通过上面的分析,了解了Binder的工作机制,但是有两点还是需要额外说明一下:首先,当客户端发起远程请求时,由于当前线程会被挂起直至服务端进程返回数据,所以如果一个远测方法时很耗时的,那么不能在UI线程中发起此远程请求;其次,由于服务端的Binder方法运行在Binder的线程池中,所以Binder方法不管是否耗时都应该采用同步的方式去实现,因为它已经运行在一个线程中了。BInder的工作机制图如下:
接下来,介绍Binder的两个很重要的方法,linkToDeath和unlinkToDeath。我们知道,Binder运行在服务端进程,如果服务端进程由于某种原因异常终止,这个时候我们到服务端的Binder连接断裂(称之为Binder死亡),会导致我们的远程调用失败。更关键的是,如果我们不知道Binder连接已经断裂,那么客户端的功能就会收到影响。为了解决这个问题,Binder中提供了两个配对的方法linkToDeath 和 unlinkToDeath ,通过linkToDeath我们可以给Binder设置一个死亡代理,当Binder死亡时,我们就会收到通知,这个时候我们就可以重新发起连接清酒从而恢复连接。
首先,声明一个DeathRecipient对象。DeathRecipient是一个接口,其内部只有一个方法bindDeath,我们需要实现这个方法,当Binder死亡的时候,系统就会回调bindDeath方法,然后我们就可以移除之前绑定的binder代理并重新绑定远程服务:
//代码后面补充
其次,在呼呼段绑定远程服务成功后,给binder设置死亡代码:
//代码后面补充。
其中linkToDeath的第二个参数是个标记位,我们直接设为0即可。经过删改你两个步骤,就给我们的Binder设置了死亡代理,当Binder死亡的时候我们就可以收到通知了。另外,通过Binder的方法isBinderAlive也可以判断Binder是否死亡。
2、添加bean数据
package com.htkj.service.bean;
import android.os.Parcel;
import android.os.Parcelable;
public class OrderBean implements Parcelable {
private String id;
private String name;
private int amount;
public OrderBean(){}
public OrderBean(String id, String name, int amount) {
this.id = id;
this.name = name;
this.amount = amount;
}
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public int getAmount() {
return this.amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
protected OrderBean(Parcel in) {
id = in.readString();
name = in.readString();
amount = in.readInt();
}
public static final Creator<OrderBean> CREATOR = new Creator<OrderBean>() {
@Override
public OrderBean createFromParcel(Parcel in) {
return new OrderBean(in);
}
@Override
public OrderBean[] newArray(int size) {
return new OrderBean[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeString(name);
dest.writeInt(amount);
}
}
3、因为bean数据实现Parcelable 接口,需要在bean相关的包名下面添加同名aidl文件。(例如本例中bean名称为OrderBean.java,该类实现Parcelable 接口,该类所在的包名为com.htkj.service.bean,即需要在com.htkj.service.bean包名下添加OrderBean.aidl文件)
// OrderBean.aidl
package com.htkj.service.bean;
parcelable OrderBean;
3、在AndroidManifest.xml中添加以下代码
<service
android:name=".service.OrderService"
android:enabled="true">
<intent-filter>
<action android:name="com.htkj.lenrning.service.OrderService"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</service>
以上服务端代码结束。
二、客户端
1、将服务端IOrderManagerInterface.aidl、OrderBean.java、OrderBean.aid三个文件复制到客户端模块,注意包名需要与服务端一样。
2、客户端绑定服务:
package com.htkj.jni.aidlclient;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import com.htkj.adapter.OrderAdapter;
import com.htkj.service.aidl.IOrderManagerInterface;
import com.htkj.service.bean.OrderBean;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button;
import android.widget.ListView;
import java.util.List;
import java.util.Random;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private List<OrderBean> orderList;
private IOrderManagerInterface orderManagerInterface;
//UI
private Button search_button;
private Button add_button;
private ListView listView;
private OrderAdapter adapter;
ServiceConnection conn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//客户端与服务连接成功后,自动进入到该方法
Log.i("MainActivity.conn","@@ onServiceConnected name="+name);
IOrderManagerInterface manager = IOrderManagerInterface.Stub.asInterface(service);
orderManagerInterface = manager;
refreshOrderList();
try {
service.linkToDeath(deathRecipient, 0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
//当服务异常断开后,自动进入该方法
Log.i("MainActivity.conn","@@ onServiceDisconnected name="+name);
}
};
IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
if (orderManagerInterface == null) {
Log.i("MainActivity","@@ binderDied "+(orderManagerInterface==null));
return;
}
orderManagerInterface.asBinder().unlinkToDeath(deathRecipient,0);
orderManagerInterface = null;
bindOrderService();
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("MainActivity","@@ onCreate");
setContentView(R.layout.activity_main);
getLayoutInflater();
//view
search_button=findViewById(R.id.search_button);
add_button=findViewById(R.id.add_button);
listView=findViewById(R.id.listView);
//组装recyclerView
LinearLayoutManager linearLayoutManager=new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
adapter=new OrderAdapter(this);
listView.setAdapter(adapter);
//onclick
search_button.setOnClickListener(this);
add_button.setOnClickListener(this);
//bind
bindOrderService();
}
//绑定服务
private void bindOrderService(){
//服务端服务类完整路径
Intent intent = new Intent("com.htkj.lenrning.service.OrderService");
//服务端应用包名
intent.setPackage("com.htkj.lenrning");
intent.addCategory(Intent.CATEGORY_LAUNCHER);
bindService(intent, conn, Context.BIND_AUTO_CREATE);
}
/**
* 重新获取数据并刷新列表。
*/
private void refreshOrderList(){
try {
if(orderManagerInterface!=null){
orderList = orderManagerInterface.getAll();
int size = orderList.size();
for(int i = 0; i < size; i ++ ) {
Log.d("lili","i = " + i + "id = " + orderList.get(i).getId() + "name =" + orderList.get(i).getName());
}
}
if(orderList!=null){
adapter.setData(orderList);
adapter.notifyDataSetChanged();
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onClick(View v) {
if(v==search_button){
//刷新订单
refreshOrderList();
}else if(v==add_button){
//增加订单
try {
OrderBean orderBean=new OrderBean();
Random random=new Random();
orderBean.setAmount(random.nextInt(800)+100);
orderBean.setId(random.nextInt(100000000)+100000+"");
orderBean.setName("玩具"+random.nextInt());
orderManagerInterface.add(orderBean);
refreshOrderList();
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//解绑服务
unbindService(conn);
}
}
//adapter
package com.htkj.adapter;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import com.htkj.jni.aidlclient.R;
import com.htkj.service.bean.OrderBean;
import java.util.List;
public class OrderAdapter extends BaseAdapter {
private List<OrderBean> orderList;
private LayoutInflater inflater;
public OrderAdapter(Context context) {
inflater = LayoutInflater.from(context);
}
public void setData (List<OrderBean> list) {
this.orderList = list;
if (this.orderList == null) {
Log.d("lili","null ....");
} else {
Log.d("lili","size = ...." + this.orderList.size());
}
}
@Override
public int getCount() {
if (orderList == null) {
return 0;
}else {
return orderList.size();
}
}
@Override
public Object getItem(int i) {
return orderList.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int i, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if(convertView == null){
convertView = inflater.inflate(R.layout.item_adapter,parent,false);
holder = new ViewHolder();
holder.tvId = (TextView) convertView.findViewById(R.id.tv_id);
holder.tvName = (TextView) convertView.findViewById(R.id.tv_name);
holder.tvAmount = (TextView) convertView.findViewById(R.id.tv_amount);
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
holder.tvId.setText(orderList.get(i).getId());
holder.tvName.setText(orderList.get(i).getName());
holder.tvAmount.setText(String.valueOf(orderList.get(i).getAmount()));
return convertView;
}
class ViewHolder{
private TextView tvId;
private TextView tvName;
private TextView tvAmount;
}
}
//main_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="5dp"
android:text="订单管理"
android:textSize="20dp"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp">
<Button
android:id="@+id/search_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15dp"
android:text="刷新订单"/>
<Button
android:id="@+id/add_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="15dp"
android:text="添加订单"/>
</LinearLayout>
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
//item_adapter
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_id"
android:layout_width="180dp"
android:layout_height="40dp"
android:text="id"
android:textSize="20px"/>
<TextView
android:id="@+id/tv_name"
android:layout_width="180dp"
android:layout_height="40dp"
android:text="id"
android:textSize="20px"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"/>
<TextView
android:id="@+id/tv_amount"
android:layout_width="180dp"
android:layout_height="40dp"
android:text="id"
android:textSize="20px"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"/>
</LinearLayout>
3、运行程序,先运行服务端应用,再运行客户端应用。效果图如下: