AIDL笔记

1.AIDL是什么?

AIDL 意思即 Android Interface Definition Language,翻译过来就是Android接口定义语言,是用于定义服务器和客户端通信接口的一种描述语言,可以拿来生成用于IPC的代码。从某种意义上说AIDL其实是一个模板,因为在使用过程中,实际起作用的并不是AIDL文件,而是据此而生成的一个IInterface的实例代码,AIDL其实是为了避免我们重复编写代码而出现的一个模板.

2.简单的Demo

1.定义aidl文件,IPersonAidlInterface.aidl,之后执行Build->Rebuild Project
interface IPersonAidlInterface {
    /**
    *  在 AIDL 中可以通过可带参数以及返回值的一个或多个方法来声明接口,参数和返回值可以是任意类型,AIDL 中支持的数据类型如下:
    *
    *  java 的 8 种数据类型:byte、short、int、long、float、double、boolean、char
    *  除此之外支持 String、charSequence、List、Map
    *  自定义数据类型
    *
    *  如果参数或返回值类型为 List 或 Map 时:
    *   List 中的所有元素都必须是 AIDL 支持的数据类型、其他 AIDL 生成的接口或自己声明的可打包类型。可选择将 List 用作“通用”类(例如,List)。
    *   另一端实际接收的具体类始终是 ArrayList,但生成的方法使用的是 List 接口。
    *
    *   Map 中的所有元素都必须是 AIDL 支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。 不支持通用 Map(如 Map<String,Integer> 形式的 Map)。
    *  另一端实际接收的具体类始终是 HashMap,但生成的方法使用的是 Map 接口。
    */
    //具体的业务
    void setName(String name);
    void setAge(int age);
    String getInfo();
}

会在app\build\generated\aidl_source_output_dir\debug\out路径下生成模板接口如下

/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package com.example.myapplication2;
// Declare any non-default types here with import statements

public interface IPersonAidlInterface extends android.os.IInterface
{
  /** Default implementation for IPersonAidlInterface. */
  public static class Default implements com.example.myapplication2.IPersonAidlInterface
  {
    @Override public void setName(java.lang.String name) throws android.os.RemoteException
    {
    }
    @Override public void setAge(int age) throws android.os.RemoteException
    {
    }
    @Override public java.lang.String getInfo() 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.myapplication2.IPersonAidlInterface
  {
    private static final java.lang.String DESCRIPTOR = "com.example.myapplication2.IPersonAidlInterface";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an com.example.myapplication2.IPersonAidlInterface interface,
     * generating a proxy if needed.
     */
    public static com.example.myapplication2.IPersonAidlInterface asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.example.myapplication2.IPersonAidlInterface))) {
        return ((com.example.myapplication2.IPersonAidlInterface)iin);
      }
      return new com.example.myapplication2.IPersonAidlInterface.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_setName:
        {
          data.enforceInterface(descriptor);
          java.lang.String _arg0;
          _arg0 = data.readString();
          this.setName(_arg0);
          reply.writeNoException();
          return true;
        }
        case TRANSACTION_setAge:
        {
          data.enforceInterface(descriptor);
          int _arg0;
          _arg0 = data.readInt();
          this.setAge(_arg0);
          reply.writeNoException();
          return true;
        }
        case TRANSACTION_getInfo:
        {
          data.enforceInterface(descriptor);
          java.lang.String _result = this.getInfo();
          reply.writeNoException();
          reply.writeString(_result);
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }
    private static class Proxy implements com.example.myapplication2.IPersonAidlInterface
    {
      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 void setName(java.lang.String name) 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.writeString(name);
          boolean _status = mRemote.transact(Stub.TRANSACTION_setName, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().setName(name);
            return;
          }
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      @Override public void setAge(int age) 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(age);
          boolean _status = mRemote.transact(Stub.TRANSACTION_setAge, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            getDefaultImpl().setAge(age);
            return;
          }
          _reply.readException();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
      }
      @Override public java.lang.String getInfo() throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        java.lang.String _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          boolean _status = mRemote.transact(Stub.TRANSACTION_getInfo, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().getInfo();
          }
          _reply.readException();
          _result = _reply.readString();
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
      public static com.example.myapplication2.IPersonAidlInterface sDefaultImpl;
    }
    static final int TRANSACTION_setName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_setAge = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    static final int TRANSACTION_getInfo = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
    public static boolean setDefaultImpl(com.example.myapplication2.IPersonAidlInterface 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.myapplication2.IPersonAidlInterface getDefaultImpl() {
      return Stub.Proxy.sDefaultImpl;
    }
  }
  
  public void setName(java.lang.String name) throws android.os.RemoteException;
  public void setAge(int age) throws android.os.RemoteException;
  public java.lang.String getInfo() throws android.os.RemoteException;
}

2.实现我们自己的远程服务业务逻辑
public class IPersonImpl extends IPersonAidlInterface.Stub {

    private String name;
    private int age;

    @Override
    public void setName(String name) throws RemoteException {
        this.name = name;
    }

    @Override
    public void setAge(int age) throws RemoteException {
        this.age = age;
    }

    @Override
    public String getInfo() throws RemoteException {
        return "My name is " + name + ", age is " + age + "!";
    }
}
3.注册具体的服务,将Binder对象返回可使用远程服务

android:process:是否需要在单独的进程中运行,当设置为android:process=”:remote”时,代表Service在单独的进程中运行。注意“:”很重要,它的意思是指要在当前进程名称前面附加上当前的包名,所以“remote”和”:remote”不是同一个意思,前者的进程名称为:remote,而后者的进程名称为:App-packageName:remote

  • 当使用android:process属性时,查看日志:
    2023-05-28 00:47:50.480 28717-28717 xiao com.example.myapplication I 绑定服务…
    2023-05-28 00:47:50.494 28717-28717 xiao com.example.myapplication I 具体的业务对象:com.example.myapplication2.IPersonAidlInterface S t u b Stub StubProxy@db3f713
    2023-05-28 00:47:52.351 28717-28717 xiao com.example.myapplication I 调用具体服务…
    2023-05-28 00:47:52.356 28717-28717 xiao com.example.myapplication I 调用的服务信息:My name is Tom, age is 10!
    可以看出获得的Binder对象为代理对象
    通过命令adb shell ps -ef "| grep com.example.myapplication"获取当前进程
    u0_a145 28717 344 0 16:27:54 ? 00:00:03 com.example.myapplication
    u0_a145 28766 344 0 16:27:57 ? 00:00:00 com.example.myapplication:remote
    可以看到service存在于不同的进程
  • 当不使用android:process属性时,查看日志:
    2023-05-28 00:53:35.721 30654-30654 xiao com.example.myapplication I 绑定服务…
    2023-05-28 00:53:35.735 30654-30654 xiao com.example.myapplication I 具体的业务对象:com.example.myapplication.aidl.IPersonImpl@3ecaf38
    2023-05-28 00:53:37.211 30654-30654 xiao com.example.myapplication I 调用具体服务…
    2023-05-28 00:53:37.211 30654-30654 xiao com.example.myapplication I 调用的服务信息:My name is Tom, age is 10!
    获得的Binder对象为同一进程对象
    u0_a145 30654 344 3 16:53:26 ? 00:00:01 com.example.myapplication
    只存在一个进程
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyApplication"
        tools:targetApi="31">
        ...

        <service
            android:name=".service.PersonService"
            android:process=":remote"
            android:enabled="true"
            android:exported="true">
        </service>
    </application>
</manifest>
public class PersonService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new IPersonImpl();
    }
}
4.在MainActivity中使用
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String TAG = "xiao";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.bind_service).setOnClickListener(this);
        findViewById(R.id.unbind_service).setOnClickListener(this);
        findViewById(R.id.use_service).setOnClickListener(this);
    }

    private IPersonAidlInterface iPersonAidlInterface;

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 根据实际情况返回 IBinder 的本地对象或其代理对象
            iPersonAidlInterface = IPersonAidlInterface.Stub.asInterface(service);
            Log.i(TAG, "具体的业务对象:" + iPersonAidlInterface);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            // Service 意外中断时调用
        }
    };

    public void bindServiceClick(View view) {
        Log.i(TAG, "绑定服务...");
        Intent intent = new Intent(this, PersonService.class);
        // 绑定服务时自动创建服务
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }

    public void unbindServiceClick(View view) {
        Log.i(TAG, "解绑服务...");
        unbindService(conn);
    }

    public void callRemoteClick(View view) {
        Log.i(TAG, "调用具体服务...");
        try {
            iPersonAidlInterface.setName("Tom");
            iPersonAidlInterface.setAge(10);
            String info = iPersonAidlInterface.getInfo();
            Log.i(TAG, "调用的服务信息:" + info);
        } catch (RemoteException | NullPointerException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onClick(View v) {
        int id = v.getId();
        switch (id) {
            case R.id.bind_service:
                bindServiceClick(v);

                break;
            case R.id.unbind_service:
                unbindServiceClick(v);
                break;
            case R.id.use_service:
                callRemoteClick(v);
                break;
        }
    }
}
5.activity布局文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/bind_service"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="绑定服务"
        app:layout_constraintBottom_toTopOf="@+id/unbind_service"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.756" />

    <Button
        android:id="@+id/unbind_service"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="解绑服务"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.587" />

    <Button
        android:id="@+id/use_service"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="使用服务"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.498"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/unbind_service"
        app:layout_constraintVertical_bias="0.286" />

</androidx.constraintlayout.widget.ConstraintLayout>

3.in out inout区别

  • tag in: 服务端修改不会同步到客户端传递的对象
  • tag out: 客户端传递对象的值无效,但服务端可修改,且客户端会同步
  • tag inout: 服务端可读取客户端传递的完整数据,且修改数据会同步到客户端对象
    在这里插入图片描述
1.Book.java 传输的数据
public class Book implements Parcelable {

    private String name;

    private int price;

    public Book() {}

    public Book(String name, int price) {
        this.name = name;
        this.price = price;
    }

    protected Book(Parcel in) {
        name = in.readString();
        price = in.readInt();
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    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];
        }
    };


    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(@NonNull Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(price);
    }

    public void readFromParcel(Parcel in) {
        this.name = in.readString();
        this.price = in.readInt();
    }

    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }

2.Book.aidl
package com.xiao.aidldemo.aidl;

import com.xiao.aidldemo.aidl.Book;

parcelable Book;
3.IBookManager.aidl
package com.xiao.aidldemo;

import com.xiao.aidldemo.Book;

interface IBookManager {

   List<Book> getBooks();

   Book addBookIn(in Book book);
   Book addBookOut(out Book book);
   Book addBookInout(inout Book book);
}
4.MyService.java 服务端
public class MyService extends Service {

    private static final String TAG = "xiao";
    private List<Book> books = new ArrayList<>();

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new IBookManagerStub();
    }

    public class IBookManagerStub extends IBookManager.Stub {

        @Override
        public List<Book> getBooks() {
            return books;
        }

        @Override
        public Book addBookIn(Book book) {
            // 修改book属性,看客户端是否会有变化
            Log.d(TAG, "addBookIn-->service book = " + book);
            book.setName("大侠一枝花");
            book.setPrice(888);
            return book;
        }

        @Override
        public Book addBookOut(Book book) {
            // 修改book属性,看客户端是否会有变化
            Log.d(TAG, "addBookOut-->service book = " + book);
            book.setName("大侠一枝花");
            book.setPrice(888);
            return book;
        }

        @Override
        public Book addBookInout(Book book) {
            // 修改book属性,看客户端是否会有变化
            Log.d(TAG, "addBookInout-->service book = " + book);
            book.setName("大侠一枝花");
            book.setPrice(888);
            return book;
        }
    }
}
5.MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener {


    private static final String TAG = "xiao";
    private IBookManager bookManager;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            bookManager = IBookManager.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            bookManager = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.getBooks).setOnClickListener(this);
        findViewById(R.id.addBookIn).setOnClickListener(this);
        findViewById(R.id.addBookOut).setOnClickListener(this);
        findViewById(R.id.addBookInout).setOnClickListener(this);

        bindService(new Intent(this, MyService.class), connection, BIND_AUTO_CREATE);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.getBooks:
                try {
                    Log.d(TAG, "getBooks: " + bookManager.getBooks());
                } catch (RemoteException e) {
                }
                break;
            case R.id.addBookIn:
                try {
                    Book book = new Book("addBookIn", 16);
                    bookManager.addBookIn(book);
                    Log.d(TAG, "addBookIn: client = " + book);
                } catch (RemoteException e) {
                }
                break;

            case R.id.addBookOut:
                try {
                    Book book = new Book("addBookOut", 17);
                    bookManager.addBookOut(book);
                    Log.d(TAG, "addBookOut: client = " + book);
                } catch (RemoteException e) {
                }
                break;
            case R.id.addBookInout:
                try {
                    Book book = new Book("addBookInout", 18);
                    bookManager.addBookInout(book);
                    Log.d(TAG, "addBookInout: client = " + book);
                } catch (RemoteException e) {
                }
                break;
        }
    }
}
6.点击不同按钮,输出的log
2023-06-09 23:09:23.306 32501-32520 xiao   D  addBookIn-->service book = Book{name='addBookIn', price=16}
2023-06-09 23:09:23.306 32460-32460 xiao   D  addBookIn: client = Book{name='addBookIn', price=16}

2023-06-09 23:09:36.327 32501-32520 xiao   D  addBookOut-->service book = Book{name='null', price=0}
2023-06-09 23:09:36.327 32460-32460 xiao   D  addBookOut: client = Book{name='大侠一枝花', price=888}

2023-06-09 23:09:47.114 32501-32520 xiao   D  addBookInout-->service book = Book{name='addBookInout', price=18}
2023-06-09 23:09:47.115 32460-32460 xiao   D  addBookInout: client = Book{name='大侠一枝花', price=888}

参考链接:
https://blog.csdn.net/ly0724ok/article/details/117450121
https://blog.csdn.net/luoyanglizi/article/details/51958091

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值