当前位置: 代码迷 >> 综合 >> Android进阶学习(7)-- AIDL
  详细解决方案

Android进阶学习(7)-- AIDL

热度:96   发布时间:2023-12-16 17:17:50.0

Android AIDL

    • AIDL是什么
    • AIDL使用
    • AIDL流程分析

AIDL是什么

AIDL(Interface definition language)是Android中IPC进程间通信的一种实现方式,通俗的来说,一个APP可以通过另一个APP提供的Service进行交互。

AIDL使用

创建两个APP项目,一个作为客户端client,一个作为服务端,实现以下功能:
1.客户端向服务端提交数据
2.服务端接收数据并返回累计提交的数据
3.客户端 服务端 接收到数据后更新UI显示数据
项目结构如下:
在这里插入图片描述
先来编写客户端Client代码,client_demo项目结构:
在这里插入图片描述
首先声明一个Person类,作为提交的数据,必须实现Parcelable接口:

public class Person implements Parcelable {String name;String sex;@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", sex='" + sex + '\'' +'}';}public Person(String name, String sex){this.name = name;this.sex = sex;}protected Person(Parcel in) {name = in.readString();sex = in.readString();}public static final Creator<Person> CREATOR = new Creator<Person>() {@Overridepublic Person createFromParcel(Parcel in) {return new Person(in);}@Overridepublic Person[] newArray(int size) {return new Person[size];}};public String getName() {return name;}public void setName(String name) {this.name = name;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeString(name);dest.writeString(sex);}
}

在main下创建aidl包,里面存放aidl文件,注意:客户端和服务端项目中AIDL文件包名必须相同,实体类的目录结构要和Java包中的结构相同:
在这里插入图片描述
创建Person.aidl文件:

package com.example.service_demo;parcelable Person;

创建IPersonAidl.aidl文件:

package com.example.service_demo;// Declare any non-default types here with import statements
import com.example.service_demo.Person;interface IPersonAidl {//声明交互的操作 这里一个是添加 一个是获取listvoid addPerson(in Person person);List<Person> getPersonList();
}

创建完成后编译一下会在Build下生成对应的aidl文件:
在这里插入图片描述
在MainActivity中编写功能:

public class MainActivity extends AppCompatActivity {IPersonAidl iPersonAidl;Button bnInvoke;TextView tvData;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();//AIDL 创建连接  绑定服务bindService();}void initView(){tvData = findViewById(R.id.tvData);bnInvoke = findViewById(R.id.bnInvoke);bnInvoke.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {try {iPersonAidl.addPerson(new Person("张三","男"));List<Person> personList = iPersonAidl.getPersonList();tvData.setText(personList.toString());Log.e("personList --->", personList.toString());} catch (RemoteException e) {e.printStackTrace();}}});}//AIDL 创建连接  绑定服务void bindService(){ServiceConnection connection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {Log.e("client_demo", "onServiceConnected: success");iPersonAidl = IPersonAidl.Stub.asInterface(service);}@Overridepublic void onServiceDisconnected(ComponentName name) {Log.e("client_demo", "onServiceDisconnected: success");iPersonAidl = null;}};Intent intent = new Intent();intent.setComponent(new ComponentName("com.example.service_demo", "com.example.service_demo.PersonAidlService"));bindService(intent, connection, Context.BIND_AUTO_CREATE);}
}

客户端的代码就编写完成了,接着编写服务端代码;
同样需要创建Person类,和客户端的一模一样;同样也需要创建相同的aidl目录,和aidl文件,和客户端是一模一样的就不贴代码了,一定要注意包名:
在这里插入图片描述
编译之后,创建PersonAidlService服务,主要实现aidl文件中定义的方法,供给AIDL调用:

public class PersonAidlService extends Service {List<Person> personList = new ArrayList();@Overridepublic void onCreate() {super.onCreate();Log.e("PersonAidlService -->", "onCreate:success");}@Nullable@Overridepublic IBinder onBind(final Intent intent) {IBinder iBinder = new IPersonAidl.Stub() {@Overridepublic void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {}@Overridepublic void addPerson(Person person) throws RemoteException {Log.e("service_demo", "addPerson:success");personList.add(person);//通过广播 通知 MainActivity 更新UiIntent i = new Intent();i.setAction("UpdateUi");i.putExtra("ReceiveData", personList.toString());sendBroadcast(i);}@Overridepublic List<Person> getPersonList() throws RemoteException {Log.e("service_demo", "getPersonList:success");return personList;}};return iBinder;}
}

MainActivity:

public class MainActivity extends AppCompatActivity {TextView tvData;UpdateBroadcastReceiver broadcastReceiver = new UpdateBroadcastReceiver();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);tvData = findViewById(R.id.tvData);//注册 更新ui 广播IntentFilter filter = new IntentFilter();filter.addAction("UpdateUi");registerReceiver(broadcastReceiver, filter);//注册 aidl 服务startService(new Intent(MainActivity.this, PersonAidlService.class));}public class UpdateBroadcastReceiver extends BroadcastReceiver{@Overridepublic void onReceive(Context context, Intent intent) {tvData.setText(intent.getExtras().getString("ReceiveData"));}}
}

我们看一下运行效果,分别启动服务端 客户端:
在这里插入图片描述

AIDL流程分析

通过生成的代码来看一下,AIDL是如何实现进程间通信的。我们通过客户端代码入手:
在这里插入图片描述
我们创建连接时,通过IPersonAidl.Stub.asInterface(service);得到了IPersonAidl的实例对象,所以才能调用里面的addPerson方法等,点进去asInterface,我们先看一下它所在类的结构:
在这里插入图片描述
它所在的类就是编译器帮我们生成的文件里面有两个内部类,一个Stub和一个Proxy,在进程通信中,通俗来说一定会有两个操作,一个是发送数据,一个是接受数据,Stub对应的就是接收数据,Proxy对应的就是发送数据。
我们接着来看asInterface方法源码:
在这里插入图片描述
asInterface所在的类是Stub,方法中首先进行了为空判断,接着调用了queryLocalInterface,我们先看一下它传入的参数DESCRIPTOR
在这里插入图片描述
就是一个全类名,我们看这个Stub的构造方法:
在这里插入图片描述
在这里插入图片描述
构造方法分别在本地保存了它本身和DESCRIPTOR
我们回过头来看queryLocalInterface:
在这里插入图片描述
IBinder的实现类就是Binder:
在这里插入图片描述
这个方法,也就是比较一下两个描述符,就是判断一下两个aidl通信是不是在一个进程中;
我们接着看asInterface的源码:
在这里插入图片描述
它最终返回的是一个Proxy对象;
那也就是说,MainActivity中通过asInterface这个方法已经得到了IPersonAidl的实例对象可以调用它的方法(也就是我们定义的addPerson 和 getPersonList)
在这里插入图片描述
接着我们来看调用方法的源码:
在这里插入图片描述
在这里插入图片描述
点进来发现并没有实现,其实看类的目录结构,我们就可以找到他的实现类:
在这里插入图片描述
在这里插入图片描述
Proxy实现了IPersonAidl,实现了里面的方法,我们点进去Proxy中的addPerson方法:
在这里插入图片描述
addPerson方法中,写入的操作完成后,会调用transact方法,transact方法也在IBinder中,所以直接去实现类Binder中找transact方法:
在这里插入图片描述
看上面的注解我们大概能知道,到这里就调用到了服务端的onTransact方法,执行IPC通信,并且客户端当前线程挂起,等待服务端返回;

因为客户端和服务端的aidl文件是一样的,生成的文件也是一样的,我们直接在客户端看onTransact方法:
在这里插入图片描述
这里首先switch case去匹配code,code是从客户端的transact中传来的,code的含义是客户端告诉服务端该调用哪个方法:
在这里插入图片描述
在这里插入图片描述
这里并不是传入的方法名,因为客户端服务端aidl文件一样,方法一样,方法顺序一样,所以这里传入的是int,也就是方法的顺序;
我们接着看onTransact方法:在这里插入图片描述
返回true后,客户端线程继续执行。这就完成了一次进程通信

  相关解决方案