AIDL使用

概述

  • Android开发过程中AIDL使用
  • Android Studio版本:Android Studio Arctic Fox
  • targetSdk 31

AIDL开发步骤

1、创建.aidl文件件

创建.aidl文件
aidl文件内容示例(定义业务接口)

// BookController.aidl
package com.example.binderdemo;

import com.example.binderdemo.Book;

// Declare any non-default types here with import statements

interface BookController {

   List<Book> getBookList();

   //注意当tag定义为out时,服务端会新创建一个Book实例,所以Book类必须得有无参构造函数
   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包名要要相同,否则编译会保存
// Book.aidl
package com.example.binderdemo;

parcelable Book;
  • Book实体类要实现Parcelable
package com.example.binderdemo;

import android.os.Parcel;
import android.os.Parcelable;

import androidx.annotation.NonNull;

/**
 * Created by Android Studio.
 *
 * @author: linruihang
 * @Date: 2021/10/25
 * @Time: 11:23
 */
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文件

build AIDL文件生成java文件

  • 生成的java文件位置如下图:
    AIDL java文件位置
/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package com.example.binderdemo;
// Declare any non-default types here with import statements

public interface BookController extends android.os.IInterface
{
  /** Default implementation for BookController. */
  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;
    }
  }
  /** Local-side IPC implementation stub class. */
  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";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an com.example.binderdemo.BookController interface,
     * generating a proxy if needed.
     */
    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) {
      // Only one user of this interface can use this function
      // at a time. This is a heuristic to detect if two different
      // users in the same process use this function.
      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;

/**
 * Created by Android Studio.
 *
 * @author: linruihang
 * @Date: 2021/10/25
 * @Time: 13:22
 */
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 {
            /**
             *void addBook(out Book book);
             * <p>
             *      参数tag为 out 的情况下,客户端不能向服务端传递数据,参数book相当于 book = new Book();
             *      所以Book必须得有无参构造函数,否则会报错;同时,客户端获取到服务端的数据(如服务端改变书名book.setName("Android开发艺术探索");
             *      服务端能够获取到新的book实例)
             * </p>
             *
             * <p>
             *      参数tag为 in 的情况下,服务端能获取到客户端传递过来的数据,但是客户端通过参数无法感知参数变化,
             *      但是能够通过返回值获取服务端相关的数据
             * </p>
             *
             * <p>
             *     参数为 inout 的情况下,服务端和客户端数据能够双向流动,服务端能够接受客户端传递过来的数据(参数),
             *     客户端也能感知服务端对数据(参数)的更改
             * </p>
             *
             */
            synchronized (this) {
                if (book != null) {
                    mBookList.add(book);
                    //book.setName("Android开发艺术探索");
                    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、编写客户端
  • Activity页面简单布局文件
<?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>
  • Activity作为客户端代码
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;

/**
 * @author linruihang
 */
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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值