1. 程式人生 > >程序間通訊學習系列(二)——簡單瞭解Binder機制

程序間通訊學習系列(二)——簡單瞭解Binder機制


Binder機制太複雜了,本文只是簡單的對Binder進行了解。

在Android中Binder是一個類,實現了IBinder介面,在Binder機制中還有兩個重要角色Binder驅動(在核心中)和ServiceManager,這兩部分Android平臺已經實現,我們不必關心,當然有興趣的童鞋可以研究下。本文主要學習瞭解應用層的Client和Service相關的Binder知識。

Messenge,ContentProvider等底層實現都是AIDL,所以以AIDL的簡單實現來學習Binder機制。

需求:圖書管理系統中,1.需要查詢圖書列表;2.新增新書到圖書庫中。

先看一下目錄結構:


首先新建一個Book

類,並實現Parcelable序列化介面(Android Studio一鍵生成序列化程式碼外掛),如下:

public class Book implements Parcelable {
    private double price;
    private long bookId;
    private String bookName;

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeDouble(this.price);
        dest.writeLong(this.bookId);
        dest.writeString(this.bookName);
    }

    public Book() {
    }

    protected Book(Parcel in) {
        this.price = in.readDouble();
        this.bookId = in.readLong();
        this.bookName = in.readString();
    }

    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];
        }
    };
}
新建IBookManager.aidl檔案,生命兩個介面:
package anyan.com.ipcdemo.aboutaidl;
//注意匯入Book類(手動匯入)
import anyan.com.ipcdemo.aboutaidl.Book;

interface IBookManager {
    List<Book> getBookList();//查詢圖書列表
    void addBook(in Book book);//新增新圖書
}

因為在IBookManager.aidl檔案中使用了非基本資料型別,所以Book類不僅要實現序列化介面,還要新建一個Book.aidl檔案(至於為什麼,我也不知道),注意這個Book.aidl檔案的全路徑包名要與Book.java的全路徑包名一致,否則編譯會報錯。內容如下:

package anyan.com.ipcdemo.aboutaidl;

parcelable Book;
Rebuild project成功後再app/build/generated/source/aidl/debug/包名 目錄下回生成一個IBookManager.java檔案,這是AS幫我們自動生成的,這裡使用了代理模式(可簡單瞭解一下,Java設計模式-代理模式實現及原理)主要分析這裡的程式碼:包含兩個介面getBookList()和addBook(),可以看到這兩個介面就是我們在IBookManager.aidl檔案中所宣告的。一個內部類Stub,繼承Binder實現IBookManager介面。Stub類裡面還有一個內部類Proxy也實現了IBookManager介面,這個就是Stub類(也就是Binder)的代理類。
public interface IBookManager extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements anyan.com.ipcdemo.aboutaidl.IBookManager {
        //Binder的唯一標識,一般用類名錶示
        private static final java.lang.String DESCRIPTOR = "anyan.com.ipcdemo.aboutaidl.IBookManager";

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

        /**
         * Cast an IBinder object into an anyan.com.ipcdemo.aboutaidl.IBookManager interface,
         * generating a proxy if needed.
         * 將IBinder物件轉換成IBookManager介面並返回
         * 這種轉換區分程序,如果是在同一程序,直接返回服務端的Stub物件;
         * 如果不在同一程序,則返回代理物件Stub.Proxy;
         */
        public static anyan.com.ipcdemo.aboutaidl.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof anyan.com.ipcdemo.aboutaidl.IBookManager))) {
                return ((anyan.com.ipcdemo.aboutaidl.IBookManager) iin);
            }
            return new anyan.com.ipcdemo.aboutaidl.IBookManager.Stub.Proxy(obj);
        }

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

        /**
         * 該方法在服務端的Binder執行緒池中執行,注意採用同步方法
         * @param code 確定客戶端請求的目標方法是哪個
         * @param data 目標方法所需引數資料
         * @param reply 目標方法返回資料
         * @param flags
         * @return
         * @throws android.os.RemoteException
         */
        @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<anyan.com.ipcdemo.aboutaidl.Book> _result = this.getBookList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    anyan.com.ipcdemo.aboutaidl.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = anyan.com.ipcdemo.aboutaidl.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 anyan.com.ipcdemo.aboutaidl.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;
            }

            /**
             * 該方法在客戶端執行,
             * @return
             * @throws android.os.RemoteException
             */
            @Override
            public java.util.List<anyan.com.ipcdemo.aboutaidl.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<anyan.com.ipcdemo.aboutaidl.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    //呼叫遠端請求,當前執行緒掛起,服務端OnTransact()方法會被呼叫,
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                    //當前執行緒繼續執行,取出結果
                    _reply.readException();
                    _result = _reply.createTypedArrayList(anyan.com.ipcdemo.aboutaidl.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void addBook(anyan.com.ipcdemo.aboutaidl.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();
                }
            }
        }
        
        //方法的id
        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<anyan.com.ipcdemo.aboutaidl.Book> getBookList() throws android.os.RemoteException;

    public void addBook(anyan.com.ipcdemo.aboutaidl.Book book) throws android.os.RemoteException;
}
如下圖所示:


接著看具體怎麼使用,建立一個BookManagerService類:

public class BookService extends Service {
    public BookService() {
    }

    //支援併發讀寫
    private CopyOnWriteArrayList<Book> bookList = new CopyOnWriteArrayList<>();

    private IBookManager.Stub binder = new IBookManager.Stub() {
        @Override
        public List<Book> getBookList() throws RemoteException {
            return bookList;
        }

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

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

在Activity中BindService:

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private IBookManager iBookManager;
    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //用到了上面提到了轉換方法asInterface,將IBinder轉換為介面,
            //同一程序直接返回Stub物件,不同程序返回Proxy代理類
            iBookManager = IBookManager.Stub.asInterface(service);
            try {
                service.linkToDeath(deathRecipient, 0);
                //拿到物件後就可以直接呼叫方法了
                List<Book> bookList = iBookManager.getBookList();
                Log.e(TAG, "onServiceConnected: " + bookList);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    //死亡代理,binder死亡時會收到通知,回撥binderDied方法
    private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            if (iBookManager == null)
                return;
            iBookManager.asBinder().unlinkToDeath(deathRecipient, 0);
            iBookManager = null;
            //重新繫結service
            Intent intent = new Intent(MainActivity.this, BookService.class);
            bindService(intent, conn, BIND_AUTO_CREATE);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

    }

    public void onClick(View view) {
        Intent intent = new Intent(this, BookService.class);
        bindService(intent, conn, BIND_AUTO_CREATE);
    }
}
至此,簡單的Binder機制分析完了,看著可能有點懵,很正常,我是看了好幾篇才只是簡單的理解了。Fighting !!!