前言
IPC 系列文章:
建議按順序閱讀。
Android IPC 之Service 還可以這么理解
Android IPC 之Binder基礎
Android IPC 之Binder應用
Android IPC 之AIDL應用(上)
Android IPC 之AIDL應用(下)
Android IPC 之Messenger 原理及應用
Android IPC 之服務端回調
Android IPC 之獲取服務(IBinder)
Android Binder 原理換個姿勢就頓悟了(圖文版)
通過前面的文章我們知道,要進行進程通信的核心是能拿到另一個進程暴露出來的IBiner引用。本篇將重點分析獲取IBinder的方式及其原理。
通過本篇文章,你將了解到:
1、獲取系統服務
2、獲取自定義服務
3、兩者區別與聯系
本篇文章,系統服務、自定義服務里的服務并非單純是指Service,而是提供某一類功能的"服務"。
1、獲取系統服務
簡單例子
以手機振動為例:
Vibrator vibrator = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(1000);
調用Context 方法getSystemService(xx),xx表示服務名字,最終返回Vibrator。
拿到Vibrator 引用后就可以調用相應的方法讓手機振動。
繼續沿著方法調用分析:
#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;
}
這個map從哪里來呢?在SystemServiceRegistry 靜態代碼塊里注冊的:
#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)最終調用了如下方法:
#SystemVibrator.java
private final IVibratorService mService;
public SystemVibrator(Context context) {
super(context);
//獲取服務端提供的接口
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 {
//真正調用之處
mService.vibrate(uid, opPkg, effect, usageForAttributes(attributes), reason, mToken);
} catch (RemoteException e) {
Log.w(TAG, "Failed to vibrate.", e);
}
}
了解過AIDL的同學都會知道,熟悉的套路:
- mService 為服務端提供的接口,客戶端調用其提供的方法即可實現相應的功能。
- 客戶端為當前待使用振動服務的App進程,服務端為提供振動服務的進程。
獲取IBinder
振動服務的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;
}
//獲取服務端的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服務端提供的接口,通過該接口獲取振動服務的IBinder引用。
其中BinderInternal.getContextObject()) 獲取ServiceManager的IBinder。
此處需要說明一下:
Client 需要從ServiceManager獲取震動服務的IBinder,而Client本身需要和ServiceManager通信,要通信那么得有IBinder吧。BinderInternal.getContextObject())就是為了獲取ServiceManager的IBinder,該方法從Binder驅動獲取了IBinder引用。
注冊服務
ServiceManager是如何找到振動服務的呢?
Android 系統啟動后,會開啟system_server進程,該進程里開啟了很多系統服務,包括AMS、WMS、振動服務等。
#SystemServer.java
private void startOtherServices() {
...
VibratorService vibrator = null;
...
vibrator = new VibratorService(context);
//向ServiceManager注冊振動服務
ServiceManager.addService("vibrator", vibrator);
...
}
繼續來看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 調用注冊服務
getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
} catch (RemoteException e) {
Log.e(TAG, "error in addService", e);
}
}
調用ServiceManager接口添加服務到ServiceManager里。
小結
好了,現在從頭到尾再捋一下。
1、ServiceManager 進程啟動
2、system_server 進程啟動,并將各個服務(包括振動服務)添加到ServiceManager里
3、客戶端從ServiceManager里獲取振動服務
用圖表示:
其中 Client、ServiceManager、SystemServer 分別運行于三個不同的進程,三者之間通過Binder進行IPC。實線為其調用目的,虛線為其調用手段。
1、SystemServer 通過IPC1 向ServiceManager注冊服務的IBinder引用
2、Client想要使用服務(如振動服務),先通過IPC2 向ServiceManager獲取
3、Client拿到服務IBinder后,調用服務接口(IPC3),使用服務提供的具體功能
為了減少多次無用IPC調用,因此Client會將拿到的各種服務緩存到數組里,當要查詢的服務已經存在,則不用進行IPC2,直接使用IPC3。
系統提供的服務如AMS、WMS、PMS等都將IBinder封裝在xxManager(如WindowManager等)里,通過xxManager就可以進行IPC使用具體的服務。
2、獲取自定義服務
上面說了系統提供的服務需要注冊到ServiceManager里,以便后來者查詢使用之。那么我們自己定義的服務該如何使用呢?
Service 的綁定流程
先來看看典型的綁定流程:
服務端代碼:
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) {
//重點在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 構造Binder對象,并將IBinder在onBind(xx)傳遞出去
2、客戶端在綁定Service成功后會收到服務端傳遞過來的IBinder
3、通過該IBinder獲取關聯的接口操作服務端
可以看出,我們在Service里定義業務邏輯(Server端),并開放了接口,通過Service的綁定功能將接IBinder傳遞給客戶端,這和獲取系統服務的邏輯是一樣的,核心都是IBinder的傳遞,接下來從源頭入手查看IBinder的傳遞。
從Context.bindService(xx)開始
由于涉及到的代碼較多,此處就不貼完整源碼了,重點關注關鍵之處和IPC 流程,多用圖示之。
綁定流程圖:
大致解釋上圖元素構成:
最頂上方框為類名。
紅色表示它們都運行在同一進程,暫且稱之為客戶端進程。
綠色表示它們都運行在同一進程,暫且稱之為系統服務進程。
黃色表示它們都運行在同一進程,暫且稱之為服務端進程。
紅色箭頭表示該調用為進程間調用,用IPC 表示之。其余為本進程內的對象調用。
分別來分析重點1、2、3。
重點1
客戶端發起綁定操作,傳入ServiceConnection 引用,該引用在ContextImpl.bindServiceCommon(xx)里被封裝在ServiceDispatcher里,而ServiceDispatcher又持有InnerConnection引用,InnerConnection 繼承自IServiceConnection.Stub 可以跨進程調用。
也就是說,客戶端進程留下了一個"樁",等待別的進程調用。
重點2
AMS 收到客戶端的綁定指令后,發起綁定操作,通過IPC 調用服務端接口。
最終調用到服務端的onBind(xx)方法,該方法里返回服務端的IBinder引用。
重點3
服務端返回IBinder引用后,委托AMS 發布這個IBinder,IBinder找到對應的客戶端進程。而在重點1里客戶端已經留下了"樁",此時AMS 順勢找到這個"樁"直接調用ServiceConnection的onServiceConnected(xx),就能將IBinder傳遞給客戶端。
可能比較繞,我們從進程的角度再簡化一下:
可以看出,以上發生了四次IPC 操作(當然里面還涉及到其它的IPC,此處忽略)。IBinder傳遞要經過兩次IPC。
IBinder 傳遞
上面分析了通過綁定流程返回服務端的IBinder引用。
但是運行的過程中卻發現問題:
服務端返回的IBinder是:IStudentServer
而客戶端收到的IBinder是:BinderProxy
這個是怎么回事呢?
既然IBinder是通過進程間傳遞的,看看其是否是支持序列化。
public interface IBinder {
...
}
public class Binder implements android.os.IBinder {
...
}
發現它們都沒有實現Parcelable 接口。它是怎么支持序列化的呢?
那只能從Parcel本身分析了。
Parcel 除了支持
readInt()
writeInt()
...
等基本數據類型外,還支持
public final IBinder readStrongBinder() {
return nativeReadStrongBinder(mNativePtr);
}
public final void writeStrongBinder(IBinder val) {
nativeWriteStrongBinder(mNativePtr, val);
}
顧名思義,應該是專門讀寫IBinder的方法,也就是說雖然沒有實現Parcelable,但是Parcel 內置支持了IBinder。
接著繼續查看其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) 調用了Parcel.cpp里的writeStrongBinder(xx)進而調用flatten_binder(xx)函數
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 標記為非本地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處在同一進程
//type 標記為本地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);
}
可以看出,根據傳入的IBinder是不是本地Binder然后打上type標記。
再來看看讀取IBinder的情況
parcel->readStrongBinder()里最終調用了:
status_t unflatten_binder(const sp<ProcessState>& proc,
const Parcel& in, sp<IBinder>* out)
{
const flat_binder_object* flat = in.readObject(false);
if (flat) {
//根據Type 標記判斷
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:
//非本地引用,獲取代理對象
*out = proc->getStrongProxyForHandle(flat->handle);
return finish_unflatten_binder(
static_cast<BpBinder*>(out->get()), *flat, in);
}
}
return BAD_TYPE;
}
由此可見,如果是Server端的IBinder與Client端不在同一進程,則會轉換為Proxy對象,最終體現在Java層的就是BinderProxy類型。
注:函數在/frameworks/native/libs/binder/Parcel.cpp
綜上所述,IBinder跨進程傳遞時:
- 如果客戶端、服務端同一進程,則服務端回傳的IBinder為當前引用
- 如果客戶端、服務端處在不同進程,則服務端回傳的IBinder為BinderProxy
3、兩者區別與聯系
獲取系統服務
系統服務會往ServiceManager注冊,ServiceManager運行在單獨的進程里,客戶端進程需要先向ServiceManager里請求IBinder,再使用IBinder獲取關聯接口進而使用系統服務。
獲取自己定義的服務
服務端進程開啟后,暴露出IBinder。客戶端通過綁定服務端進程里的Service,將IBinder跨進程傳遞至客戶端,客戶端再使用IBinder獲取關聯接口進而使用自定義服務。此過程沒有借助于ServiceManager。
不論是哪種方式,核心都需要獲得IBinder,IBinder的獲取需要IPC。
至此,Android IPC 系列文章已經分析完畢
本文基于Android 10.0。
您若喜歡,請點贊、關注,您的鼓勵是我前進的動力
持續更新中,和我一起步步為營系統、深入學習Android
1、Android各種Context的前世今生
2、Android DecorView 必知必會
3、Window/WindowManager 不可不知之事
4、View Measure/Layout/Draw 真明白了
5、Android事件分發全套服務
6、Android invalidate/postInvalidate/requestLayout 徹底厘清
7、Android Window 如何確定大小/onMeasure()多次執行原因
8、Android事件驅動Handler-Message-Looper解析
9、Android 鍵盤一招搞定
10、Android 各種坐標徹底明了
11、Android Activity/Window/View 的background
12、Android Activity創建到View的顯示過
13、Android IPC 系列
14、Android 存儲系列
15、Java 并發系列不再疑惑
16、Java 線程池系列