Android中Binder詳細(xì)學(xué)習(xí)心得
該文章是一個系列文章,是本人在Android開發(fā)的漫漫長途上的一點(diǎn)感想和記錄,我會盡量按照先易后難的順序進(jìn)行編寫該系列。該系列引用了《Android開發(fā)藝術(shù)探索》以及《深入理解Android 卷Ⅰ,Ⅱ,Ⅲ》中的相關(guān)知識,另外也借鑒了其他的優(yōu)質(zhì)博客,在此向各位大神表示感謝,膜拜!?。×硗?,本系列文章知識可能需要有一定Android開發(fā)基礎(chǔ)和項(xiàng)目經(jīng)驗(yàn)的同學(xué)才能更好理解,也就是說該系列文章面向的是Android中高級開發(fā)工程師。
前言
上一次還不如不說去面試了呢,估計是掛了,數(shù)據(jù)結(jié)構(gòu)與算法方面雖然面試前突擊了一波,但是時間太短,當(dāng)時學(xué)的也不好。另外Android的一些知識也不是很了解。不過這也加大了我寫博客的動力。許多知識總覺得自己掌握的還挺好,不過一問到比較細(xì)節(jié)的方面就不太清楚了。所以寫這整個博客的目的也是加深自己的知識,培養(yǎng)自己的溝通能力,和大家一起學(xué)習(xí)吧。
好了,閑話少說,我們這一篇先解決上一篇中遺留的問題,之后有時間的話,我把這次的面試經(jīng)歷單寫一篇博客,和大家共勉。
本篇我們來看一下ServiceManager。上一篇中沒怎么說它,ServiceManager作為Android系統(tǒng)服務(wù)的大管家。我們還是有必要來看一下它的。
ServiceManager概述
ServiceManager是Android世界中所有重要系統(tǒng)服務(wù)的大管家。像前文提到的AMS(ActivityManagerService),還有許多以后可能分析到的PackageManagerService等等服務(wù)都需要像ServiceManager中注冊。那么為何需要一個ServiceManager呢,其重要作用何在呢?私認(rèn)為有以下幾點(diǎn):
ServiceManager能集中管理系統(tǒng)內(nèi)的所有服務(wù),它能施加權(quán)限控制,并不是任何進(jìn)程都能注冊服務(wù)的。 ServiceManager支持通過字符串名稱來查找對應(yīng)的Service。這個功能很像DNS。由于各種原因的影響,Server進(jìn)程可能生死無常。 如果讓每個Client都去檢測,壓力實(shí)在太大了。 現(xiàn)在有了統(tǒng)一的管理機(jī)構(gòu),Client只需要查詢ServiceManager,就能把握動向,得到最新信息。
ServiceManager
[SystemServer.java]
public void setSystemProcess() {
try {
//注冊服務(wù),第二個參數(shù)為this,這里假設(shè)SystemServer通過“socket”與SM交互
ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
..........
} catch (PackageManager.NameNotFoundException e) {
........
}
}
我們SystemServer進(jìn)程中的AMS通過SM的代理與SM進(jìn)程交互(讀者也可以把這個過程想象為你所能理解的進(jìn)程間通信方式,例如管道、Socket等),并把自己注冊在SM中。這個情況下,我們使用ServiceManager的代理與SM進(jìn)程交互,既然有代理,那么也得有對應(yīng)的服務(wù)端。那么根據(jù)我們之前博客的思路分析的話,就是如下的流程:
ServiceManager是如何啟動的?
按照我們之前博客的思路,我們在SystemServer端有了個ServiceManager的代理,那么Android系統(tǒng)中應(yīng)該提供類似AMS這樣的繼承或間接繼承自java層Binder然后重寫onTransact方法以處理請求。但是并沒有,ServiceManager并沒有使用如AMS這樣復(fù)雜的Binder類結(jié)構(gòu)。而是直接與Binder驅(qū)動設(shè)備打交道。所以我們上一篇說了ServiceManager不一樣。我們來看具體看一下。
ServiceManager在init.rc配置文件中配置啟動,是一個以c/c++語言編寫的程序。init進(jìn)程、SM進(jìn)程等關(guān)系如下圖

我們來看它的main方法。
int main(int argc, char **argv)
{
struct binder_state *bs;
//①應(yīng)該是打開binder設(shè)備吧?
bs = binder_open(128*1024);
if (!bs) {
ALOGE("failed to open binder driver\n");
return -1;
}
//②成為manager
if (binder_become_context_manager(bs)) {
ALOGE("cannot become context manager (%s)\n", strerror(errno));
return -1;
}
......
//③處理客戶端發(fā)過來的請求
binder_loop(bs, svcmgr_handler);
return 0;
}
①打開Binder設(shè)備
[binder.c]
struct binder_state*binder_open(unsigned mapsize)
{
struct binder_state*bs;
bs=malloc(sizeof(*bs));
......
//打開Binder設(shè)備
bs->fd=open("/dev/binder",O_RDWR);
......
bs->mapsize=mapsize;
//進(jìn)行內(nèi)存映射
bs->mapped=mmap(NULL,mapsize,PROT_READ,MAP_PRIVATE,bs->
fd,0);
}
這一步的目的是把內(nèi)核層的binder驅(qū)動映射到用戶空間。我們知道進(jìn)程之間是獨(dú)立的,進(jìn)程呢運(yùn)行在用戶空間內(nèi),內(nèi)核層的Binder驅(qū)動可以看成是一個文件(實(shí)際上它也是,Linux上都是文件)。這一步呢,可以看成把一個文件映射到用戶空間,我們的進(jìn)程呢通過這個文件進(jìn)行交互。

②成為manager
[Binder.c]
int binder_become_context_manager(struct binder_state*bs)
{
//實(shí)現(xiàn)太簡單了!這個有個0,什么鬼?
return ioctl(bs->fd,BINDER_SET_CONTEXT_MGR,0);
}
③處理客戶端發(fā)過來的請求
[Binder.c]
void binder_loop(struct binder_state *bs, binder_handler func)
{
int res;
struct binder_write_read bwr;
uint32_t readbuf[32];
bwr.write_size = 0;
bwr.write_consumed = 0;
bwr.write_buffer = 0;
readbuf[0] = BC_ENTER_LOOPER;
binder_write(bs, readbuf, sizeof(uint32_t));
for (;;) {//果然是循環(huán)
bwr.read_size = sizeof(readbuf);
bwr.read_consumed = 0;
bwr.read_buffer = (uintptr_t) readbuf;
res = ioctl(bs->fd, BINDER_WRITE_READ, &bwr);
if (res < 0) {
ALOGE("binder_loop: ioctl failed (%s)\n", strerror(errno));
break;
}
//接收到請求交給binder_parse,最終會調(diào)用func來處理這些請求
res = binder_parse(bs, 0, (uintptr_t) readbuf, bwr.read_consumed, func);
if (res == 0) {
ALOGE("binder_loop: unexpected reply?!\n");
break;
}
if (res < 0) {
ALOGE("binder_loop: io error %d %s\n", res, strerror(errno));
break;
}
}
}
上面?zhèn)魅雈unc的是svcmgr_ handler函數(shù)指針,所以會在svcmgr_handler中進(jìn)行集中處理客戶端的請求。
[service_manager.c]
ServiceManager的代理是如何獲得的?
我們來回到最初的調(diào)用
[SystemServer.java]
public void setSystemProcess() {
try {
//注冊服務(wù),第二個參數(shù)為this,這里假設(shè)SystemServer通過“socket”與SM交互
ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
..........
} catch (PackageManager.NameNotFoundException e) {
........
}
}
上面的請求最終是通過SM服務(wù)代理發(fā)送的,那這個代理是怎么來的呢?我們來看
[ServiceManager.java]
public static void addService(String name, IBinder service) {
try {
getIServiceManager().addService(name, service, false);
} catch (RemoteException e) {
Log.e(TAG, "error in addService", e);
}
}
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
// 是這里,沒錯了
sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
return sServiceManager;
}
我們先來看BinderInternal.getContextObject()
[BinderInternal.java]
//好吧,它還是個native函數(shù) public static final native IBinder getContextObject();
跟進(jìn)[android_ util_Binder.cpp]
static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
return javaObjectForIBinder(env, b);
}
跟進(jìn)[ProcessState.cpp]
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
return getStrongProxyForHandle(0);
}
/*這個函數(shù)是不是我們之前見過*/
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
sp<IBinder> result;
AutoMutex _l(mLock);
handle_entry* e = lookupHandleLocked(handle);
if (e != NULL) {
IBinder* b = e->binder;
if (b == NULL || !e->refs->attemptIncWeak(this)) {
if (handle == 0) {//這里我們的handle為0
Parcel data;
//在handle對應(yīng)的BpBinder第一次創(chuàng)建時
//會執(zhí)行一次虛擬的事務(wù)請求,以確保ServiceManager已經(jīng)注冊
status_t status = IPCThreadState::self()->transact(
0, IBinder::PING_TRANSACTION, data, NULL, 0);
if (status == DEAD_OBJECT)
return NULL;//如果ServiceManager沒有注冊,直接返回
}
//這里還是以handle參數(shù)創(chuàng)建了BpBinder
b = new BpBinder(handle);
e->binder = b;
if (b) e->refs = b->getWeakRefs();
result = b;
} else {
result.force_set(b);
e->refs->decWeak(this);
}
}
return result;
}
我們再一步步返回
[android_ util_Binder.cpp]
static jobject android_os_BinderInternal_getContextObject(JNIEnv* env, jobject clazz)
{
sp<IBinder> b = ProcessState::self()->getContextObject(NULL);
//這里的b = new BpBinder(0);
return javaObjectForIBinder(env, b);
}
/*這個函數(shù)我們上一篇是不是也見過*/
jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val) {
if (val == NULL) return NULL;
//如果val是Binder對象,進(jìn)入下面分支,此時val是BpBinder
if (val->checkSubclass(&gBinderOffsets)) {
// One of our own!
jobject object = static_cast<JavaBBinder*>(val.get())->object();
LOGDEATH("objectForBinder %p: it's our own %p!\n", val.get(), object);
return object;
}
.........
//調(diào)用BpBinder的findObject函數(shù)
//在Native層的BpBinder中有一個ObjectManager,它用來管理在Native BpBinder上創(chuàng)建的Java BinderProxy對象
//findObject用于判斷gBinderProxyOffsets中,是否存儲了已經(jīng)被ObjectManager管理的Java BinderProxy對象
jobject object = (jobject)val->findObject(&gBinderProxyOffsets);
if (object != NULL) {
jobject res = jniGetReferent(env, object);
............
//如果該Java BinderProxy已經(jīng)被管理,則刪除這個舊的BinderProxy
android_atomic_dec(&gNumProxyRefs);
val->detachObject(&gBinderProxyOffsets);
env->DeleteGlobalRef(object);
}
//創(chuàng)建一個新的BinderProxy對象
object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);
if (object != NULL) {
env->SetLongField(object, gBinderProxyOffsets.mObject, (jlong)val.get());
val->incStrong((void*)javaObjectForIBinder);
jobject refObject = env->NewGlobalRef(
env->GetObjectField(object, gBinderProxyOffsets.mSelf));
//新創(chuàng)建的BinderProxy對象注冊到BpBinder的ObjectManager中,同時注冊一個回收函數(shù)proxy_cleanup
//當(dāng)BinderProxy對象detach時,proxy_cleanup函數(shù)將被調(diào)用,以釋放一些資源
val->attachObject(&gBinderProxyOffsets, refObject,
jnienv_to_javavm(env), proxy_cleanup);
// Also remember the death recipients registered on this proxy
sp<DeathRecipientList> drl = new DeathRecipientList;
drl->incStrong((void*)javaObjectForIBinder);
//將死亡通知list和BinderProxy聯(lián)系起來
env->SetLongField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast<jlong>(drl.get()));
// Note that a new object reference has been created.
android_atomic_inc(&gNumProxyRefs);
//垃圾回收相關(guān);利用gNumRefsCreated記錄創(chuàng)建出的BinderProxy數(shù)量
//當(dāng)創(chuàng)建出的BinderProxy數(shù)量大于200時,該函數(shù)將利用BinderInternal的ForceGc函數(shù)進(jìn)行一個垃圾回收
incRefsCreated(env);
return object;
}
}
接著返回到[ServiceManager.java]
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
// 是這里,沒錯了BinderInternal.getContextObject()是BinderProxy對象
sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
return sServiceManager;
}
跟進(jìn)[[ServiceManagerNative.java]]
static public IServiceManager asInterface(IBinder obj)
{
if (obj == null) {
return null;
}
我們知道這里的obj指向的是BinderProxy對象
IServiceManager in =
(IServiceManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ServiceManagerProxy(obj);
}
跟進(jìn)[Binder.java]
final class BinderProxy implements IBinder {
public IInterface queryLocalInterface(String descriptor) {
return null;
}
}
跟進(jìn)[ServiceManagerNative.java]
class ServiceManagerProxy implements IServiceManager {
public ServiceManagerProxy(IBinder remote) {
//這里的mRemote指向了BinderProxy,與我們上一篇博客中講述的遙相呼應(yīng)
mRemote = remote;
}
}
本節(jié)小結(jié)
我們詳盡講述了SM進(jìn)程的啟動以及它作為服務(wù)大管家的意義。結(jié)合上一篇的內(nèi)容我們總算是把Binder講述的比較清楚了。
Binder補(bǔ)充說明 AIDL
經(jīng)過上面的介紹,你應(yīng)該明白Java層Binder的架構(gòu)中,Bp端可以通過BinderProxy的transact()方法與Bn端發(fā)送請求,而Bn端通過集成Binder重寫onTransact()接收并處理來自Bp端的請求。這個結(jié)構(gòu)非常清晰簡單,在Android6.0,我們可以處處看到這樣的設(shè)計,比如我們的ActivityManagerNavtive這個類,涉及到Binder通信的基本上都是這種設(shè)計。不過如果我們想要自己來定義一些遠(yuǎn)程服務(wù)。那這樣的寫法就比較繁瑣,還好Android提供了AIDL,并且在Android8.0之后,我們可以看到與ActivityManagerNavtive相似的許多類已經(jīng)被標(biāo)注過時,因?yàn)锳ndroid系統(tǒng)也使用AIDL了。
AIDL的簡單例子
AIDL的語法與定義一個java接口非常類似。下面我就定以一個非常簡單的aidl
IMyAidlInterface.aidl
interface IMyAidlInterface {
int getTest();
}
然后基本上就行了,我們重新build之后會得到一個
IMyAidlInterface.java文件,這個文件由aidl工具生成,我們現(xiàn)在使用的基本是AndroidStudio,即使你使用的是Eclipse也沒關(guān)系,這個文件會自動生成,不需要你操心。但是我們還是得來看看我們生成的這個文件
public interface IMyAidlInterface extends android.os.IInterface {
//抽象的Stub類,繼承自Binder并實(shí)現(xiàn)我們定義的IMyAidlInterface接口
//繼承自Binder,重寫onTransact方法,是不是感覺跟我們的XXXNative很像
public static abstract class Stub extends android.os.Binder implements com.mafeibiao.testapplication.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "com.mafeibiao.testapplication.IMyAidlInterface";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.mafeibiao.testapplication.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.mafeibiao.testapplication.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.mafeibiao.testapplication.IMyAidlInterface))) {
return ((com.mafeibiao.testapplication.IMyAidlInterface) iin);
}
return new com.mafeibiao.testapplication.IMyAidlInterface.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_getTest: {
data.enforceInterface(DESCRIPTOR);
int _result = this.getTest();
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
/*這個Proxy不用說肯定是代理了,其內(nèi)部還有個mRemote對象*/
private static class Proxy implements com.mafeibiao.testapplication.IMyAidlInterface {
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 int getTest() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getTest, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_getTest = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}
public int getTest() throws android.os.RemoteException;
}
可見,AIDL的本質(zhì)與XXXNative之類的類并沒有什么本質(zhì)的不同,不過他的出現(xiàn)使得構(gòu)建一個Binder服務(wù)的工作大大簡化了。
AIDL的使用詳解
上面用一個非常簡單的小例子來解密AIDL的本質(zhì),但是在實(shí)際使用AIDL的時候還有許多地方需要注意。
AIDL支持的數(shù)據(jù)類型 基本數(shù)據(jù)類型(int,long,charmboolean,double等) String和CharSequence List:只支持ArrrayList,并且里面每個元素的類型必須是AIDL支持的 Map:只支持HashMap,t,并且里面每個元素的類型必須是AIDL支持的 Parcelable:所有實(shí)現(xiàn)Parcelable接口的對象 AIDL:所有的AIDL接口本身也可以在AIDL文件中使用
以上6種數(shù)據(jù)類型就是AIDL所支持的所有類型,其中自定義的Parcel對象和AIDL對象必須要顯示import進(jìn)來,不管他們是否和當(dāng)前的AIDL文件位于同一個包內(nèi)。
另外一個需要注意的地方是如果我們在AIDL中使用了自定義的Parcelable接口的對象,那么我們必須新建一個和它同名的AIDL文件,并在其中聲明它為Parcelable類型。
如下例
[IBookManager.aidl]
package com.ryg.chapter_2.aidl;
/*這里顯示import*/
import com.ryg.chapter_2.aidl.Book;
interface IBookManager {
//這里我們使用了自定義的Parcelable對象
List<Book> getBookList();
void addBook(in Book book);
}
這里我們新建一個與Book同名的AIDL文件并聲明
[Book.aidl]
package com.ryg.chapter_2.aidl; parcelable Book;
定向tag
定向tag:這是一個極易被忽略的點(diǎn)——這里的“被忽略”指的不是大家都不知道,而是很少人會正確的使用它。
AIDL中的定向 tag 表示了在跨進(jìn)程通信中數(shù)據(jù)的流向,其中 in 表示數(shù)據(jù)只能由客戶端流向服務(wù)端, out 表示數(shù)據(jù)只能由服務(wù)端流向客戶端,而 inout 則表示數(shù)據(jù)可在服務(wù)端與客戶端之間雙向流通。其中,數(shù)據(jù)流向是針對在客戶端中的那個傳入方法的對象而言的。in 為定向 tag 的話表現(xiàn)為服務(wù)端將會接收到一個那個對象的完整數(shù)據(jù),但是客戶端的那個對象不會因?yàn)榉?wù)端對傳參的修改而發(fā)生變動;out 的話表現(xiàn)為服務(wù)端將會接收到那個對象的的空對象,但是在服務(wù)端對接收到的空對象有任何修改之后客戶端將會同步變動;inout 為定向 tag 的情況下,服務(wù)端將會接收到客戶端傳來對象的完整信息,并且客戶端將會同步服務(wù)端對該對象的任何變動。
另外,Java 中的基本類型和 String ,CharSequence 的定向 tag 默認(rèn)且只能是 in 。還有,請注意,請不要濫用定向 tag ,而是要根據(jù)需要選取合適的——要是不管三七二十一,全都一上來就用 inout ,等工程大了系統(tǒng)的開銷就會大很多——因?yàn)榕帕姓韰?shù)的開銷是很昂貴的。
所有的非基本參數(shù)都需要一個定向tag來指出數(shù)據(jù)的流向,不管是 in , out , 還是 inout ?;緟?shù)的定向tag默認(rèn)是并且只能是 in 。
本篇總結(jié)
我們本篇詳細(xì)分析了ServiceManager,ServiceManager并沒有使用復(fù)雜的類結(jié)構(gòu),他直接與Binder驅(qū)動設(shè)備交互達(dá)到IPC通信的目的。感謝你對腳本之家的支持。
相關(guān)文章
Android提高之TelephonyManager功能探秘
這篇文章主要介紹了Android的TelephonyManager功能,可以幫助讀者更好的理解Java反射機(jī)制,需要的朋友可以參考下2014-08-08
Android開發(fā)實(shí)現(xiàn)日期時間控件選擇
這篇文章主要為大家詳細(xì)介紹了Android開發(fā)實(shí)現(xiàn)日期時間控件選擇,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-09-09
微信小程序電商常用倒計時實(shí)現(xiàn)實(shí)例
這篇文章主要介紹了微信小程序電商常用倒計時實(shí)現(xiàn)實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-06-06
Android onSaveInstanceState和onRestoreInstanceState觸發(fā)的時機(jī)
這篇文章主要介紹了Android onSaveInstanceState和onRestoreInstanceState觸發(fā)的時機(jī)的相關(guān)資料,需要的朋友可以參考下2017-05-05
Android使用Handler實(shí)現(xiàn)View彈性滑動
這篇文章主要介紹了Android使用Handler實(shí)現(xiàn)View彈性滑動,介紹的非常詳細(xì),具有參考借鑒價值,需要的朋友可以參考下2016-08-08
Android應(yīng)用程序保持后臺喚醒(使用WakeLock實(shí)現(xiàn))
本篇文章主要介紹了使用WakeLock使Android應(yīng)用程序保持后臺喚醒的相關(guān)知識。具有很好的參考價值。下面跟著小編一起來看下吧2017-04-04

