Android 使用AIDL实现进程间通讯

1 启动远端service进程

本文主要介绍如何使用AIDL来实现进程间的通讯过程。
要实现进程间通讯,首先要创建两个进程的环境。通过在AndroidMenifest中配置如下属性即可使组件运行在指定的进程中

android:process=""

例如要使Service运行在名为com.example.yeliang.testapplication:remote的进程中(com.example.yeliang.testapplication 是程序的包名),可以添加如下配置

<service
	android:name=".h_aidl.RemoteService"
	android:process=":remote"/>

不指定此属性的组件,则默认运行在主进程。
接下来看运行在主进程的Activity和非运行主进程的Service是如何进行通讯的。

1.1 创建ServiceConnection对象

用于作为bindService方法的入参。

ServiceConnection serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder binder) {
             IBookManager bookManager = BookBinder.asInterface(binder);
                try {
                    List<Book> list = bookManager.getBookList();

                    Log.i("=ipc=", "onServiceConnected list = " + list);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
             
            }
        };
1.2 绑定Service
 Intent intent = new Intent(MainProcessActivity.this, RemoteService.class);
 bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);

此时远端进程的Service将会启动。

1.3 在service中创建并返回Binder

远端进程的Service创建后,首先在onCreate中创建用于主进程获取数据的Binder对象,然后在onBind()方法中返回此Binder.

public class RemoteService extends Service {

    private Binder mBinder;
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();

    @Override
    public void onCreate() {
        super.onCreate();
        mBinder = new BookBinder(mBookList);

        mBookList.add(new Book("Java",10020));
        mBookList.add(new Book("Python",10050));
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Book book = intent.getParcelableExtra("book");
        Log.i("=ipc=", "RemoteService onBind 服务端返回的 binder = " + mBinder);
        return mBinder;
    }

}

onBind方法返回的IBinder对象即为Activity中定义的ServiceConnection对象中的onServiceConnected方法里面的IBinder参数

onServiceConnected(ComponentName name, IBinder binder)

但是这两个IBinder并不是一个对象,本身这两个对象也不在同一个进程中。
实际上Service中onBind()方法返回的IBinder对象是BookBinder对象。
而onServiceConnected()方法中接收到的IBinder是BinderProxy对象。后续会详细介绍。

经过上面的步骤,主进程的Activity和远端进程的Service处于不同的进程。Activity中通过持有的Binder对象来调用获取远端进程的数据,进而实现进程间通讯。

2 IBookManager结构

2.1 整体结构

在的onServiceConnected()方法中,首先会通过binder来获取一个IBookManager对象

IBookManager bookManager = BookBinder.asInterface(binder);

接下来看下IBookManager接口的整体结构
在这里插入图片描述

2.2 完整IBookManager类
public interface IBookManager extends IInterface {


    abstract class Stub extends android.os.Binder implements IBookManager {
        private static final String DESCRIPTOR = "com.example.yeliang.testapplication.h_aidl.IBookManager";

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }


        //如果客户端调用asInterface,则实际传过来的binder对象是 BinderProxy 的实例
        //如果服务户端调用asInterface,则实际传过来的binder对象是 IBookManager 的实例
        public static IBookManager asInterface(IBinder binder) {
            Log.i("=ipc=", "Stub asInterface binder = " + binder);
            if (binder == null) {
                return null;
            }

            IInterface iInterface = binder.queryLocalInterface(DESCRIPTOR);
            //1 以 (iInterface instanceof IBookManager)  来区分当前是哪个进程
            if (iInterface instanceof IBookManager) {
                Log.i("=ipc=", "Stub  sInterface iInterface instanceof IBookManager");
                //2 表示同一进程直接返回 iInterface 对象
                return (IBookManager) iInterface;
            }

            Log.i("=ipc=", "Stub asInterface return new IBookManager.Stub.Proxy(obj)");

            //3 如果不是同一个进程 则通过binder来构造Stub.Proxy对象并返回
            return new IBookManager.Stub.Proxy(binder);
        }

        @Override
        public IBinder asBinder() {
            return this;
        }

        @Override
        protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {

            Log.i("=ipc=", "Stub.onTransact code = " + code);
            switch (code) {
                case INTERFACE_TRANSACTION:
                    Log.i("=ipc=", "Stub.onTransact case INTERFACE_TRANSACTION");
                    reply.writeString(DESCRIPTOR);
                    return true;

                case TRANSACTION_getBookList:
                    Log.i("=ipc=", "Stub.onTransact case TRANSACTION_getBookList start");
                    data.enforceInterface(DESCRIPTOR);
                    List<Book> result = getBookList();
                    reply.writeNoException();
                    reply.writeTypedList(result);
                    Log.i("=ipc=", "Stub.onTransact case TRANSACTION_getBookList end");
                    return true;
                case TRANSACTION_addBook:
                    data.enforceInterface(DESCRIPTOR);
                    Book book;
                    if (0 != data.readInt()) {
                        book = Book.CREATOR.createFromParcel(data);
                    } else {
                        book = null;
                    }
                    addBook(book);
                    reply.writeNoException();
                    return true;
            }

            Log.i("=ipc=", "Stub.onTransact execute super");
            return super.onTransact(code, data, reply, flags);
        }

        public static class Proxy implements IBookManager {

            private IBinder mRemote;

            Proxy(IBinder remote) {
                mRemote = remote;
            }

            public String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public List<Book> getBookList() throws RemoteException {
                Log.i("=ipc=", "Stub.Proxy getBookList" + ", binder = " + mRemote);
                Parcel data = Parcel.obtain();
                Parcel reply = Parcel.obtain();
                List<Book> bookList;
                try {
                    data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getBookList, data, reply, 0);
                    Log.i("=ipc=", "Stub.Proxy transact done");
                    reply.readException();
                    bookList = reply.createTypedArrayList(Book.CREATOR);
                } finally {
                    reply.recycle();
                    data.recycle();
                }

                return bookList;
            }

            @Override
            public void addBook(Book book) throws RemoteException {
                Parcel data = Parcel.obtain();
                Parcel reply = Parcel.obtain();

                data.writeInterfaceToken(DESCRIPTOR);
                if (book != null) {
                    data.writeInt(1);
                    book.writeToParcel(data, 0);
                } else {
                    data.writeInt(0);
                }

                try {
                    mRemote.transact(Stub.TRANSACTION_addBook, data, reply, 0);
                    reply.readException();
                } finally {
                    reply.recycle();
                    data.recycle();
                }
            }

            @Override
            public IBinder asBinder() {
                return mRemote;
            }
        }

        static final int TRANSACTION_getBookList = IBinder.FIRST_CALL_TRANSACTION + 0;
        static final int TRANSACTION_addBook = IBinder.FIRST_CALL_TRANSACTION + 1;
    }

    List<Book> getBookList() throws RemoteException;

    void addBook(Book book) throws RemoteException;
}

IBookManager是一个接口类,并且继承自IInterface对象

public interface IBookManager extends IInterface

IInterface是Binder的基础类,使用Binder来作为进程间通讯则必须实现此接口。

2.3 IBookManager中的抽象函数
List<Book> getBookList() throws RemoteException;
void addBook(Book book) throws RemoteException;

当客户端持有IBookManager对象的实例时,即可通过此接口函数来获取远端进程的数据。

2.4 IBookManager中的内部抽象类Stub
2.4.1 Stub类结构

Stub首先继承自Binder,并且实现了IBookManager接口。

abstract class Stub extends android.os.Binder implements IBookManager
2.4.2 Stub的标识

再来看Stub中,首先是用来标识Stub的DESCRIPTOR。

private static final String DESCRIPTOR = "com.example.yeliang.testapplication.h_aidl.IBookManager";
2.4.3 asInterface()方法

此方法会根据binder来创创建并返回一个IBookManager实例。

public static IBookManager asInterface(IBinder binder) {
            Log.i("=ipc=", "Stub asInterface binder = " + binder);
            if (binder == null) {
                return null;
            }

            IInterface iInterface = binder.queryLocalInterface(DESCRIPTOR);
            //1 以 (iInterface instanceof IBookManager)  来区分当前是哪个进程
            if (iInterface instanceof IBookManager) {
                Log.i("=ipc=", "Stub  sInterface iInterface instanceof IBookManager");
                //2 表示同一进程直接返回 iInterface 对象
                return (IBookManager) iInterface;
            }

            Log.i("=ipc=", "Stub asInterface return new IBookManager.Stub.Proxy(obj)");

            //3 如果不是同一个进程 则通过binder来构造Stub.Proxy对象并返回
            return new IBookManager.Stub.Proxy(binder);
        }

入参binder:
如果客户端(统一进程)调用asInterface,则实际传过来的binder对象是 BinderProxy 的实例。
如果服务户端调用asInterface,则实际传过来的binder对象是 IBookManager 的实例即BookBinder对象。

返回值IBookManger
如果调用asInterface()方法入参为BinderProxy的实例,即需要跨进程传输数据,则返回IBookManger.Stub.Proxy实例。
如果是同一进程调用asInterface()方法,则返回的是BookBinder实例。

2.4.5 onTransact()方法

此方法运行在服务端的进程中,用于把服务端的数据写入到Parcel对象中,然后返回给客户端。

@Override
        protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {

            Log.i("=ipc=", "Stub.onTransact code = " + code);
            switch (code) {
                case INTERFACE_TRANSACTION:
                    Log.i("=ipc=", "Stub.onTransact case INTERFACE_TRANSACTION");
                    reply.writeString(DESCRIPTOR);
                    return true;

                case TRANSACTION_getBookList:
                    Log.i("=ipc=", "Stub.onTransact case TRANSACTION_getBookList start");
                    data.enforceInterface(DESCRIPTOR);
                    List<Book> result = getBookList();
                    reply.writeNoException();
                    reply.writeTypedList(result);
                    Log.i("=ipc=", "Stub.onTransact case TRANSACTION_getBookList end");
                    return true;
            }

            Log.i("=ipc=", "Stub.onTransact execute super");
            return super.onTransact(code, data, reply, flags);
        }

当主进程通过IBookManager的实例即IBookManager.Stub.Proxy对象调用getBookList()方法时,Proxy将会通过transact()函数来发起远端进程的转换。

mRemote.transact(Stub.TRANSACTION_getBookList, data, reply, 0);

紧接着将会调用Stub中的onTransact()函数,onTransact()函数将会调用BookBinder获取响应数据,并将此数据写入到Pacel对象中。此时客户端即可获取到服务端的数据。

2.5 Stub中的静态内部类Proxy

IBookBinder.Stub.Proxy是客户端的一个代理对象,通过此对象可以获取到服务端即对端进程的数据。Proxy实现了IBookManager的接口,但是这些接口并不真正做远端数据的写入操作,而是起着转发的作用,即将所要获取的数据转发给服务端的Stub#onTransact()方法执行。

3 总结

3.1 类关系图

在这里插入图片描述

3.2 当Activity与Service不在同一进程时的日志信息
2020-07-11 15:20:14.274 15752-15752/? I/=ipc=: RemoteService onBind 服务端返回的 binder = com.example.yeliang.testapplication.h_aidl.BookBinder@c1d8a61
2020-07-11 15:20:14.275 15752-15780/? I/=ipc=: Stub.onTransact code = 1079135572
2020-07-11 15:20:14.275 15752-15780/? I/=ipc=: Stub.onTransact execute super
2020-07-11 15:20:14.275 15267-15267/com.example.yeliang.testapplication I/=ipc=: Activity onServiceConnected 客户端接收到的 binder = android.os.BinderProxy@eb7fe88
2020-07-11 15:20:14.275 15267-15267/com.example.yeliang.testapplication I/=ipc=: Stub asInterface binder = android.os.BinderProxy@eb7fe88
2020-07-11 15:20:14.276 15267-15267/com.example.yeliang.testapplication I/=ipc=: Stub asInterface return new IBookManager.Stub.Proxy(obj)
2020-07-11 15:20:14.276 15267-15267/com.example.yeliang.testapplication I/=ipc=: Stub.Proxy getBookList, binder = android.os.BinderProxy@eb7fe88
2020-07-11 15:20:14.276 15752-15781/? I/=ipc=: Stub.onTransact code = 1
2020-07-11 15:20:14.276 15752-15781/? I/=ipc=: Stub.onTransact case TRANSACTION_getBookList start
2020-07-11 15:20:14.276 15752-15781/? I/=ipc=: BookBinder getBookList, binder = com.example.yeliang.testapplication.h_aidl.BookBinder@c1d8a61
2020-07-11 15:20:14.276 15752-15781/? I/=ipc=: Stub.onTransact case TRANSACTION_getBookList end
2020-07-11 15:20:14.276 15267-15267/com.example.yeliang.testapplication I/=ipc=: Stub.Proxy transact done
2020-07-11 15:20:14.276 15267-15267/com.example.yeliang.testapplication I/=ipc=: onServiceConnected list = [Book{bookId=10020, bookName='Java'}, Book{bookId=10050, bookName='Python'}]
3.3 当Activity与Service在同一进程时的日志信息
2020-07-11 15:22:37.936 16156-16156/com.example.yeliang.testapplication I/=ipc=: RemoteService onBind 服务端返回的 binder = com.example.yeliang.testapplication.h_aidl.BookBinder@9150021
2020-07-11 15:22:37.937 16156-16156/com.example.yeliang.testapplication I/=ipc=: Activity onServiceConnected 客户端接收到的 binder = com.example.yeliang.testapplication.h_aidl.BookBinder@9150021
2020-07-11 15:22:37.937 16156-16156/com.example.yeliang.testapplication I/=ipc=: Stub asInterface binder = com.example.yeliang.testapplication.h_aidl.BookBinder@9150021
2020-07-11 15:22:37.937 16156-16156/com.example.yeliang.testapplication I/=ipc=: Stub  sInterface iInterface instanceof IBookManager
2020-07-11 15:22:37.938 16156-16156/com.example.yeliang.testapplication I/=ipc=: BookBinder getBookList, binder = com.example.yeliang.testapplication.h_aidl.BookBinder@9150021
2020-07-11 15:22:37.938 16156-16156/com.example.yeliang.testapplication I/=ipc=: onServiceConnected list = [Book{bookId=10020, bookName='Java'}, Book{bookId=10050, bookName='Python'}]
3.4 流程总结

1 首先定义IBookManager接口,此接口中定义了需要获取远端数据的方法。并且IBookManager实现了IInterface接口,表明IBookManager支持跨进程传输数据。

2 主进程需要获取远端进程的数据时,首先通过onServiceConnected(ComponentName name, IBinder binder)方法返回的IBinder对象来构建BookManager的实例

IBookManager bookManager = BookBinder.asInterface(binder);

因为IBookManager本身已经定义了远端进程获取数据的接口,而此处又获取到了IBookManger的实例。所以即可通过此实例来获取到远端的数据。

3 当Service和Activity运行在不同的进程时,Activity接收到的是binder为BinderProxy的实例。
当Service和Activity运行在同一的进程时,Activity接收到的binder即为BookBinder的实例。

4 BookBinder继承自BookManager.Stub类,所以BookBinder.asInterface()会调用到IBookManage.Stub的asInterface()方法。在此方法中会根据binder对象的不同返回不同的IBookManager实例。

5 当binder对象为BinderProxy实例时,会根据此binder创建一个BookManger.Stub.Proxy对象。

6 当主进程通过BookManger.Stub.Proxy获取远端方法数据时,首先会调用transact()方法

mRemote.transact(Stub.TRANSACTION_getBookList, data, reply, 0);

7 然后会调用BookManager.Stub类中的onTransat()方法,并将获取到数据写入到reply中

data.enforceInterface(DESCRIPTOR);
List<Book> result = getBookList();
reply.writeNoException();
reply.writeTypedList(result);

8 此时IBookManager.Stub.Proxy中已经能够获取到返回的reply值

 bookList = reply.createTypedArrayList(Book.CREATOR);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值