概述
- Android开发过程中AIDL使用
- Android Studio版本:Android Studio Arctic Fox
- targetSdk 31
AIDL开发步骤
1、创建.aidl文件件
aidl文件内容示例(定义业务接口)
package com.example.binderdemo;
import com.example.binderdemo.Book;
interface BookController {
List<Book> getBookList();
void addBook(in Book book);
Book getBook(in int pos);
}
AIDL支持的数据类型
- Java 编程语言中的所有原始类型(如 int、long、char、boolean 等)
- String和CharSequence;
- List:只支持ArrayList,里面每个元素都必须能够被AIDL支持;
- Map:只支持HashMap,里面的每个元素都必须被AIDL支持,包括key和value;
- Parcelable:所有实现了Parcelable接口的对象;
- AIDL:所有的AIDL接口本身也可以在AIDL文件中使用.
in、out、inout简单总结
- in:服务端修改不同步到客户端对象,可以看作不是同一个对象,
- out 客户端传递对象无效,实际传值为新创建对象到服务端,可以看作返回一个这个新创建对象
- inout 服务端修改数据同步到客户端对象,可以看作对同一个对象进行修改。
(如果想要更深入理解可以可以参考你真的理解AIDL中的in,out,inout么?)
Book实体类
- 注意:Book.aidl与Book.java包名要要相同,否则编译会保存
package com.example.binderdemo;
parcelable Book;
package com.example.binderdemo;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
public class Book implements Parcelable {
private String name;
public Book() {
}
public Book(String name) {
this.name = name;
}
protected Book(Parcel in) {
name = in.readString();
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(this.name);
}
public void readFromParcel(Parcel parcel) {
name = parcel.readString();
}
@NonNull
@Override
public String toString() {
return "book name :" + name;
}
}
2、build project生成java文件
- 生成的java文件位置如下图:
package com.example.binderdemo;
public interface BookController extends android.os.IInterface
{
public static class Default implements com.example.binderdemo.BookController
{
@Override public java.util.List<com.example.binderdemo.Book> getBookList() throws android.os.RemoteException
{
return null;
}
@Override public void addBook(com.example.binderdemo.Book book) throws android.os.RemoteException
{
}
@Override public com.example.binderdemo.Book getBook(int pos) throws android.os.RemoteException
{
return null;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
public static abstract class Stub extends android.os.Binder implements com.example.binderdemo.BookController
{
private static final java.lang.String DESCRIPTOR = "com.example.binderdemo.BookController";
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
public static com.example.binderdemo.BookController asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.binderdemo.BookController))) {
return ((com.example.binderdemo.BookController)iin);
}
return new com.example.binderdemo.BookController.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_getBookList:
{
data.enforceInterface(descriptor);
java.util.List<com.example.binderdemo.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(descriptor);
com.example.binderdemo.Book _arg0;
_arg0 = new com.example.binderdemo.Book();
this.addBook(_arg0);
reply.writeNoException();
if ((_arg0!=null)) {
reply.writeInt(1);
_arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_getBook:
{
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
com.example.binderdemo.Book _result = this.getBook(_arg0);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.example.binderdemo.BookController
{
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 java.util.List<com.example.binderdemo.Book> getBookList() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.binderdemo.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getBookList();
}
_reply.readException();
_result = _reply.createTypedArrayList(com.example.binderdemo.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addBook(com.example.binderdemo.Book book) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().addBook(book);
return;
}
_reply.readException();
if ((0!=_reply.readInt())) {
book.readFromParcel(_reply);
}
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public com.example.binderdemo.Book getBook(int pos) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
com.example.binderdemo.Book _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(pos);
boolean _status = mRemote.transact(Stub.TRANSACTION_getBook, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getBook(pos);
}
_reply.readException();
if ((0!=_reply.readInt())) {
_result = com.example.binderdemo.Book.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static com.example.binderdemo.BookController sDefaultImpl;
}
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_getBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
public static boolean setDefaultImpl(com.example.binderdemo.BookController impl) {
if (Stub.Proxy.sDefaultImpl != null) {
throw new IllegalStateException("setDefaultImpl() called twice");
}
if (impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.example.binderdemo.BookController getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
public java.util.List<com.example.binderdemo.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.example.binderdemo.Book book) throws android.os.RemoteException;
public com.example.binderdemo.Book getBook(int pos) throws android.os.RemoteException;
}
3、创建AIDL服务
package com.example.binderdemo.service;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import androidx.annotation.Nullable;
import com.example.binderdemo.Book;
import com.example.binderdemo.BookController;
import java.util.ArrayList;
import java.util.List;
public class AidlService extends Service {
private static final String TAG = "AidlService";
private List<Book> mBookList;
@Override
public void onCreate() {
super.onCreate();
initData();
}
private void initData() {
mBookList = new ArrayList<>();
mBookList.add(new Book("book1"));
mBookList.add(new Book("book2"));
mBookList.add(new Book("book3"));
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new BookBinder();
}
class BookBinder extends BookController.Stub {
@Override
public List<Book> getBookList() throws RemoteException {
Log.d(TAG, "getBookList: " + mBookList.size());
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
synchronized (this) {
if (book != null) {
mBookList.add(book);
Log.d(TAG, "addBook: " + book.toString());
} else {
Log.e(TAG, "addBook: book is null");
Book bookAidl = new Book("aidl");
mBookList.add(bookAidl);
}
}
}
@Override
public Book getBook(int pos) throws RemoteException {
synchronized (this) {
if (pos < 0 || pos >= mBookList.size()) {
return null;
}
return mBookList.get(pos);
}
}
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy================");
}
}
Service组件注册(配置process属性,让service与Activity运行在不同的进程)
<service
android:name=".service.AidlService"
android:enabled="true"
android:exported="true"
android:process=":remote" />
4、编写客户端
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/btn_add"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/add" />
<Button
android:id="@+id/btn_get"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/get" />
<Button
android:id="@+id/btn_get_all"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/get_all_book" />
</LinearLayout>
package com.example.binderdemo;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.media.metrics.LogSessionId;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.example.binderdemo.service.AidlService;
import java.util.List;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "MainActivity";
private Button mBtnGet;
private Button mBtnAdd;
private Button mBtnGetAll;
private BookController mBookController;
private ServiceConnection mServiceConnection;
private Intent mIntent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtnGet = findViewById(R.id.btn_get);
mBtnAdd = findViewById(R.id.btn_add);
mBtnGetAll = findViewById(R.id.btn_get_all);
mBtnAdd.setOnClickListener(this::onClick);
mBtnGet.setOnClickListener(this::onClick);
mBtnGetAll.setOnClickListener(this::onClick);
bind();
}
private void bind() {
mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
Log.d(TAG, "onServiceConnected======");
mBookController = BookController.Stub.asInterface(iBinder);
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.d(TAG, "onServiceDisconnected======");
mBookController = null;
}
};
mIntent = new Intent(MainActivity.this, AidlService.class);
bindService(mIntent, mServiceConnection, BIND_AUTO_CREATE);
}
@Override
public void onClick(View view) {
int id = view.getId();
if (id == R.id.btn_add) {
if (mBookController != null) {
try {
Book testBook = new Book("testBook");
mBookController.addBook(testBook);
Log.d(TAG, "add book result:" + testBook.toString());
} catch (RemoteException e) {
Log.e(TAG, "add book error:" + e.getMessage());
}
}
} else if (id == R.id.btn_get_all) {
if (mBookController != null) {
try {
List<Book> bookList = mBookController.getBookList();
if (bookList != null) {
for (Book book : bookList) {
Log.d(TAG, book.toString());
}
Log.d(TAG, "book nun = " + bookList.size());
}
} catch (RemoteException e) {
Log.e(TAG, "get book list error:" + e.getMessage());
}
}
} else if (id == R.id.btn_get) {
try {
Book book = mBookController.getBook(2);
if (book != null) {
Log.d(TAG, "get book:" + book.toString());
} else {
Log.d(TAG, "get book: null");
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mServiceConnection != null) {
unbindService(mServiceConnection);
}
if (mIntent != null) {
stopService(mIntent);
}
}
}
需要注意的是在客户端调用这些远程方法时是同步调用,在主线程调用可能会导致ANR,应该在子线程去调用.
5、运行验证
- 服务端成功接收到客户端发送的数据(进程间通信成功)
本文旨在记录AIDL学习过程,如若文章中涉及的知识点有误,敬请指正! 完整代码:AIDLDemo
|