Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Binder 機制學習

Android Binder 機制學習

編輯:關於Android編程

概念

Android 中的Binder機制在Android系統框架中發揮著重要的作用,Binder在Android中具體表現為一個類,繼承自IBinder接口,具體的功能是實現IPC(跨進程通訊)機制,還可以理解為一種虛擬設備,設備驅動是/dev/binder,Binder 是 ServiceManager和ActivityManagerService、 PackageManagerService以及其他一個服務之間通訊的橋梁。從Android應用層來說,Binder是客戶端和服務端進行通信的媒介,當你bindService的時候,服務端會返回一個包含了服務端業務調用的 Binder對象,通過這個Binder對象,客戶端就可以獲取服務端提供的服務或者數據,這裡的服務包括普通服務和基於AIDL的服務。

例子

這裡以一個AIDL的例子來分析Binder的工作原理。

首先定義一個AIDL接口:Book.aidl、IBookManager.aidl 他們的代碼如下:

//Book.aidl
// ook.aidl
package com.sample.aidl;

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

parcelable Book;


//IBookManager.aidl
package com.sample.aidl;

// Declare any non-default types here with import statements
import com.sample.aidl.Book;

interface IBookManager {
   void addBook(in Book book);
   List getList();
}

上面定義了一個IBookManager接口,裡面有兩個方法,分別為addBook 和 getList。Book是一個實現了Parcelable接口的對象,跨進程方式傳輸的對象通常都需要實現Parcelable接口。

public class Book implements Parcelable {
    public String name;

    public Book(String name) {
        this.name = name;
    }

    protected Book(Parcel in) {
        this.name = in.readString();
    }

    public static final Creator CREATOR = new Creator() {
        @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.writeString(this.name);
    }
}

上述代碼很簡單,就是定義了一個接口,然後需要build一下module,之後我們就可以拿來用了。

我這裡采用開啟一個服務的方式來實現調用AIDL,當然我這裡的例子都是在一個應用程序上執行的,所以沒有跨進程,但是我們可以以此類推,把服務放在另一個應用程序上也是可以運行的(後面會分析原理)

接下來我們寫一個服務:

public class AIDLService extends android.app.Service {
    private static final String TAG = "AIDLService";

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

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG,"AIDLService onCreate");
    }

    class BookManager extends IBookManager.Stub {
        List books = new ArrayList<>();

        @Override
        public void addBook(Book book) throws RemoteException {
            Log.i(TAG, "addBook :" + book.name);
            books.add(book);
        }

        @Override
        public List getList() throws RemoteException {
            Log.i(TAG, "getList()");
            return books;
        }
    }
}

然後在Activity中啟動這個服務,並獲得IBookManager接口:

public class AIDLActivity extends AppCompatActivity {
    IBookManager manger;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i("onn","onCreate");
        bindService(new Intent(this, AIDLService.class), new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                try {
                    manger = IBookManager.Stub.asInterface(service);
                    manger.addBook(new Book("book11111"));
                    manger.getList();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                manger = null;
            }
        }, BIND_AUTO_CREATE);
    }
}

上面的過程和我們普通使用AIDL的過程是一樣的,定義了一個BookManager實現了IBookManger接口,然後實現了對應的方法,在onBind方法中返回這個Binder對象,然後我們在Acitivty中獲取這個IBookManager對象然後調了一下它實現的兩個方法。(很簡單的一個例子)

不必多言,運行結果如下:

10-18 13:39:53.623 26124-26124/com.sample I/AIDLService: AIDLService onCreate
10-18 13:39:53.673 26124-26124/com.sample I/AIDLService: addBook :book11111
10-18 13:39:53.673 26124-26124/com.sample I/AIDLService: getList()

可以看到我們已經實現了一個簡單的AIDL例子。

原理分析

大家都知道,在寫AIDL的時候,IDE會自動幫我們生成對應的一些代碼,這裡代碼存在的位置是:build->generated->source->aidl 中。我們打開裡面的IBookManager文件,裡面代碼如下:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /android/有心課堂/RecyclerViewSample/app/src/main/aidl/com/sample/aidl/IBookManager.aidl
 */
package com.sample.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.sample.aidl.IBookManager {
        private static final java.lang.String DESCRIPTOR = "com.sample.aidl.IBookManager";

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

        /**
         * Cast an IBinder object into an com.sample.aidl.IBookManager interface,
         * generating a proxy if needed.
         */
        public static com.sample.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.sample.aidl.IBookManager))) {
                return ((com.sample.aidl.IBookManager) iin);
            }
            return new com.sample.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_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    com.sample.aidl.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.sample.aidl.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List _result = this.getList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.sample.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 void addBook(com.sample.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();
                }
            }

            @Override
            public java.util.List getList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.sample.aidl.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

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

    public void addBook(com.sample.aidl.Book book) throws android.os.RemoteException;

    public java.util.List getList() throws android.os.RemoteException;
}

下面我們一步一步來分析:

首先裡面有一個接口IBookManager繼承android.os.IInterface接口,然後這個接口裡面有個內部類:Stub,Stub裡又有一個內部類Proxy

先分析Stub:

Stub繼承自Binder對象並且實現了IBookManager接口:

public static abstract class Stub extends android.os.Binder implements com.sample.aidl.IBookManager {
        private static final java.lang.String DESCRIPTOR = "com.sample.aidl.IBookManager";

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

        /**
         * Cast an IBinder object into an com.sample.aidl.IBookManager interface,
         * generating a proxy if needed.
         */
        public static com.sample.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.sample.aidl.IBookManager))) {
                return ((com.sample.aidl.IBookManager) iin);
            }
            return new com.sample.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_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    com.sample.aidl.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.sample.aidl.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_getList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List _result = this.getList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

我們聚焦到asInterface(IBinder obj) 方法中,簡單的讀一讀代碼,可以理解到裡面的代碼邏輯是這樣的:

首先通過queryLocalInterface方法查詢是不是本地的服務,如果是本地的服務就強轉成IBookManger並且返回,如果不是就返回Proxy對象。

這樣看來我們上面寫的例子中調用的就是本地的服務,所以就會直接返回這個對象咯。

我們再看回Activity的代碼:

 public void onServiceConnected(ComponentName name, IBinder service){
      manger = IBookManager.Stub.asInterface(service);
 }

這樣我們是不是可以很好的理解上面的代碼為什麼要這樣寫?

好了,如果是本地的服務就這樣很好的理解,但是遠程的服務就會相對來說復雜一點:

我們首先來看一看Proxy的代碼:

private static class Proxy implements com.sample.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 void addBook(com.sample.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();
                }
            }

            @Override
            public java.util.List getList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.sample.aidl.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

當服務是遠程服務的時候,如果是遠程服務asInterface方法會返回:

return new com.sample.aidl.IBookManager.Stub.Proxy(obj);

返回了一個Proxy對象,然後我們在Activity中調用對應的addBook getList方法的時候,就是Binder大發神威的時候了。假如我們調用了addBook 方法的時候,Binder會調用Proxy中的addBook方法(這個過程運行在Binder線程池中):

  @Override
            public void addBook(com.sample.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();
                }
            }

這裡首先把book的數據寫入_data中然後再調用了一下transact方法。

我們看會Stub中的方法,裡面有一個方法叫做onTransact。很明顯,調用了transact最終會調用這裡的方法。

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

通過分析,我們看到,他會從data那裡把Book對象拿出來,然後調了一下this.addBook()方法,這個方法是一個未實現的方法,具體的實現會在遠程服務端中。

通過以上的分析,這個遠程調用服務的流程就理順了,其實在AIDL裡面,我們可以簡單的把本地和遠程的服務稱之為客戶端和服務端。

整個過程是這樣的:首先客戶端會發起遠程調用,然後就會掛起等待服務端執行完畢並且返回的時候客戶端才會繼續執行。

拓展

上面我們理順了AIDL的基本執行流程,其實我們在開發的時候SDK為我們提供了直接寫AIDL文件的方式,會自動幫我們生成對應的實現。其實我們完全可以拋棄AIDL文件自己實現對應的功能。

我們完全可以照著生成的代碼類比的寫出來,我們可以這樣做:

首先聲明一個接口繼承IInterface叫做IBookManger:

interface IBookManager extends andorid.os.IInterface{
   void addBook(in Book book);
   List getList();
}

然後寫一個類IBookManagerImpl,繼承自IBookManger,這個類類似於Stub,並且我們可以實現了addBook和getList這兩個方法,然後我們在Activity中調用的時候就很簡單了。直接:

    manager = IBookMangerImpl.asInterface(service);

在Service的onBind中返回 new BookMangerImpl()即可。

總來的來,Binder在上層應用中只是一中跨進程通訊方式,並且結構設計采用的是C/S結構。

源碼地址:https://github.com/Jamlh/Sample

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved