前言
IPC 系列文章:
建議按順序閱讀祭埂。
Android IPC 之Service 還可以這么理解
Android IPC 之Binder基礎(chǔ)
Android IPC 之Binder應(yīng)用
Android IPC 之AIDL應(yīng)用(上)
Android IPC 之AIDL應(yīng)用(下)
Android IPC 之Messenger 原理及應(yīng)用
Android IPC 之服務(wù)端回調(diào)
Android IPC 之獲取服務(wù)(IBinder)
Android Binder 原理換個(gè)姿勢(shì)就頓悟了(圖文版)
通過前面的文章我們知道恩伺,要進(jìn)行進(jìn)程通信的核心是能拿到另一個(gè)進(jìn)程暴露出來的IBiner引用睦授。本篇將重點(diǎn)分析獲取IBinder的方式及其原理涩嚣。
通過本篇文章驮宴,你將了解到:
1桩了、獲取系統(tǒng)服務(wù)
2踱侣、獲取自定義服務(wù)
3、兩者區(qū)別與聯(lián)系
本篇文章趟畏,系統(tǒng)服務(wù)贡歧、自定義服務(wù)里的服務(wù)并非單純是指Service,而是提供某一類功能的"服務(wù)"赋秀。
1利朵、獲取系統(tǒng)服務(wù)
簡(jiǎn)單例子
以手機(jī)振動(dòng)為例:
Vibrator vibrator = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(1000);
調(diào)用Context 方法getSystemService(xx),xx表示服務(wù)名字猎莲,最終返回Vibrator绍弟。
拿到Vibrator 引用后就可以調(diào)用相應(yīng)的方法讓手機(jī)振動(dòng)。
繼續(xù)沿著方法調(diào)用分析:
#ContextImpl.java
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
#SystemServiceRegistry
public static Object getSystemService(ContextImpl ctx, String name) {
//從map 里獲取鍵值
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
這個(gè)map從哪里來呢著洼?在SystemServiceRegistry 靜態(tài)代碼塊里注冊(cè)的:
#SystemServiceRegistry.java
static {
...
registerService(Context.VIBRATOR_SERVICE, Vibrator.class,
new CachedServiceFetcher<Vibrator>() {
@Override
public Vibrator createService(ContextImpl ctx) {
return new SystemVibrator(ctx);
}});
...
}
可以看出返回了SystemVibrator樟遣,它是Vibrator(抽象類)的子類。
Vibrator.vibrate(xx)最終調(diào)用了如下方法:
#SystemVibrator.java
private final IVibratorService mService;
public SystemVibrator(Context context) {
super(context);
//獲取服務(wù)端提供的接口
mService = IVibratorService.Stub.asInterface(ServiceManager.getService("vibrator"));
}
public void vibrate(int uid, String opPkg, VibrationEffect effect,
String reason, AudioAttributes attributes) {
if (mService == null) {
Log.w(TAG, "Failed to vibrate; no vibrator service.");
return;
}
try {
//真正調(diào)用之處
mService.vibrate(uid, opPkg, effect, usageForAttributes(attributes), reason, mToken);
} catch (RemoteException e) {
Log.w(TAG, "Failed to vibrate.", e);
}
}
了解過AIDL的同學(xué)都會(huì)知道身笤,熟悉的套路:
- mService 為服務(wù)端提供的接口豹悬,客戶端調(diào)用其提供的方法即可實(shí)現(xiàn)相應(yīng)的功能。
- 客戶端為當(dāng)前待使用振動(dòng)服務(wù)的App進(jìn)程液荸,服務(wù)端為提供振動(dòng)服務(wù)的進(jìn)程瞻佛。
獲取IBinder
振動(dòng)服務(wù)的IBinder是通過:
ServiceManager.getService("vibrator")
獲取的。
#ServiceManager.java
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
//獲取IBinder
return Binder.allowBlocking(rawGetService(name));
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
//獲取服務(wù)端的ServiceManager
sServiceManager = ServiceManagerNative
.asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
return sServiceManager;
}
private static IBinder rawGetService(String name) throws RemoteException {
...
final IBinder binder = getIServiceManager().getService(name);
...
return binder;
}
又是熟悉的套路娇钱,IServiceManager 為ServiceManager服務(wù)端提供的接口涤久,通過該接口獲取振動(dòng)服務(wù)的IBinder引用涡尘。
其中BinderInternal.getContextObject()) 獲取ServiceManager的IBinder。
此處需要說明一下:
Client 需要從ServiceManager獲取震動(dòng)服務(wù)的IBinder响迂,而Client本身需要和ServiceManager通信考抄,要通信那么得有IBinder吧。BinderInternal.getContextObject())就是為了獲取ServiceManager的IBinder蔗彤,該方法從Binder驅(qū)動(dòng)獲取了IBinder引用川梅。
注冊(cè)服務(wù)
ServiceManager是如何找到振動(dòng)服務(wù)的呢?
Android 系統(tǒng)啟動(dòng)后然遏,會(huì)開啟system_server進(jìn)程贫途,該進(jìn)程里開啟了很多系統(tǒng)服務(wù),包括AMS待侵、WMS丢早、振動(dòng)服務(wù)等。
#SystemServer.java
private void startOtherServices() {
...
VibratorService vibrator = null;
...
vibrator = new VibratorService(context);
//向ServiceManager注冊(cè)振動(dòng)服務(wù)
ServiceManager.addService("vibrator", vibrator);
...
}
繼續(xù)來看addService(xx):
#ServiceManager.java
public static void addService(String name, IBinder service) {
addService(name, service, false, IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT);
}
public static void addService(String name, IBinder service, boolean allowIsolated,
int dumpPriority) {
try {
//IPC 調(diào)用注冊(cè)服務(wù)
getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
} catch (RemoteException e) {
Log.e(TAG, "error in addService", e);
}
}
調(diào)用ServiceManager接口添加服務(wù)到ServiceManager里秧倾。
小結(jié)
好了怨酝,現(xiàn)在從頭到尾再捋一下。
1那先、ServiceManager 進(jìn)程啟動(dòng)
2农猬、system_server 進(jìn)程啟動(dòng),并將各個(gè)服務(wù)(包括振動(dòng)服務(wù))添加到ServiceManager里
3售淡、客戶端從ServiceManager里獲取振動(dòng)服務(wù)
用圖表示:
其中 Client斤葱、ServiceManager、SystemServer 分別運(yùn)行于三個(gè)不同的進(jìn)程揖闸,三者之間通過Binder進(jìn)行IPC揍堕。實(shí)線為其調(diào)用目的,虛線為其調(diào)用手段汤纸。
1衩茸、SystemServer 通過IPC1 向ServiceManager注冊(cè)服務(wù)的IBinder引用
2、Client想要使用服務(wù)(如振動(dòng)服務(wù))蹲嚣,先通過IPC2 向ServiceManager獲取
3、Client拿到服務(wù)IBinder后祟牲,調(diào)用服務(wù)接口(IPC3)隙畜,使用服務(wù)提供的具體功能
為了減少多次無用IPC調(diào)用,因此Client會(huì)將拿到的各種服務(wù)緩存到數(shù)組里说贝,當(dāng)要查詢的服務(wù)已經(jīng)存在议惰,則不用進(jìn)行IPC2,直接使用IPC3乡恕。
系統(tǒng)提供的服務(wù)如AMS言询、WMS俯萎、PMS等都將IBinder封裝在xxManager(如WindowManager等)里,通過xxManager就可以進(jìn)行IPC使用具體的服務(wù)运杭。
2夫啊、獲取自定義服務(wù)
上面說了系統(tǒng)提供的服務(wù)需要注冊(cè)到ServiceManager里,以便后來者查詢使用之辆憔。那么我們自己定義的服務(wù)該如何使用呢撇眯?
Service 的綁定流程
先來看看典型的綁定流程:
服務(wù)端代碼:
IStudentServer iStudentServer = new IStudentServer.Stub() {
@Override
public void say(String world) throws RemoteException {
Log.d(TAG, "hello " + world);
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return iStudentServer.asBinder();
}
客戶端代碼:
ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//重點(diǎn)在service 類型
IStudentServer iStudentServer = IStudentServer.Stub.asInterface(service);
try {
iStudentServer.say("hello");
} catch (Exception e) {
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private void bindService() {
Intent intent = new Intent(MainActivity.this, MyService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
大致闡述上述流程:
1、Service 構(gòu)造Binder對(duì)象虱咧,并將IBinder在onBind(xx)傳遞出去
2熊榛、客戶端在綁定Service成功后會(huì)收到服務(wù)端傳遞過來的IBinder
3、通過該IBinder獲取關(guān)聯(lián)的接口操作服務(wù)端
可以看出腕巡,我們?cè)赟ervice里定義業(yè)務(wù)邏輯(Server端)玄坦,并開放了接口,通過Service的綁定功能將接IBinder傳遞給客戶端绘沉,這和獲取系統(tǒng)服務(wù)的邏輯是一樣的煎楣,核心都是IBinder的傳遞,接下來從源頭入手查看IBinder的傳遞梆砸。
從Context.bindService(xx)開始
由于涉及到的代碼較多转质,此處就不貼完整源碼了,重點(diǎn)關(guān)注關(guān)鍵之處和IPC 流程帖世,多用圖示之休蟹。
綁定流程圖:
大致解釋上圖元素構(gòu)成:
最頂上方框?yàn)轭惷?br>
紅色表示它們都運(yùn)行在同一進(jìn)程,暫且稱之為客戶端進(jìn)程日矫。
綠色表示它們都運(yùn)行在同一進(jìn)程赂弓,暫且稱之為系統(tǒng)服務(wù)進(jìn)程。
黃色表示它們都運(yùn)行在同一進(jìn)程哪轿,暫且稱之為服務(wù)端進(jìn)程盈魁。
紅色箭頭表示該調(diào)用為進(jìn)程間調(diào)用,用IPC 表示之窃诉。其余為本進(jìn)程內(nèi)的對(duì)象調(diào)用杨耙。
分別來分析重點(diǎn)1、2飘痛、3珊膜。
重點(diǎn)1
客戶端發(fā)起綁定操作,傳入ServiceConnection 引用宣脉,該引用在ContextImpl.bindServiceCommon(xx)里被封裝在ServiceDispatcher里车柠,而ServiceDispatcher又持有InnerConnection引用,InnerConnection 繼承自IServiceConnection.Stub 可以跨進(jìn)程調(diào)用。
也就是說竹祷,客戶端進(jìn)程留下了一個(gè)"樁"谈跛,等待別的進(jìn)程調(diào)用。
重點(diǎn)2
AMS 收到客戶端的綁定指令后塑陵,發(fā)起綁定操作感憾,通過IPC 調(diào)用服務(wù)端接口。
最終調(diào)用到服務(wù)端的onBind(xx)方法猿妈,該方法里返回服務(wù)端的IBinder引用吹菱。
重點(diǎn)3
服務(wù)端返回IBinder引用后,委托AMS 發(fā)布這個(gè)IBinder彭则,IBinder找到對(duì)應(yīng)的客戶端進(jìn)程鳍刷。而在重點(diǎn)1里客戶端已經(jīng)留下了"樁",此時(shí)AMS 順勢(shì)找到這個(gè)"樁"直接調(diào)用ServiceConnection的onServiceConnected(xx)俯抖,就能將IBinder傳遞給客戶端输瓜。
可能比較繞,我們從進(jìn)程的角度再簡(jiǎn)化一下:
可以看出芬萍,以上發(fā)生了四次IPC 操作(當(dāng)然里面還涉及到其它的IPC尤揣,此處忽略)。IBinder傳遞要經(jīng)過兩次IPC柬祠。
IBinder 傳遞
上面分析了通過綁定流程返回服務(wù)端的IBinder引用北戏。
但是運(yùn)行的過程中卻發(fā)現(xiàn)問題:
服務(wù)端返回的IBinder是:IStudentServer
而客戶端收到的IBinder是:BinderProxy
這個(gè)是怎么回事呢?
既然IBinder是通過進(jìn)程間傳遞的漫蛔,看看其是否是支持序列化嗜愈。
public interface IBinder {
...
}
public class Binder implements android.os.IBinder {
...
}
發(fā)現(xiàn)它們都沒有實(shí)現(xiàn)Parcelable 接口。它是怎么支持序列化的呢莽龟?
那只能從Parcel本身分析了蠕嫁。
Parcel 除了支持
readInt()
writeInt()
...
等基本數(shù)據(jù)類型外,還支持
public final IBinder readStrongBinder() {
return nativeReadStrongBinder(mNativePtr);
}
public final void writeStrongBinder(IBinder val) {
nativeWriteStrongBinder(mNativePtr, val);
}
顧名思義毯盈,應(yīng)該是專門讀寫IBinder的方法剃毒,也就是說雖然沒有實(shí)現(xiàn)Parcelable,但是Parcel 內(nèi)置支持了IBinder搂赋。
接著繼續(xù)查看其native方法赘阀,看看有何奧妙之處。
static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
return javaObjectForIBinder(env, parcel->readStrongBinder());
}
return NULL;
}
static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{
Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
if (parcel != NULL) {
const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));
if (err != NO_ERROR) {
signalExceptionForError(env, clazz, err);
}
}
}
注:方法在/frameworks/core/jni/android_os_Parcel.cpp
先分析寫入IBinder的情況:
parcel->writeStrongBinder(xx) 調(diào)用了Parcel.cpp里的writeStrongBinder(xx)進(jìn)而調(diào)用flatten_binder(xx)函數(shù)
status_t flatten_binder(const sp<ProcessState>& /*proc*/,
const sp<IBinder>& binder, Parcel* out)
{
flat_binder_object obj;
...
if (binder != NULL) {
IBinder *local = binder->localBinder();
if (!local) {
//本地引用不存在
BpBinder *proxy = binder->remoteBinder();
if (proxy == NULL) {
ALOGE("null proxy");
}
const int32_t handle = proxy ? proxy->handle() : 0;
//type 標(biāo)記為非本地Binder
obj.hdr.type = BINDER_TYPE_HANDLE;
obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
obj.handle = handle;
obj.cookie = 0;
} else {
//IBinder為本地的Binder引用脑奠,也就是和Server處在同一進(jìn)程
//type 標(biāo)記為本地Binder
obj.hdr.type = BINDER_TYPE_BINDER;
obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
obj.cookie = reinterpret_cast<uintptr_t>(local);
}
} else {
...
}
return finish_flatten_binder(binder, obj, out);
}
可以看出基公,根據(jù)傳入的IBinder是不是本地Binder然后打上type標(biāo)記。
再來看看讀取IBinder的情況
parcel->readStrongBinder()里最終調(diào)用了:
status_t unflatten_binder(const sp<ProcessState>& proc,
const Parcel& in, sp<IBinder>* out)
{
const flat_binder_object* flat = in.readObject(false);
if (flat) {
//根據(jù)Type 標(biāo)記判斷
switch (flat->hdr.type) {
case BINDER_TYPE_BINDER:
//本地引用
*out = reinterpret_cast<IBinder*>(flat->cookie);
return finish_unflatten_binder(NULL, *flat, in);
case BINDER_TYPE_HANDLE:
//非本地引用捺信,獲取代理對(duì)象
*out = proc->getStrongProxyForHandle(flat->handle);
return finish_unflatten_binder(
static_cast<BpBinder*>(out->get()), *flat, in);
}
}
return BAD_TYPE;
}
由此可見酌媒,如果是Server端的IBinder與Client端不在同一進(jìn)程,則會(huì)轉(zhuǎn)換為Proxy對(duì)象迄靠,最終體現(xiàn)在Java層的就是BinderProxy類型秒咨。
注:函數(shù)在/frameworks/native/libs/binder/Parcel.cpp
綜上所述,IBinder跨進(jìn)程傳遞時(shí):
- 如果客戶端掌挚、服務(wù)端同一進(jìn)程雨席,則服務(wù)端回傳的IBinder為當(dāng)前引用
- 如果客戶端、服務(wù)端處在不同進(jìn)程吠式,則服務(wù)端回傳的IBinder為BinderProxy
3陡厘、兩者區(qū)別與聯(lián)系
獲取系統(tǒng)服務(wù)
系統(tǒng)服務(wù)會(huì)往ServiceManager注冊(cè),ServiceManager運(yùn)行在單獨(dú)的進(jìn)程里特占,客戶端進(jìn)程需要先向ServiceManager里請(qǐng)求IBinder糙置,再使用IBinder獲取關(guān)聯(lián)接口進(jìn)而使用系統(tǒng)服務(wù)。
獲取自己定義的服務(wù)
服務(wù)端進(jìn)程開啟后是目,暴露出IBinder谤饭。客戶端通過綁定服務(wù)端進(jìn)程里的Service懊纳,將IBinder跨進(jìn)程傳遞至客戶端揉抵,客戶端再使用IBinder獲取關(guān)聯(lián)接口進(jìn)而使用自定義服務(wù)。此過程沒有借助于ServiceManager嗤疯。
不論是哪種方式冤今,核心都需要獲得IBinder,IBinder的獲取需要IPC茂缚。
至此戏罢,Android IPC 系列文章已經(jīng)分析完畢
本文基于Android 10.0。
您若喜歡阱佛,請(qǐng)點(diǎn)贊帖汞、關(guān)注,您的鼓勵(lì)是我前進(jìn)的動(dòng)力
持續(xù)更新中凑术,和我一起步步為營(yíng)系統(tǒng)翩蘸、深入學(xué)習(xí)Android
1、Android各種Context的前世今生
2淮逊、Android DecorView 必知必會(huì)
3催首、Window/WindowManager 不可不知之事
4、View Measure/Layout/Draw 真明白了
5泄鹏、Android事件分發(fā)全套服務(wù)
6郎任、Android invalidate/postInvalidate/requestLayout 徹底厘清
7、Android Window 如何確定大小/onMeasure()多次執(zhí)行原因
8备籽、Android事件驅(qū)動(dòng)Handler-Message-Looper解析
9舶治、Android 鍵盤一招搞定
10分井、Android 各種坐標(biāo)徹底明了
11、Android Activity/Window/View 的background
12霉猛、Android Activity創(chuàng)建到View的顯示過
13尺锚、Android IPC 系列
14、Android 存儲(chǔ)系列
15惜浅、Java 并發(fā)系列不再疑惑
16瘫辩、Java 線程池系列