IPC機制之Binder機制

前言

Binder是Android的一個類,它實現(xiàn)了IBinder接口。從IPC角度來說,Binder是Android中的一種跨進程通信方式;從Android應用層來說,Binder是客戶端和服務端進行通信的媒介,當bindService的時候,服務端會返回一個包含了服務端業(yè)務調(diào)用的Binder對象,通過這個Binder對象,客戶端就可以獲取服務端提供的服務或者數(shù)據(jù),這里的服務包括普通服務和基于AIDL的服務。

在Android開發(fā)中,Binder主要用在Service中,包括AIDL(Android Interface Defination Language)和Messenger,其中普通Service中的Binder不涉及進程間通信,所以較為簡單,無法觸及Binder的核心,而Messenger的底層其實是AIDL,所以這里選擇AIDL來分析Binder的工作機制。

使用示例

新建三個文件,Book.java、Book.aidl和IBookManager.aidl,代碼如下:
Book.java

package com.example.runningh.myapplication.aidl;

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

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

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

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

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

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

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

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

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

Book .java是一個表示圖書信息的類,實現(xiàn)了Parcelable接口。

Book.aidl:

package com.example.runningh.myapplication.aidl;

parcelable Book;

Book.aidl是Book類在AIDL中的聲明。

IBookManager.aidl:

package com.example.runningh.myapplication.aidl;

import com.example.runningh.myapplication.aidl.Book;
import com.example.runningh.myapplication.aidl.IOnNewBookArrivedListener;

interface IBookManager {

    List<Book> getBookList();

    void addBook(in Book book);
}

IBookManager.adil是我們定義的一個接口,里面有兩個方法:getBookList和addBook,其中getBookList用于從遠程服務端獲取圖書列表,而addBook用于往圖書列表中添加一本書。

注意:在IBookManager.aidl中要手動導入Book類,不支持自動導入,這是AIDL的特殊之處。

通過執(zhí)行build操作后,系統(tǒng)為IBookManager.aidl生成的Binder類,在gen目錄下的source文件夾下可找到IBookManager.java類。

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /Users/RunningH/Documents/TencentGame/ThirdParty/ThemeSkinning/MyApplication2/app/src/main/aidl/com/example/runningh/myapplication/aidl/IBookManager.aidl
 */
package com.example.runningh.myapplication.aidl;
public interface IBookManager extends android.os.IInterface {
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements com.example.runningh.myapplication.aidl.IBookManager {
        private static final java.lang.String DESCRIPTOR = "com.example.runningh.myapplication.aidl.IBookManager";
        
        /** Construct the stub at attach it to the interface. */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }
        
        /**
         * Cast an IBinder object into an com.example.runningh.myapplication.aidl.IBookManager interface,
         * generating a proxy if needed.
         */
        public static com.example.runningh.myapplication.aidl.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj==null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof com.example.runningh.myapplication.aidl.IBookManager))) {
                return ((com.example.runningh.myapplication.aidl.IBookManager)iin);
            }
            return new com.example.runningh.myapplication.aidl.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_getBookList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.example.runningh.myapplication.aidl.Book> _result = this.getBookList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    com.example.runningh.myapplication.aidl.Book _arg0;
                    if ((0!=data.readInt())) {
                        _arg0 = com.example.runningh.myapplication.aidl.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.example.runningh.myapplication.aidl.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.example.runningh.myapplication.aidl.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.example.runningh.myapplication.aidl.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.example.runningh.myapplication.aidl.Book.CREATOR);
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
            
            @Override public void addBook(com.example.runningh.myapplication.aidl.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();
                }
            }
        }
    }


    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.example.runningh.myapplication.aidl.Book> getBookList() throws android.os.RemoteException;
    public void addBook(com.example.runningh.myapplication.aidl.Book book) throws android.os.RemoteException;
}

首先這個類聲明了兩個方法getBookList和addBook,顯然這就是我們在IBookMananger.aidl中所聲明的方法,同時它還聲明了兩個整型的id分別用于標識這兩個方法,這兩個id用于標識在transact過程中客戶端所請求的到底是哪個方法。接著,它聲明了一個內(nèi)部類Stub,這個Stub就是一個Binder類,當客戶端和服務端都位于同一個進程時,方法調(diào)用不會跨進程的transact過程,而當兩者位于不同進程時,方法調(diào)用需要走transact過程,這個邏輯由Stub的內(nèi)部代理Proxy來完成。

我們看一下怎么使用這個類,通過調(diào)用過程來熟悉其中的方法。

我們新建一個工程并在新建BookManagerActivity、BookManagerService并將BookManagerService放在另外一個進程中(在manifest中聲明)。在BookManagerActivity通過BindService的方式和BookManagerService綁定。

package com.example.runningh.myapplication.aidl;

import android.app.Activity;
import android.app.ListActivity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;
import android.util.Log;

import com.example.runningh.myapplication.R;

import java.util.List;

/**
 * Created by RunningH on 2017/11/26.
 */

public class BookManagerActivity extends Activity {
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IBookManager iBookManager = IBookManager.Stub.asInterface(service); //注意這里調(diào)用了Stub的asInterface方法,通過服務端的IBinder對象找到
            try {
                List<Book> list = iBookManager.getBookList();
                Log.i("ABC", "query book list, list type:" + list.getClass().getCanonicalName());
                Log.i("ABC", "query book list:" + list.toString());
                Book newBook = new Book(3, "HTML");
                iBookManager.addBook(newBook);
                Log.i("ABC", "add book:" + newBook);
                List<Book> newList = iBookManager.getBookList();
                Log.i("ABC", "query book list:" + newList.toString());
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

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

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

BookManagerService.java:

package com.example.runningh.myapplication.aidl;

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 RunningH on 2017/11/26.
 */

public class BookManagerService extends Service {
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();

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

    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "IOS"));
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
}
  • BookManagerService中new了一個Stub對象,并重寫addBook和getBookList方法。這樣Stub的構造方法被調(diào)用,構造方法中調(diào)用了Binder的attachInterface將該Binder對象(Stub就是一個Binder對象)和DESCRIPTOR(Binder的唯一標識,一般用Binder的類名表示)聯(lián)系起來。
  • 上面生成的Stub對象(也就是Binder對象)通過BinderService的方式傳遞給客戶端,也就是BookManagerActivity的mConnection的onServiceConnected方法中service對象。通過調(diào)用IBookManager.Stub.asInterface方法得到客戶端所需要的AIDL接口類型對象。這是區(qū)分進程的,如果客戶端和服務端處于同一個進程,那么執(zhí)行queryLocalInterface(DESCRIPTOR)返回就不為空(還記得上面生成Stub對象的時候?qū)⒃搶ο蠛虳ESCRIPTOR綁定嗎?這里就是取出來),所以此方法返回的就是服務端的Stub對象本身。如果是不同的進程則返回的是系統(tǒng)封裝或的Stub.proxy對象。對應于下面的則是new了一個Stub.Proxy對象。
public static com.example.runningh.myapplication.aidl.IBookManager asInterface(android.os.IBinder obj) {
     if ((obj==null)) {
         return null;
     }
     android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
     if (((iin!=null)&&(iin instanceof com.example.runningh.myapplication.aidl.IBookManager))) {
          return ((com.example.runningh.myapplication.aidl.IBookManager)iin);
    }
    return new com.example.runningh.myapplication.aidl.IBookManager.Stub.Proxy(obj);
}
  • 先看如果處于同一個進程的情況,得到服務端的Stub對象后,直接調(diào)用getBookList和addBook方法。因為Stub對象實現(xiàn)了IBookManager接口,并實現(xiàn)了其中的getBookList和addBook方法,所以這里實際調(diào)用的是getBookList和addBook方法。這樣就實現(xiàn)了客戶端和服務端的交互。
  • 再來看不同進程的情況,我們得到的是一個Stub.Proxy對象。在客戶端調(diào)用getBookList和addBook方法,實際上調(diào)用的是Stub.Proxy對象的getBookList和addBook方法。
private static class Proxy implements com.example.runningh.myapplication.aidl.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.example.runningh.myapplication.aidl.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.example.runningh.myapplication.aidl.Book> _result;
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
            _reply.readException();
            _result = _reply.createTypedArrayList(com.example.runningh.myapplication.aidl.Book.CREATOR);
        }
        finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }

    @Override public void addBook(com.example.runningh.myapplication.aidl.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();
        }
    }
}
  • getBookList方法中調(diào)用了mRemote.transact方法,第一個參數(shù)標識調(diào)用哪一個方法,第二個參數(shù)標識需要傳遞進去的參數(shù),第三個參數(shù)標識需要返回的數(shù)據(jù),第四個參數(shù)一般為0。調(diào)用transact方法發(fā)起RPC(遠程過程調(diào)用)請求,同時當前線程掛起(等待數(shù)據(jù)返回),然后服務端的onTransact方法會被調(diào)用,直到RPC過程返回后,當前線程繼續(xù)執(zhí)行,并從_reply中取出RPC過程的返回結果。
  • addBook方法調(diào)用了mRemote.transact方法,參數(shù)的意思同上一樣。由于addBook不需要返回值,所以它不需要從_reply中取出返回值。
  • 上面兩個方法都調(diào)用了transact方法,那么該方法被調(diào)用后會觸發(fā)服務端的Stub對象的onTransact方法被調(diào)用。
@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.example.runningh.myapplication.aidl.Book> _result = this.getBookList();
            reply.writeNoException();
            reply.writeTypedList(_result);
            return true;
        }
        case TRANSACTION_addBook: {
            data.enforceInterface(DESCRIPTOR);
            com.example.runningh.myapplication.aidl.Book _arg0;
            if ((0!=data.readInt())) {
                _arg0 = com.example.runningh.myapplication.aidl.Book.CREATOR.createFromParcel(data);
            }
            else {
                _arg0 = null;
            }
            this.addBook(_arg0);
            reply.writeNoException();
            return true;
        }
    }
    return super.onTransact(code, data, reply, flags);
}
  • 通過onTransact方法,我們通過code參數(shù)作為標識具體調(diào)用的哪一個方法,比如TRANSACTION_getBookList就是調(diào)用getBookList方法,這里做的操作是調(diào)用Stub對象的getBookList方法并且將結果寫入到reply(如果該操作需要結果返回的話)中然后返回。

總結:上述就是Binder的工作機制,我們需要注意的是當客戶端發(fā)起遠程請求時,由于當前線程會被掛起直至服務器端進程返回數(shù)據(jù),所以如果一個遠程方法是很耗時的,那么不能在UI線程中發(fā)起遠程請求;其次由于服務端的Binder方法運行在Binder的線程池中,所以Binder方法不管是否耗時都是應該采用同步的方法去實現(xiàn),因為它已經(jīng)運行在一個線程中了。

參考:這篇文章是對《Android開發(fā)藝術探索》中IPC機制一章中的Binder內(nèi)容進行的總結。這本書的作者任玉剛是一個大牛,這本書也是很通俗易懂,對Android開發(fā)的進階有非常大的幫助。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容