Android多进程机制(二)Binder工作机制

2018-06-13  本文已影响23人  Utte

Binder工作机制

我们可以先从SDK自动为我们生成的AIDL对应的Binder类来分析Binder工作机制。

建立AIDL

AIDL中使用了Binder,所以我们先通过AIDL来分析一下Binder工作过程。先创建一个Book类,实现Parcelable接口。

public class Book implements Parcelable {

    public int bookId;
    public String bookName;

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

    private Book(Parcel in) {
        bookId = in.readInt();
        bookName = 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];
        }
    };

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

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

}

创建两个AIDL文件,Book.aidl用来声明Book类,IBookManager.aidl为定义的接口,写两个方法。

Book.aidl

package com.utte.aidltest;

parcelable Book;

IBookManager.aidl

package com.utte.aidltest;

import com.utte.aidltest.Book;

interface IBookManager {
    List<Book> getBookList();
    void addBook(in Book book);
}

目录像这样

可以发现虽然Book.java在同一个包中,但是在IBookManager.aidl中用Book时还是需要import。

生成Binder类

此时如果build就会在generated目录下找到一个IBookManager.java,这是系统自动为IBookManager.aidl生成的Binder类。

虽然生成了IBookManager.java,但是可能会报错找不到Book类。

在Module:app的Gradle的android{}中加如下代码,就能解决了。

sourceSets {
    main {
        manifest.srcFile 'src/main/AndroidManifest.xml'
        java.srcDirs = ['src/main/java', 'src/main/aidl']
        resources.srcDirs = ['src/main/java', 'src/main/aidl']
        aidl.srcDirs = ['src/main/aidl']
        res.srcDirs = ['src/main/res']
        assets.srcDirs = ['src/main/assets']
    }
}

分析IBookManager.aidl

先看一下整体结构:

package com.utte.aidltest;
public interface IBookManager extends android.os.IInterface {
    private static final java.lang.String DESCRIPTOR = "com.utte.aidltest.IBookManager";
    public static abstract class Stub extends android.os.Binder implements com.utte.aidltest.IBookManager {
        
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        public static com.utte.aidltest.IBookManager asInterface(android.os.IBinder 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 {
            //...
        }

        private static class Proxy implements com.utte.aidltest.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;
            }

            @Override
            public java.util.List<com.utte.aidltest.Book> getBookList() throws android.os.RemoteException {
                //...
            }

            @Override
            public void addBook(com.utte.aidltest.Book book) throws android.os.RemoteException {
                //...
            }
        }

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

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

    public void addBook(com.utte.aidltest.Book book) throws android.os.RemoteException;
}

来看我画了好久的图:

IBinderManager.png

IBookManager:

所有需要在Binder中运输的接口都需要继承IInterface。

Stub:

Stub其实就是Binder类。

这两个id用于标识IBookManager中的两个抽象方法,用于区分transact过程中客户端具体请求的是哪个方法。

Proxy:

Stub的内部代理,主要控制是否需要跨进程transact的逻辑。

下面来看这里两个类中成员的具体含义和细节。

DESCRIPTOR

Binder的唯一标识,一般用Binder的类名表示。

private static final java.lang.String DESCRIPTOR = "com.utte.aidltest.IBookManager";
asInterface()

用于将服务端中的Binder对象转换成客户端所需的AIDL接口类型的对象,这种转换过程是区分进程的,如果客户端服务端位于同一进程,那么此方法返回的是服务端的Stub对象本身,否则返回的是系统封装后的Stub.proxy对象。

public static com.utte.aidltest.IBookManager asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.utte.aidltest.IBookManager))) {
        return ((com.utte.aidltest.IBookManager) iin);
    }
    return new com.utte.aidltest.IBookManager.Stub.Proxy(obj);
}
asBinder()

返回当前Binder对象。

@Override
public android.os.IBinder asBinder() {
    return this;
}
@Override
public android.os.IBinder asBinder() {
    return mRemote;
}
onTransact()

这个方法运行在服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。具体流程:

  1. 服务端通过code来判断客户端请求的目标方法是什么。
  2. data装着目标方法的参数,将参数取出。
  3. 传入参数,调用执行code所代表的方法。
  4. 执行对应方法完毕后,向reply中写入目标方法的返回值。
  5. 如果onTransact()返回false,那么客户端会请求失败。
@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_getBookList: {
            data.enforceInterface(DESCRIPTOR);
            java.util.List<com.utte.aidltest.Book> _result = this.getBookList();
            reply.writeNoException();
            reply.writeTypedList(_result);
            return true;
        }
        case TRANSACTION_addBook: {
            data.enforceInterface(DESCRIPTOR);
            com.utte.aidltest.Book _arg0;
            if ((0 != data.readInt())) {
                _arg0 = com.utte.aidltest.Book.CREATOR.createFromParcel(data);
            } else {
                _arg0 = null;
            }
            this.addBook(_arg0);
            reply.writeNoException();
            return true;
        }
    }
    return super.onTransact(code, data, reply, flags);
}
Proxy#getBookList()

这个方法运行在客户端。客户端远程调用此方法时,具体流程如下:

  1. 创建方法所需要的Parcel型的_data和_reply,再创建真正的返回值对象_result。
  2. 把方法参数写入_data(如果有参数)。
  3. 调用transact()来发起远程过程调用(RPC)请求,当前线程挂起。
  4. 服务端onTransact()被调用。
  5. RPC返回,当前线程继续执行,从_reply中取出返回结果构造_result。
  6. 回收_data和_reply,返回_result。
@Override
public java.util.List<com.utte.aidltest.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.utte.aidltest.Book> _result;
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
        _reply.readException();
        _result = _reply.createTypedArrayList(com.utte.aidltest.Book.CREATOR);
    } finally {
        _reply.recycle();
        _data.recycle();
    }
    return _result;
}
Proxy#addBook()

和Proxy#getBookList()基本一致,只不过没有返回值,不需要从_reply中取出返回值。

@Override
public void addBook(com.utte.aidltest.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);
        if ((book != null)) {
            _data.writeInt(1);
            book.writeToParcel(_data, 0);
        } else {
            _data.writeInt(0);
        }
        mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
        _reply.readException();
    } finally {
        _reply.recycle();
        _data.recycle();
    }
}

注意点与总结

自己实现Binder

不依赖AIDL,自己可以根据上面的分析来实现一个Binder。

上一篇下一篇

猜你喜欢

热点阅读