前言
本故事純屬虛構,如有不通順的邏輯請輕噴,??
《犬夜叉2021》
第一章:我還能找到你嗎,阿籬
犬夜叉和奈落大決戰之后,四魂之玉、食骨之井消失,誰也不知道去了哪,而犬夜叉和阿籬再次被分割到兩個世界,
于是犬夜叉拜托一位研究世界宇宙的法師——積木,來幫助他找到阿籬,
時間轉眼來到了2021年,積木終于發現了這個世界的秘密,,
其實我們所在的整個宇宙叫做Android宇宙,犬夜叉和阿籬所處的兩個世界其實是兩個行程,兩個世界可以通過食骨之井相連接,
所以想讓犬夜叉重新聯系到阿籬,必須再找到當年的食骨之井,
第二章:食骨之井改名Binder井?
“犬夜叉,我終于找到了”
“找到什么了?是阿籬嗎?阿籬找到了????”
“沒有,不過我找到了關鍵的東西——食骨之井”
“在哪,快帶我去”
于是,積木法師帶著犬夜叉來到一間屋子里:
這間屋子門面上寫著《內核屋》三個大字,犬夜叉一個箭步飛了進去,在里面果然找到了當年那個食骨之井,但是又有點不一樣,因為它被改了名,旁邊一個破碎的板子上寫著——Binder井,板子上還同時刻有使用說明:
Binder井
這口井聯系這兩個世界,你看到的也許不是真實的,請慎用!
如需使用,請找到當年遺落的四魂之玉,現在它叫SM之玉(ServiceManager),
找到SM之玉,心里默念你想聯系的那個世界那個人,如果她在那個世界的SM之玉碎片中留下了地址,那么你就能找到她,
“積木法師,你知道SM之玉嗎,哪里可以找到它”,犬夜叉問到,
第三章:四魂之玉——ServiceManager
“說到SM之玉,還要從宇宙的起源說起,Android宇宙創建初期,誕生了第一個有人的世界(用戶行程),叫做Init世界,而SM之玉就是由這個世界創建的,
SM之玉創建后,啟動了Binder井,成為了他的守護神,
但是它的真身存在于單獨的世界中,無法獲得,為了讓人們能夠使用到它,它特意在每個世界都留下了自己的碎片(代理),”
“在哪在哪,快告訴我”,
“第0街道(句柄值固定為0)”,積木法師指了一個方向說到,
第四章:阿籬,我想你了
犬夜叉急忙去第0街道找到了SM之玉的碎片,然后回到Binder井旁邊,心里默念道:
“
SM之玉,
求求你幫我找到阿籬吧,
”
忽然,Binder井刮出一陣狂風,一個虛影出現在了犬夜叉的面前,
是阿籬~
“阿籬,你能聽到我說話嗎?”
“犬夜叉,我能聽到,沒想到還能看到你”,阿籬的虛影說到,
“我想你了,阿籬...”
故事End
故事結束了,
再幫大家理一下故事梗概,其實也就是Binder的作業流程:
- 阿籬(服務端) 為了讓犬夜叉(客戶端) 找到她,在四魂之玉(ServiceManager) 上留下了他們世界(行程) 的地址,
- 犬夜叉在第0街道(句柄為0) 找到了四魂之玉碎片(ServiceManager代理),
- 通過四魂之玉碎片,犬夜叉看到了阿籬的虛影(服務端代理),并通過虛影告訴了阿籬,想她了(通信),
當然,故事畢竟是故事,并不能完全說清楚,
所以下面就完整看看Binder的作業流程和原理~
代碼實作犬夜叉的需求
首先,我們使用AIDL來實作剛才故事中的場景——讓犬夜叉和阿籬兩個不同行程的人說上話:
//IMsgManager.aidl
interface IMsgManager {
String getMsg();
void tell(in String msg);
}
//阿籬
public class AliWorldService extends Service {
private static final String TAG = "lz1";
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private Binder mBinder = new IMsgManager.Stub() {
@Override
public String getMsg() throws RemoteException {
String tellMsg="犬夜叉...是我";
Log.e(TAG, "阿籬:" + tellMsg);
return tellMsg;
}
@Override
public void tell(String msg) throws RemoteException {
Log.e(TAG, "我是阿籬,我收到了你說的:" + msg);
}
};
}
<service
android:name=".binder.AliWorldService"
android:process=":aliworld">
</service>
//犬夜叉
public class QycWorldActivity extends Activity {
private static final String TAG = "lz1";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_qyc);
Intent i = new Intent(this, AliWorldService.class);
bindService(i, mConnection, Context.BIND_AUTO_CREATE);
}
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IMsgManager msgManager = IMsgManager.Stub.asInterface(service);
try {
String tellMsg="阿籬,是你嗎";
Log.e(TAG, "犬夜叉:" + tellMsg);
msgManager.tell(tellMsg);
String msg = msgManager.getMsg();
Log.e(TAG, "我是犬夜叉,我收到了你說的:" + msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mConnection);
}
}
運行,列印結果:
E/lz1: 犬夜叉:阿籬,是你嗎
E/lz1: 我是阿籬,我收到了你說的:阿籬,是你嗎
E/lz1: 阿籬:犬夜叉...是我
E/lz1: 我是犬夜叉,我收到了你說的:犬夜叉...是我
AIDL原理
代碼比較簡單,服務器端新建一個Binder物件并傳到onBind方法中,客戶端bindservice之后,獲取到服務端的代理介面,就可以進行方法的呼叫了,
AIDL其實只是一個幫助我們實作行程間通信的工具,它會根據我們寫的AIDL檔案代碼,生成相應的java介面代碼,其內部也是通過Binder實作的,
我們可以通過build——generated——aidl_source_output_dir——debug——out檔案路徑找到AIDL為我們生成的介面類,代碼如下:
public interface IMsgManager extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements com.example.studynote.binder.IMsgManager {
//1
private static final java.lang.String DESCRIPTOR = "com.example.studynote.binder.IMsgManager";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
//2
public static com.example.studynote.binder.IMsgManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.studynote.binder.IMsgManager))) {
return ((com.example.studynote.binder.IMsgManager) iin);
}
return new com.example.studynote.binder.IMsgManager.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
//4
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getMsg: {
data.enforceInterface(descriptor);
java.lang.String _result = this.getMsg();
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_tell: {
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
this.tell(_arg0);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.example.studynote.binder.IMsgManager {
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;
}
//3
@Override
public java.lang.String getMsg() throws android.os.RemoteException {
android.os.Parcel _data = https://www.cnblogs.com/jimuzz/p/android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getMsg, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void tell(java.lang.String msg) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(msg);
mRemote.transact(Stub.TRANSACTION_tell, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getMsg = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_tell = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.lang.String getMsg() throws android.os.RemoteException;
public void tell(java.lang.String msg) throws android.os.RemoteException;
}
代碼比較長,我們依次來分析下:
DESCRIPTOR,Binder的唯一標示,
在Stub類的構造方法中,就是通過attachInterface方法將當前的Binder和這個唯一標示進行了系結,
asInterface(),將服務端的Binder物件轉換成客戶端所需的介面型別物件,
這個方法是客戶端呼叫的,在這個方法中,會通過queryLocalInterface(DESCRIPTOR)方法,傳入唯一標示,來獲取對應的Binder,
如果是服務端和客戶端在同一個行程,那么就會回傳服務端的Binder物件,也就是Stub物件本身,然后就直接呼叫物件的方法了,
如果在不同行程,也就是我們一般的跨行程情況,就會回傳封裝后的Stub.Proxy這個代理物件,
Proxy.getMsg/tell
接著就看看代理類里面的方法,也就是我們在客戶端(Activity)中實際呼叫的方法,
這其中有兩個比較重要的物件 :_data物件和 _reply物件,都是Parcel型別的,這里會對資料進行一個序列化操作,這樣才能進行跨行程傳輸,
如果方法傳有引數,就會把引數寫到_data物件,然后呼叫transact方法發起遠程呼叫請求(RPC),同時當前執行緒掛起,等待服務端執行完請求,
mRemote.transact(Stub.TRANSACTION_getMsg, _data, _reply, 0);
可以看到,傳入了一個int型別的code——TRANSACTION_getMsg,這個code其實就是為了要確定呼叫的是哪個方法,
等請求結束后,當前執行緒繼續,會從_reply物件中取出回傳結果,
整個IPC流程就結束了,那服務器到底是在哪里進行任務執行的呢?繼續看onTransact方法,
onTransact
onTransact方法就是服務端要做的事了,運行在服務端的Binder執行緒池中,
當客戶端發起遠程呼叫請求后,會通過系統底層封裝,其實也就是內核層的Binder驅動,然后交給服務端的onTransact方法,
在該方法中,首先通過code知曉是哪個方法,然后就執行目標方法,并將序列化結果寫到reply中,RPC程序結束,交給客戶端處理,
case TRANSACTION_getMsg: {
data.enforceInterface(descriptor);
java.lang.String _result = this.getMsg();
reply.writeNoException();
reply.writeString(_result);
return true;
}
最后畫張圖總結下AIDL整個流程:
Binder
經過了上述AIDL的例子,大家是不是對Binder又進一步了解了呢?
在java層面,其實Binder就是一個實作了IBinder介面的類,
真正跨行程的部分還是在客戶端發起遠程呼叫請求之后,系統底層封裝好,交給服務端的時候,而這個系統底層封裝,其實就是發生在Linux內核中,
而在內核中完成這個通信關鍵功能的還是Binder,這次不是Binder類了,而是Binder驅動,
驅動你可以理解為一種硬體介面,可以幫助作業系統來控制硬體設備,
Binder驅動被添加運行到Linux內核空間,這樣,兩個不同行程就可以通過訪問內核空間來完成資料交換:把資料傳給Binder驅動,然后處理好再交給對方行程,完成跨行程通信,
而剛才通過AIDL的例子我們可以知道,客戶端在請求服務端通信的時候,并不是直接和服務端的某個物件聯系,而是用到了服務端的一個代理物件,通過對這個代理物件操作,然后代理類會把方法對應的code、傳輸的序列化資料、需要回傳的序列化資料交給底層,也就是Binder驅動,(這也解釋了為什么犬夜叉看到的是阿籬的虛影,而不是真身??)
然后Binder驅動把對應的資料交給服務器端,等結果計算好之后,再由Binder驅動把資料回傳給客戶端,
ServiceManager
到這里,可能就會有朋友會發現有點不對勁,剛才不是說還有個ServiceManager嗎?這里AIDL通信中咋沒有呢,漏了啊??
ServiceManager其實是為了管理系統服務而設定的一種機制,每個服務注冊在ServiceManager中,由ServiceManager統一管理,我們可以通過服務名在ServiceManager中查詢對應的服務代理,從而完成呼叫系統服務的功能,所以ServiceManager有點類似于DNS,可以把服務名稱和具體的服務記錄在案,供客戶端來查找,
在我們這個AIDL的案例中,能直接獲取到服務端的Service,也就直接能獲取到服務端的代理類IMsgManager,所以就無需通過ServiceManager這一層來尋找服務了,
而且ServiceManager本身也運行在一個單獨的行程,所以它本身也是一個服務端,客戶端其實是先通過跨行程獲取到ServiceManager的代理物件,然后通過ServiceManager代理物件再去找到對應的服務,
而ServiceManager就像我們剛才AIDL中的Service一樣,是可以直接找到的,他的句柄永遠是0,是一個“眾所周知”的句柄,所以每個APP程式都可以通過binder機制在自己的行程空間中創建一個ServiceManager代理物件,
(這也解釋了為什么四魂之玉在另外一個世界,其實就在另外一個行程,我們只能通過句柄值為0找到四魂之玉的碎片,其實也就是代理??)
所以通過ServiceManager查找系統服務并呼叫方法的程序是進行了兩次跨行程通信,
APP行程——>ServiceManager行程——>系統服務行程(比如AactivityManagerService)
下面我們就拿ActivityManagerService舉例看看怎么通過ServeiceManager獲取到系統服務,
系統服務通信舉例
熟悉APP啟動流程的朋友都知道,startActivityForResult方法會轉到mInstrumentation.execStartActivity方法中,而這里獲取AMS服務的程序就用到了Binder機制:
//mInstrumentation.execStartActivity
int result = ActivityManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
主要看這一句:ServiceManager.getService(Context.ACTIVITY_SERVICE)
這不就是ServiceManager嗎,按照我們之前理解的那樣,這里傳入了一個name——Context.ACTIVITY_SERVICE,然后就能獲取到對應服務的代理類,也就是IBinder物件,這里也就是對應的AMS的代理物件——IActivityManager,
然后就可以對AMS進行一系列操作了,
這里的AMS服務其實對應的服務端,而我們呼叫的一方也就是APP本身的行程,就作為客戶端,
剩下的問題就是,AMS是什么時候注冊到ServiceManager的呢?答案在SystemServer中,之前的文章中說過,AMS的啟動是在SystemServer中完成的,其實啟動的同時也完成了在ServiceManager中的注冊,這里貼下AMS的啟動和注冊服務代碼,不熟悉的朋友可以翻翻SystemServer的原始碼或者我之前的文章,
//SystemServer.java
private void startBootstrapServices() {
//...
//啟動AMS
mActivityManagerService = mSystemServiceManager.startService(
ActivityManagerService.Lifecycle.class).getService();
mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
mActivityManagerService.setInstaller(installer);
//為系統行程設定應用程式實體并啟動,
mActivityManagerService.setSystemProcess();
}
public void setSystemProcess() {
try {
ServiceManager.addService(Context.ACTIVITY_SERVICE, this, /* allowIsolated= */ true,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
}
}
到此,完整的Binder流程也介紹完了,再補一張Binder機制的流程圖:
總結
所以Binder到底是什么呢?我想你心里已經有了答案,這里借用《Android開發藝術探索》書中的內容總結下,希望大家好好回味下~
直觀的說,Binder是一個類,實作了IBinder介面,
從IPC(行程間通信)角度來說,Binder是Android中一種跨行程通信方式,
還可以理解為一種虛擬的物理設備,它的設備驅動是/dev/binder,
從Android FrameWork角度來說,Binder是ServiceManager連接各種Manager(ActivityManager,WindowManager等等)和回應ManagerService的橋梁,
從Android應用層來說,Binder是客戶端和服務端進行通信的媒介,
Android體系架構
思維導圖鏈接
參考
《Android開發藝術探索》
Binder學習指南
Android行程間通信(IPC)機制Binder簡要介紹和學習計劃
Android系統的Binder機制之一——Service Manager
拜拜
有一起學習的小伙伴可以關注下?? 我的公眾號——碼上積木,每天剖析一個知識點,我們一起積累知識,形成完整體系架構,公眾號回復111可獲得面試題《思考與解答》以往期刊,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/248964.html
標籤:Android

