Android中AIDL的使用

编程入门 行业动态 更新时间:2024-10-10 10:29:12

<a href=https://www.elefans.com/category/jswz/34/1771384.html style=Android中AIDL的使用"/>

Android中AIDL的使用


 

AIDL,即Android Interface Definition Language,Android接口定义语言。这门语言是为了实现进程间通信。每一个进程都有自己的一块独立的内存,都在自己的内存上存储自己的数据,执行自己的操作,每个进程之间你不知我,我不知你,而AIDL,就是两个进程之间沟通的桥梁。

aidl文件支持的数据类型包括:

  • 八种基本数据类型:byte、char、short、int、long、float、double、boolean
  • String、CharSequence
  • 实现了Parcelable接口的数据类型
  • List类型。List承载的数据必须是AIDL支持的类型,或者是其他声明的AIDL对象
  • Map类型。Map承载的数据必须是AIDL支持的类型,或者是其他声明的AIDL对象

在使用其他声明的AIDL对象的时候必须要导包,即使要使用的AIDL对象文件和当前正在编辑的aidl文件在同一个文件夹下。

aidl文件可以分为两类,一类用来声明实现了Parcelable接口的数据类型,以供其他AIDL文件使用那些非默认支持的数据类型。还有一类是用来定义接口方法,声明要暴露哪些接口给客户端调用。

AIDL的具体使用步骤是:

一、创建一个服务端工程

1、如果aidl文件中涉及到实现了Parcelable接口的数据类型,则先将该aidl文件定义出来。在src文件夹下右键,选择新建aidl文件,这里新建了一个Book.aidl文件。

新建完以后,会在main文件下生成一个aidl的文件夹,aidl文件夹下的目录结构和java文件夹下的目录结构一样。

Book.aidl文件中会有一个默认方法。

我们删除掉这个默认方法,只声明一个parcelable数据类型,这个文件就成为声明Parcelable数据类型的AIDL文件。注意这里的parcelable中的p是小写的。

2、这个时候我们来定义Book类并实现Parcelable接口,注意Book类在java文件夹下的目录与Book.aidl文件在aidl文件夹下的目录保持一致。

public class Book implements Parcelable {private String name;public Book(String name) {this.name=name;}protected Book(Parcel in) {this.name = in.readString();}public static final Creator<Book> CREATOR = new Creator<Book>() {@Overridepublic Book createFromParcel(Parcel in) {return new Book(in);}@Overridepublic Book[] newArray(int size) {return new Book[size];}};public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeString(name);}/*** 这个方法是自己写上去的,并不是根据错误提示自动生成的代码* 默认生成的模板类的对象只支持为in的定向tag。因为默认生成的类里面只有 writeToParcel() 方法。* 而如果要支持为out或者inout的定向tag的话,还需要实现readFromParcel()方法。* 而这个方法其实并没有在 Parcelable 接口里面,所以需要我们从头写。*/public void readFromParcel(Parcel dest){name=dest.readString();}
}

这里需要注意的一点是如果要支持为out或inout的定向tag的话,需要我们手写readFromParcel()方法。

3、然后我们开始写BookController.aidl文件来定义能被客户端调用的接口。

package com.example.service;import com.example.service.Book;// Declare any non-default types here with import statementsinterface BookController {int getInt();//int类型String getString();//String类型List<Book> getBookList();//aidl对象void addBook(inout Book book);//aidl对象}

这里需要注意的是,虽然Book.aidl文件和BookController.aidl文件在同一个包下,还是需要手动导入一下。

4、至此,我们的aidl文件写完了,然后我们Rebuild project,系统会帮我们生成一个与AIDL文件同名的java文件,这个java文件才是与我们跨进程通信密切相关的东西。

5、定义一个Service,通过这个Service将接口暴露给外部。

public class AIDLService extends Service {private List<Book> bookList;public AIDLService() {}@Overridepublic void onCreate() {super.onCreate();bookList = new ArrayList<>();initData();}private void initData() {Book book1 = new Book("花千骨");Book book2 = new Book("公主小妹");Book book3 = new Book("仙剑奇侠传");Book book4 = new Book("飘");Book book5 = new Book("茶花女");Book book6 = new Book("解忧杂货铺");Book book7 = new Book("活着");Book book8 = new Book("三生三世十里桃花");bookList.add(book1);bookList.add(book2);bookList.add(book3);bookList.add(book4);bookList.add(book5);bookList.add(book6);bookList.add(book7);bookList.add(book8);}private final BookController.Stub stub = new BookController.Stub() {@Overridepublic int getInt() throws RemoteException {return bookList == null ? 0 : bookList.size();}@Overridepublic String getString() throws RemoteException {return bookList == null ? "" : bookList.get(0).getName();}@Overridepublic List<Book> getBookList() throws RemoteException {return bookList;}@Overridepublic void addBook(Book book) throws RemoteException {if (book != null) {bookList.add(book);} else {Log.i("ruxing", "接收到了一个空对象 Inout");}}};@Nullable@Overridepublic IBinder onBind(Intent intent) {return stub;}
}

6、在清单文件中注册服务。

        <service android:name=".AIDLService"android:enabled="true"android:exported="true"><intent-filter><action android:name="com.example.service.action"/><category android:name="android.intent.category.DEFAULT"/></intent-filter></service>

至此,服务端开发完成了。

二、创建一个custom工程

1、新建一个custom工程,把服务端的aidl文件夹下的内容全部拷贝到新项目中,如果aidl中有用到bean实体类,把实体类也拷贝过来,然后Rebuild Project。

2、在客户端的activity中连接远程服务,实现aidl通信。

public class MainActivity extends AppCompatActivity {private BookController bookController;private boolean connected;private List<Book> bookList;private Button mBtnGetBookList;private Button mBtnAddBook;private Button mBtnGetBookSize;private Button mBtnGetFirstBookName;private ServiceConnection serviceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {bookController = BookController.Stub.asInterface(service);connected = true;}@Overridepublic void onServiceDisconnected(ComponentName name) {bookController = null;connected = false;}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mBtnGetBookList = findViewById(R.id.btn_get_book_list);mBtnAddBook = findViewById(R.id.btn_add);mBtnGetBookSize = findViewById(R.id.btn_get_book_size);mBtnGetFirstBookName = findViewById(R.id.btn_first_book_name);mBtnGetBookList.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (connected) {try {bookList = bookController.getBookList();for (int i = 0; i < bookList.size(); i++) {Log.i("ruxing", "name=" + bookList.get(i).getName());}} catch (RemoteException e) {e.printStackTrace();}}}});mBtnAddBook.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {if (connected) {Book book = new Book("新书");try {bookController.addBook(book);Log.i("ruxing", "向服务器添加了一本新书==="+book.getName());} catch (RemoteException e) {e.printStackTrace();}}}});mBtnGetBookSize.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {if (connected) {try {int size = 0;size = bookController.getInt();Log.i("ruxing", "共有" + size + "本书");} catch (RemoteException e) {e.printStackTrace();}}}});mBtnGetFirstBookName.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {if (connected) {try {String name = bookController.getString();Log.i("ruxing", "第一本书的书名是:" + name);} catch (RemoteException e) {e.printStackTrace();}}}});Intent intent = new Intent();intent.setPackage("com.example.service");intent.setAction("com.example.service.action");bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);}@Overrideprotected void onDestroy() {super.onDestroy();if (connected) {unbindService(serviceConnection);}}
}

三、代码分析

在创建完aidl文件以后Rebuild Project以后,会在build文件夹下自动生成BookController.aidl对应的BookController.java文件。

BookController.java文件中部分代码如下:

可以看到,BookController继承自IInterface接口,他自己本身也是一个接口,里面有在BookController.aidl中定义的方法。 

查看BookController.java的结构,可以看到,里面定义了一个Stub类,在Stub类中又定义了一个Proxy类。

1、首先,我们知道,在服务端我们需要创建一个实现IBinder接口的实例对象并提供公共方法给客户端调用这里我们需要把BookController.aidl文件中的几个方法暴露出来提供给客户端,所以我们在服务端的Service中定义了一个Stub对象。

通过看BookController.java中对Stub的定义,我们能看到,Stub实现了IBinder接口,并实现了BookController.aidl中定义的几个方法,满足我们的要求。

2、然后,是在客户端onServiceConnected()回调方法中接收Binder。

通过看BookController.java中对asInterface()方法的定义,我们看到,asInterface()方法返回的可能是一个BookController对象,也有可能是一个Proxy对象。

接下来我们看一下queryLocalInterface()方法,queryLocalInterface方法的入参是包名。

在Binder.java中是通过包名来决定返回值的。然后我们看一下Binder.java中mDescriptor和mOwner赋值的操作。

我们看到在attachInterface方法中对这两个变量做了赋值,Stub的构造方法中也调用了attachInterface方法来做这个初始化的工作。

我们继续看asInterface()方法,前边说到,asInterface()方法返回的可能是一个BookController对象,也有可能是一个Proxy对象,这是因为我们的发送方和接收方可能是同一个进程。如果在同一个进程,在这个进程中,服务端新建Stub对象对mOwner和mDescriptor赋值,这个时候再调用asInterface方法的时候再调用queryLocalInterface方法就直接返回mOwner对象了,mOwner对象就是BookController本身,因为同一个进程中,就不需要再借助Binder传递了。如果不在同一个进程,服务端新建Stub对象对mOwner和mDescriptor赋值,客户端调用asInterface方法的时候再调用queryLocalInterface方法返回的是null,这个时候就会新建一个Proxy对象返回了。

3、我们再看一下asInterface返回Proxy对象不在同一进程的情况,Proxy类实现了BookController接口,这样我们在客户端就可以访问服务端暴露的方法了。

综合上边的我们知道,Stub是在服务端创建的,Proxy是在客户端创建的。

4、接下来我们看一下调用Proxy中的方法是怎么最后走到服务端的。我们以addBook()方法为例,首先定义了两个包,_data存储的是客户端发送的数据,_reply存储的是服务端返回的数据。

4.1 因为客户端可能会向很多服务发送数据,这里首先判断一下调用的是否是本服务。

4.2 然后后边我们看到用到addBook方法。getDefaultImpl()方法返回BookController对象,调用BookController对象的addBook()方法,BookController的addBook()方法在服务端的Service中实现,至此,从客户端走到了服务端。

整体的过程是:

 

完整的代码地址:

.git

更多推荐

Android中AIDL的使用

本文发布于:2024-02-05 11:47:05,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1745365.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:Android   AIDL

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!