首页 > 代码库 > Android-Binder 简析

Android-Binder 简析

前言

对于Android来说,Binder的重要性怎么说都不为过。不管是我们的四大组件Activity、Service、BroadcastReceiver、ContentProvider,还是经常在应用中使用到的各种ServiceManager,其背后都是Binder在支撑。然而Binder机制又不是三言两语能够描述得清楚的,因此本文通过对一个简单的AIDL Demo进行分析,让读者对Binder有个初步的认识,要想深入了解Binder背后的原理,可以参考最后的延伸阅读。

Demo

首先我们通过AIDL新建一个跨进程通信的Demo,然后在代码中简单分析Binder的运行过程。

Server Module

我们先新建一个提供接口的AIDL服务端module,服务端主要提供AddBook和getBookList两个功能,其目录如下:

技术分享

p2.png

  • IBookManager.AIDL

// IAIDLServer.aidl
package com.nancyyihao.aidlserver;
import  com.nancyyihao.aidlserver.Book;

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

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

package com.nancyyihao.aidlserver;

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

/**
 * Created by jumper on 2016/9/7.
 */
public class Book implements Parcelable {
    private String bookName;
    private int bookId;

    public Book(){}

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

    public Book(Parcel parcel){
        bookName = parcel.readString();
        bookId = parcel.readInt();
    }

    public String getBookName() {
        return bookName;
    }

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

    public int getBookId() {
        return bookId;
    }

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

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

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

    public static final Parcelable.Creator<Book> CREATOR = new Creator<Book>(){

        @Override
        public Book createFromParcel(Parcel source) {
            return new Book(source);
        }

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

    };
}
  • Book.AIDL

package com.nancyyihao.aidlserver;
parcelable Book;
  • BookManagerService

package com.nancyyihao.aidlserver;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * Created by jumper on 2016/9/7.
 */
public class BookManagerService extends Service {

    private static final String TAG = "BMS";
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        // to distinguish with client module, we set the book id different from client module
        mBookList.add(new Book(3,"Android"));
        mBookList.add(new Book(4,"iOS"));
    }

    private Binder mBinder = new IBookManager.Stub() {

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

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

Client Module

把Server module的代码拷贝一份(AIDL包名不能变),然后新建一个MainActivity即可

  • MainActivity

package com.nancyyihao.aidlserver;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import com.nancyyihao.R;

import java.util.List;

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            Log.e("trace", "onServiceConnected");
            try {
                List<Book> bookList = bookManager.getBookList();
                Log.e(TAG, "query book list, list type:" + bookList.getClass().getCanonicalName());
                Log.e(TAG, "query book list:" + bookList.toString());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final Intent intent = new Intent();
        intent.setAction("com.nancyyihao.startservice");
        intent.setPackage("com.nancyyihao.aidlserver"); // server‘s package name
        Log.e("trace", "bindService");
        bindService(intent, mConnection, BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mConnection);
    }
}

分析

把代码写好后,build一下,就能看到自动生成了一个IBookManager.Java文件

  • IBookManager.Java

技术分享

p3.png

package com.nancyyihao.aidlserver;
// 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 com.nancyyihao.aidlserver.IBookManager {
        private static final java.lang.String DESCRIPTOR = "com.nancyyihao.aidlserver.IBookManager"; // Binder Indentifier

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

        /**
         * Cast an IBinder object into an com.nancyyihao.aidlserver.IBookManager interface,
         * generating a proxy if needed.
         */
        public static com.nancyyihao.aidlserver.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.nancyyihao.aidlserver.IBookManager))) {
                return ((com.nancyyihao.aidlserver.IBookManager) iin);  // local Binder
            }
            return new com.nancyyihao.aidlserver.IBookManager.Stub.Proxy(obj);  // remote Binder
        }

        @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_getBookList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.nancyyihao.aidlserver.Book> _result = this.getBookList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    com.nancyyihao.aidlserver.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.nancyyihao.aidlserver.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.nancyyihao.aidlserver.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.nancyyihao.aidlserver.Book> getBookList() throws android.os.RemoteException {
                android.os.Parcel _data = http://www.mamicode.com/android.os.Parcel.obtain();"hljs-number" style="color: #f99b15;">_reply = android.os.Parcel.obtain();
                java.util.List<com.nancyyihao.aidlserver.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.nancyyihao.aidlserver.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void addBook(com.nancyyihao.aidlserver.Book book) throws android.os.RemoteException {
                android.os.Parcel _data = http://www.mamicode.com/android.os.Parcel.obtain();"hljs-number" style="color: #f99b15;">_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();
                }
            }
        }

        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.nancyyihao.aidlserver.Book> getBookList() throws android.os.RemoteException;

    public void addBook(com.nancyyihao.aidlserver.Book book) throws android.os.RemoteException;
}

Client先调用bindService启动服务,会调用BookManagerService的onCreate方法,接着调用onBind方法,该方法会返回远程的Binder---mBinder,该Binder包含getBookList和AddBook两个方法的具体实现。

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
     private Binder mBinder = new IBookManager.Stub() {

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

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

当Client和server成功建立连接时,就会调用Client的onServiceConnected(name, service)方法把远程的mBinder回调给Client,此时的service就是远程的mBinder对象

private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            Log.e("trace", "onServiceConnected");
            try {
                List<Book> bookList = bookManager.getBookList();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

Client端通过asInterface方法把mBinder转成AIDL接口,如果是本进程内的Binder就直接返回,否则返回内部代理类proxy

 public static com.nancyyihao.aidlserver.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.nancyyihao.aidlserver.IBookManager))) {
                return ((com.nancyyihao.aidlserver.IBookManager) iin);  // local Binder
            }
            return new com.nancyyihao.aidlserver.IBookManager.Stub.Proxy(obj);  // remote Binder
        }

接着执行

 try {
                List<Book> bookList = bookManager.getBookList();
            } catch (Exception e) {
                e.printStackTrace();
            }

此时的bookManager通过asInterface方法转换后,返回的实际上是本地的proxy类

private static class Proxy implements com.nancyyihao.aidlserver.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.nancyyihao.aidlserver.Book> getBookList() throws android.os.RemoteException {
                android.os.Parcel _data = http://www.mamicode.com/android.os.Parcel.obtain();"hljs-number" style="color: #f99b15;">_reply = android.os.Parcel.obtain();
                java.util.List<com.nancyyihao.aidlserver.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.nancyyihao.aidlserver.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void addBook(com.nancyyihao.aidlserver.Book book) throws android.os.RemoteException {
                android.os.Parcel _data = http://www.mamicode.com/android.os.Parcel.obtain();"hljs-number" style="color: #f99b15;">_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();
                }
            }
        }
        static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

通过代码我们可以看到proxy类其实也是一个IBookManager接口,调用bookManager.getBookList()其实是调用Proxy.getBookList

 @Override
            public java.util.List<com.nancyyihao.aidlserver.Book> getBookList() throws android.os.RemoteException {
                android.os.Parcel _data = http://www.mamicode.com/android.os.Parcel.obtain();"hljs-number" style="color: #f99b15;">_reply = android.os.Parcel.obtain();
                java.util.List<com.nancyyihao.aidlserver.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.nancyyihao.aidlserver.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

Proxy.getBookList方法中调用了mRemote.trasact()

    public final boolean transact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException {
        if (false) Log.v("Binder", "Transact: " + code + " to " + this);
        if (data != null) {
            data.setDataPosition(0);
        }
        boolean r = onTransact(code, data, reply, flags);
        if (reply != null) {
            reply.setDataPosition(0);
        }
        return r;
    }

Client就是在这里和Server进行远程通信的!把需要的参数放data中,服务端执行完后把接口写到result里。从代码中可以看到transact方法中调用了onTransact方法。我们再看看onTransact方法有啥东西。

  @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
           ......
                case TRANSACTION_getBookList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.nancyyihao.aidlserver.Book> _result = this.getBookList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
              ......
        }

直接调用了this.getBookList方法返回结果,这个this到底是哪个对象?前面提到mRemote其实是远程中的Binder对象,其代码如下

 private Binder mBinder = new IBookManager.Stub() {

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

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

this.getBookList其实就是mRemote.getBookList,就是上面代码中的mBinder.getBookList!然后把返回结果放到result中即可。至此,整个通信过程分析完毕。

总结

  • Client是通过本地的Proxy类像Server发起通信

  • Client通过ServerConnection接口回调收到Server远程Binder对象

  • IPC过程发生在transact方法中,该方法会挂起直到服务端返回结果,因此不能在主线程调用远程服务。

技术分享

p1.jpg

图片和文中Demo来自《Android开发技术探索》

源码下载

  • Android-AIDL-Demo

延伸阅读

  • Binder学习指南

  • Android Bander设计与实现 - 设计篇

  • Android进程间通信(IPC)机制Binder简要介绍和学习计划

Android-Binder 简析