Android进程通讯——AIDL

版权声明:本文为博主原创文章,可以随意转载,但是必须在开头标明出处。 https://blog.csdn.net/qq_29951983/article/details/78957090

本文参照《Android开发艺术探索》以及官方文档写的读书笔记,欢迎讨论,请勿转载。
在《Android开发艺术探索》这么书中使用跨进程通讯的两个场景:
1、通过多进程来获取多分内存空间
2、当前应用需要向其他应用获取数据

跨进程对我的理解就像请求网络调用接口似的,声明一个接口去在客户端调用,只不过调用的时候当前线程阻塞了,通过序列化的方式将用户自定义的对象(实现了Parcelable接口)或AIDL对象从客户端发送到服务端。然后服务端所对应的方法运行在一个Binder池内,将对象反序列化,从而得到对象,这时候将返回值返回,主线程从阻塞状态中重新恢复到运行的状态。

注意几点:
1、当非跨进程时(在Manifest没有指定process属性),当客户端调用AIDL方法时,客户端在UI线程,那么被调用的服务端的方法也在UI线程。反之。如果。客户端调用AIDL方法时,客户端在非UI线程,那么被调用的服务端方法也在非UI线程。
2、当跨进程时(在Manifest指定process属性),服务端的方法运行在Binder的线程池中,所以Binder方法不管是否耗时都应该采用同步的方式去实现。
3、当客户端发起远程请求时,由于当前线程会被挂起直至服务端进程返回数据,所以如果一个远程方法是很耗时的,那么不能再UI线程中发起此远程请求。
4、如果一个远程方法是耗时的,我在客户端调用时会被挂起(我上面说的第3点)。但是如果我的远程方法返回void,也就是远程方法在怎么耗时,我客户端也不需要你返回数据,那么可以在AIDL方法中增加oneway关键字。
oneway void addBook(in Book book)
这样客户端就不会被挂起了。
5、在跨进程中,客户端传递的对象是经过序列化的,在服务端接收的对象,是经过反序列化的。服务端接收的对象只是里面的东西一样,但是根本不是同一个对象,这点要注意。
6、所有非原语(原语含义看下面)参数都需要指示数据走向的方向标记。可以是 in、out 或 inout。
原语默认为 in,不能是其他方向。
7、AIDL接口中只支持方法,不支持声明静态常量。

刚刚说到类似调用接口,所以得新建一个AIDL接口文件。

package ipc;
import ipc.Book;
import ipc.IOnNewBookArrivedListener;
// Declare any non-default types here with import statements

interface IBookManager {
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

            List<Book> getBookList();
            void addBook(in Book book);
            void registerListener(IOnNewBookArrivedListener listener);
}

在官网给的文档中说明AIDL文件支持的数据有:
Java 编程语言中的所有原语类型(如 int、long、char、boolean 等等)
String
CharSequence
List
Map
在《Android开发艺术探索》中作者加入了其他内容
Parcelable:所有实现了Parcelable接口的对象
AIDL:所有的AIDL接口本身也可以在AIDL文件中使用

其中自定义的Parcelable对象和AIDL对象要显示import进来,不管他们是否和当前的AIDL文件位于同一包内。

最后还要声明一个实现Parcelable对象的 .aidl 文件。 此 .aidl 文件与 C 语言中的头文件类似,并未编译。

// Book.aidl
package ipc;
parcelable Book;
// IOnNewBookArrivedListener.aidl
package ipc;

// Declare any non-default types here with import statements
import ipc.Book;

interface IOnNewBookArrivedListener {
    void onNewBookArrivedListener(in Book newBook);
}
package ipc;

/**
 * Created by apple on 2018/1/1.
 */

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

public class Book implements Parcelable {
    public int bookId;
    public String bookName;

    public Book(String bookName, int bookId) {
        this.bookName = bookName;
        this.bookId = bookId;
    }

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

    protected Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }

    public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    public int getBookId() {
        return bookId;
    }

    public void setBookId(int bookId) {
        this.bookId = bookId;
    }

    public String getBookName() {
        return bookName;
    }

    public void setBookName(String bookName) {
        this.bookName = bookName;
    }

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(bookId);
        dest.writeString(bookName);
    }
}

目录视图:


AIDL包
这里写图片描述
实体类包
这里写图片描述

当定义完AIDL接口时Android Studio会在
app/build/generated/source/aidl/debug/ipc/
该目录下生成一个跟定义接口同名的切后缀名为.java的文件
这里写图片描述
没看到可以Rebuild Project一下。

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /Users/apple/AndroidStudioProjects/MyApplication5/app/src/main/aidl/ipc/IBookManager.aidl
 */
package ipc;
// Declare any non-default types here with import statements

public interface IBookManager extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements ipc.IBookManager {
        private static final java.lang.String DESCRIPTOR = "ipc.IBookManager";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an ipc.IBookManager interface,
         * generating a proxy if needed.
         */
        public static ipc.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof ipc.IBookManager))) {
                return ((ipc.IBookManager) iin);
            }
            return new ipc.IBookManager.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 {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_basicTypes: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    long _arg1;
                    _arg1 = data.readLong();
                    boolean _arg2;
                    _arg2 = (0 != data.readInt());
                    float _arg3;
                    _arg3 = data.readFloat();
                    double _arg4;
                    _arg4 = data.readDouble();
                    java.lang.String _arg5;
                    _arg5 = data.readString();
                    this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getBookList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<ipc.Book> _result = this.getBookList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    ipc.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = ipc.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    return true;
                }
                case TRANSACTION_registerListener: {
                    data.enforceInterface(DESCRIPTOR);
                    ipc.IOnNewBookArrivedListener _arg0;
                    _arg0 = ipc.IOnNewBookArrivedListener.Stub.asInterface(data.readStrongBinder());
                    this.registerListener(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements ipc.IBookManager {
            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;
            }

            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             */
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) 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(anInt);
                    _data.writeLong(aLong);
                    _data.writeInt(((aBoolean) ? (1) : (0)));
                    _data.writeFloat(aFloat);
                    _data.writeDouble(aDouble);
                    _data.writeString(aString);
                    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public java.util.List<ipc.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<ipc.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(ipc.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void addBook(ipc.Book book) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, null, android.os.IBinder.FLAG_ONEWAY);
                } finally {
                    _data.recycle();
                }
            }

            @Override
            public void registerListener(ipc.IOnNewBookArrivedListener listener) 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.writeStrongBinder((((listener != null)) ? (listener.asBinder()) : (null)));
                    mRemote.transact(Stub.TRANSACTION_registerListener, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        static final int TRANSACTION_registerListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;

    public java.util.List<ipc.Book> getBookList() throws android.os.RemoteException;

    public void addBook(ipc.Book book) throws android.os.RemoteException;

    public void registerListener(ipc.IOnNewBookArrivedListener listener) throws android.os.RemoteException;
}

asInterface
将一个IBinder对象转换成一个ipc.IBookManager接口,转换成的接口就是我们之前在ipc包里面定义的IBookManager接口,就相当于向上转型了,如果是跨进程通讯就会生成new ipc.IBookManager.Stub.Proxy(obj);这个代理类。
asBinder
返回当前的Binder对象。
onTransact
当处于跨进程通讯中,该方法运行在服务端中的Binder线程池中,客户端调用声明的方法时,会通过系统封装后交由此方法来处理。
Proxy#getBookList
这个方法运行在客户端

简单说一下asInterface、onTransact、Proxy#getBookList流程调用流程:
这里写图片描述

同理addBook方法中初始了一个_data可以写入一个值,之后将传入的book对象先序列化,然后在onTransact中的addBook方法中反序列化,然后调用this.addBook方法,同样,该方法也是运行在Binder池中,也要进行同步处理。同样序列化和反序列化生成的对象地址不一致。

但是在该addBook方法,没有返回值,所以客户端线程没有必要进行阻塞等待。可以在定义AIDL方法时增加oneway字符,这样客户端就不会阻塞了。

最后《Android开发艺术探索》以及官方文档都给出了调用AIDL的步骤,就感觉跟之前调用Service返回一个IBinder没什么区别,只不过跨进程的内部东西,通过AIDL自动生成的java文件已经给你写好了。
我直接粘贴书中的代码:

public class BookManagerService extends Service {
    private static final String TAG = "BMS";
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();

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

    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book("Android", 1));
        mBookList.add(new Book("Ios", 2));
    }


    IBookManager.Stub iBookManager = new IBookManager.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
        }

        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }

        @Override
        public void registerListener(IOnNewBookArrivedListener listener) throws RemoteException {
            listener.onNewBookArrivedListener(null);
        }
    };
}
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "BookManagerActivity";

    private ServiceConnection mConnection = new ServiceConnection() {

        public void onServiceConnected(ComponentName className, IBinder service) {
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            try {
                List<Book> list = bookManager.getBookList();
                Log.i(TAG, "query book list, list type:"
                        + list.getClass().getCanonicalName());
                Log.i(TAG, "query book list:" + list.toString());
                Book newBook=new Book("Android艺术探索",3);
                System.out.println("newBook = " + newBook);
                bookManager.addBook(newBook);
                Log.i(TAG,"add book:"+newBook);
                List<Book> newList = bookManager.getBookList();
                Log.i(TAG, "query book list:" + newList.toString());
                bookManager.registerListener(iOnNewBookArrivedListener);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        public void onServiceDisconnected(ComponentName className) {
        }
    };

    private IOnNewBookArrivedListener.Stub iOnNewBookArrivedListener=new IOnNewBookArrivedListener.Stub() {
        @Override
        public void onNewBookArrivedListener(Book newBook) throws RemoteException {
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {

        unbindService(mConnection);
        super.onDestroy();
    }
}

这里写图片描述
微信公众号:
这里写图片描述

QQ群:365473065

猜你喜欢

转载自blog.csdn.net/qq_29951983/article/details/78957090