編輯:關於Android編程
最近要實現Android中免注冊Activity就可以運行的問題,那麼結果是搞定了,就是可以不用在AndroidManifest.xml中聲明這個Activity即可運行,主要是通過騙取系統,偷龍轉鳳技術的,這個知識點後面會詳細講解的,因為在研究了這個問題過程中遇到了很多知識點,當然最重要也是最根本的就是Android中的Binder機制和遠程服務調用機制,而關於Binder機制的話,在Android中算是一個非常大的系統架構模塊了,光這篇文章是肯定不能講解到全部的,而且本人也不是非常的熟悉Binder機制,只是發表個人的理解,並且會用最簡單的語言講解最核心的知識,因為現在有很多知識都在介紹Binder機制,但是大部分說的都太抽象了大部分都看不懂。
接下來幾篇主要是介紹關於Android中的應用啟動流程,通過Hook機制攔截Activity的啟動流程,達到我們想要的功能,同時可以實現Activity無需在AndroidManifest.xml中聲明即可運行的效果。
我們如果做過多進程之間通信的話都了解遠程服務的使用了,使用也是非常簡單的,下面來看一下如何定義自己的遠程服務:
1、定義一個AIDL文件
類似於定義接口類型,這個AIDL文件將在本地和遠端都要使用到
2、定義遠端服務
在遠程服務中的onBind方法,實現AIDL接口的具體方法,並且返回Binder對象
3、本地創建連接對象
本地創建一個服務連接對象,實現ServiceConnection接口,在連接成功之後,會得到一個遠端傳遞過來的Binder對象,就是上面的遠端服務onBind方法返回的,得到Binder對象之後在進行轉化就可以得到AIDL對象,然後即可調用方法
4、連接服務
連接服務也是比較簡單的,這時候把上面的連接對象傳遞進去即可
到這裡我們就看到完成了本地端和遠端的通信了,如果把DemoService遠端服務定義在另外一個進程中,那麼這裡就可以實現多進程通信了。看到上面的步驟很簡單,但是有一個核心的地方就是Demo.Stub類,這個類起著重要的作用,下面來分析一下他的實現,每次定義了AIDL接口文件之後,編譯一下就會在gen目錄中產生對應的java文件了:
這裡的實現稍微有點復雜了,但是這些內容其實都是編譯工具幫我們實現的,而這個實現都是有一定規則的:
1、AIDL接口必須實現IInterface接口
關於IInterface接口的實現也很簡單:
這裡可以把當前的AIDL對象轉化成一個IBinder類型對象
2、AIDL接口中肯定有一個靜態實現類Stub
這個類必須實現Binder類,以及本身的AIDL接口類型。那麼這個類就具備了Binder類中的四個功能:
1》可以將Binder對象轉化成AIDL對象,調用asInterface方法,可以看到這個方法其實和上面的asBinder方法對立的
2》通信方法onTransact實現,這個方法是最核心的用於通信之間的邏輯實現
3》通過queryLocalInterface方法可以根據類的描述符(字符串可以唯一標識這個遠端服務的名稱即可)獲取到對應的AIDL對象(其實是IInterface類型的)
4》在構造方法中必須調用Binder中的attachInterface方法把當前服務對象和描述符進行關聯
上面看完了Stub類之後,發現他其實是遠端服務Binder對象的一個中間者,用來和客戶端進行交互的,下面再來看一下Proxy類:
這個代理對象其實就是遠端傳遞過來Binder對象的一個代理,他是客戶端這邊用戶和服務端交互的中間者。我們在前面的Stub類中可以看到:
這裡會把遠端傳遞過來的Binder對象轉化成一個本地對象,發現先是通過queryLocalInterface方法借助服務描述符來獲取對象:
而這個mOwner和mDescriptor之間的關系就在attachInterface方法中進行初始化的,也就是在Stub類的構造方法中
那麼現在就清楚了,如果客戶端和服務端是在一個進程中,那麼其實queryLocalInterface獲取的就是Stub對象,如果不在一個進程queryLocalInterface查詢的對象肯定為null,因為不同進程有不同虛擬機,肯定查不到mOwner對象的,所以這時候其實是返回的Proxy對象了。通過上面的講解之後,發現多進程服務通信基准就是借助Binder對象,先傳遞Binder對象,然後在把Binder轉成可以使用的原生對象即可調用了,而對於Stub類和Proxy類其實就是相當於是服務端和客戶端的中間者,把一些邏輯封裝起來,這種設計也會顯得不是那麼凌亂:
Stub類是服務端的中間者,一般是實現了AIDL接口類型和繼承了Binder類,具備將Binder對象轉化成原生對象的能力
Proxy類是客戶端的中間者,一般是實現了AIDL接口類型
這種圖就夠形象了,所以後面會看到系統中的一些服務使用的時候其實也是跨進程使用,比如下面來看一下著名的PackageManager,IPackageManager,PackageManagerService體系:
這個就是IPackageManager.aidl文件,因為我們還沒有編譯源碼,所以這裡可能需要AIDL工具單獨編譯才能看到IPackageManager.java了:
這裡看到了熟悉的遠端服務中間者Stub和本地端的中間者Proxy類了,而這兩個類的規則都和上面一樣的。
下面來看一下遠端服務實現代碼PackageManagerService.java:
實現了上面的的Stub類功能。
下面我們再走一遍獲取PackageManager的流程:
而這個getPackageManager方法是在ContextImpl.java中實現的:
看到了吧,這裡先從ServiceManager的getService中獲取到一個PackageManager遠端的IBinder對象,然後在使用Stub的asInterface方法進行轉化成本地的PackageManager對象,其實就是那個Proxy對象。然後就可以通過PackageManager來調用方法和遠端的PackageManagerService服務進行通信了。
通過上面的PackageManager案例可以分析,我們在使用系統中的服務的時候的流程都是如此:
每個應用在使用系統服務的時候,都會走這麼幾步:
1、調用getSystemService(String serviceName)方法獲取服務對象
2、而getSystemService一般都是在ContextImpl類中實現的,其實是調用了ServiceManager的getService方法
3、調用ServiceManager的getService方法獲取遠端服務的IBinder對象
4、有了遠端服務的IBinder對象之後,在使用遠端服務的中間者類Stub進行轉化對象asInterface方法
5、因為系統中的服務獲取都是肯定是跨進程的,遠端服務都是在system_server進程中的,所以asInterface方法中返回的是Proxy代理對象,也就是本地端的中間者。
6、最後返回的對象其實就是這個Proxy對象,而這個對象內部使用了靜態代理方式,內部有一個來自遠端的mRemote變量即IBinder對象。然後直接調用方法其實就是調用mRemote的transact方法進行通信了。
所以在這個過程中可以看到有兩個對象很重要,一個是ServiceManager,一個是IBinder對象。下面再來一一介紹
那麼現在又有幾個問題了,這個ServiceManager是干嘛的?如何能夠通過服務描述符就可以獲取到對應服務的IBinder對象?這個就要看一下ServiceManager的功能了:
首先看看他獲取遠端服務的IBinder對象方法,他本身會維護一個IBinder緩存池,也是為了效率高考慮,對於一個應用頻繁的使用一些服務的話效率就會高很多。
然後最核心的獲取服務的方法是getIServiceManager方法:
這個方法看到了,又是一個遠程通信獲取Binder對象,而這次是IServiceManager對象了:
看到這裡的ServiceManager也是通過遠端服務獲取到他的IBinder對象,然後在轉化成本地對象進行使用的。那麼剛剛看到系統的服務都是通過ServiceManager管理獲取的,而現在ServiceManager本身是怎麼獲取到的IBinder對象的呢?這個就要從系統啟動的時機看了,眾所周知系統啟動的時候是根據init.rc文件進行操作的:
這裡會啟動一個servicemanager服務,那麼就要去service_manager.c程序中的入口程序看了:
這個入口其實包含了Binder機制的重要信息,而主要就是三件事:
1、打開底層的Binder驅動程序,這個後面介紹Binder機制在介紹
2、通過向binder程序發送命令:BINDER_SET_CONTEXT_MGR,告訴binder程序,我要成為大管家
3、進入循環監聽上層應用的服務請求處理,所以這裡可以看到其實ServiceManager是一個守護進程在後台默默監聽
在第二步中成為大管家的代碼深入看一看:
其實這裡的邏輯也是比較簡單的,首先創建一個屬於servicemanager的binder節點,然後在創建一個binder鏈表,而這個鏈表的作用就是存放上層中需要系統服務的所有binder對象的節點,這樣ServiceManager就可以實現了服務的增加和查詢操作了。
再來看看ServiceManager的添加服務操作:
添加服務比較復雜,首先查看這個服務有沒有注冊權限限制,不是所有的服務都能注冊的,然後在查看這個服務是不是已經被注冊過了,最後在通知binder驅動程序注冊一個服務即可。
然後在來看看ServiceManager的查找服務功能:
查找服務就比較簡單了,直接通過服務的描述符名稱遍歷binder鏈表節點即可。
1、Service Manager能集中管理系統內的所有服務,它能被施加權限控制,並不是任何進程都能注冊服務的。
2、Service Manager支持通過字符串名稱來查找對應的Service。
3、由於各種原因的影響,Server進程可能生死無常。如果有了Service Manager做統一的管理,那麼Client只要向Service Manager做查詢,就能得到Server的最新信息。
下面來看一下一些系統服務是如何進程注冊的,這裡用MediaService來進行查看吧:
系統中的MediaService服務的啟動也是在init.rc中的
查看main_mediaserver.cpp源碼的main函數:
看到了系統的MediaServer依賴很多其他服務的,而這些服務必須要注冊的,他們的注冊操作都是在各自的初始化方法中的,這裡用MediaPlayerService來看看注冊操作:
看到熟悉的代碼了把,這裡通過ServiceManager來進行服務注冊了,那麼這裡是如何獲取到ServiceManager的?
看看ProcessState.cpp的源碼:
看看getStrongProxyForHandle方法實現:
這裡看到了,會使用IPCThreadState的transact方法和底層的Binder進行通信的,然後使用一個句柄handle構造一個BpBinder對象,而BpBinder對象其實就是native層實現的Binder對象,以後只要看到Bp開頭的就是代理對象對應Java層的Proxy對象,Bn開頭的就是native對象對應Java層的Stub對象。
在上面分析servicemanager的時候知道會維護一個binder節點鏈表,那裡其實就有一個每個binder對應句柄handle,而後續進行通信的話都是通過這個句柄來標識是哪個服務的binder對象了,這樣也就在通信的時候不會發生紊亂了,而servicemanager的句柄handle就是0。還有一個知識點就是可以看到IPC通信的時候傳輸數據使用的就是Parcel類,這個類就是為了跨進程通信產生的,他有一個方法readStrongBinder,就是可以從Parcel的數據中獲取到Binder對象,這個也是在跨進程中傳遞Binder對象的核心地方。
好了,上面就通過系統的mediaserver服務來講解了系統服務的注冊流程:
到這裡就分析完了Android中的遠程服務調用機制邏輯以及ServiceManager這個服務大管家的作用:
1、首先跨進程通信的話,肯定會有兩個對象:一個是本地端的中間者Proxy對象,一個是遠程端的中間者Stub對象
2、Proxy對象通過靜態代理模式維持一個遠端傳遞過來的Binder對象,而Stub對象可以把遠端傳遞過來的Binder對象轉化成一個實際服務對象給應用使用
3、Android中在使用系統服務的時候通過getSystemService方法獲取到的其實都是Stub把遠端的Binder轉化的對象,因為系統服務都是在system_server進程中,所以肯定是跨進程獲取對象的,那麼這個Binder對象其實就是上面的Proxy對象
4、系統的服務都是在一個指定的系統進程中system_server
5、服務大管家ServiceManager在系統啟動的時候也是先獲取自生的Binder對象,然後轉化成實際操作對象,然後才可以操作系統服務的注冊和查詢功能
下面是系統一些服務的注冊流程:
上面已經介紹了遠程五福調用機制以及ServiceManager的實現原理,下面就要看看另外一個重點,也是上面提到的一個重要對象Binder,准確來說這個是Binder機制,在Android中Binder機制最復雜的一個架構系統了,它的設計很復雜,所以有很多同學在了解Binder機制的時候,總是看著看著就暈了,今天我們就直說重點,而且說得要相對明了簡單。
第一、Android中的IPC為何要采用Binder機制
Binder是Android系統進程間通信(IPC)方式之一。Linux已經擁有的進程間通信IPC手段包括(Internet Process Connection):管道(Pipe)、信號(Signal)和跟蹤(Trace)、插口(Socket)、報文隊列(Message)、共享內存(Share Memory)和信號量(Semaphore)。
Binder基於Client-Server通信模式,傳輸過程只需一次拷貝,為發送發添加UID,PID身份,既支持實名Binder也支持匿名Binder,安全性高。對Binder而言,Binder可以看成Server提供的實現某個特定服務的訪問接入點, Client通過這個‘地址’向Server發送請求來使用該服務;對Client而言,Binder可以看成是通向Server的管道入口,要想和某個Server通信首先必須建立這個管道並獲得管道入口。
第二、Android中的Binder實現原理
其實Android中的Binder通信都是通過虛擬驅動設備程序/dev/binder來實現的,我們知道一些硬件都會對應一個驅動程序,而binder驅動程序沒有對應的硬件,所以叫做虛擬驅動設備程序,其實他就是一個字符驅動設備,或者叫做miscdevice混雜設備驅動。
其實混雜驅動設備是字符設備的一種,它們共享一個主設備號(10),但次設備號不同,所有的混雜設備形成一個鏈表,對設備訪問時內核根據次設備號查找到相應的miscdevice設備。例如:觸摸屏,LED,按鍵,串口。即:為了節約主設備號,將某些設備用鏈表的形式連接在一起,最後通過查找次設備區分。這裡用主設備無法匹配出設備驅動,只能找到鏈表,再通過次設備號,才能找到設備驅動。而之前所學的,一般字符設備,通過主設備號,就能找到設備驅動了。我們可以通過命令查看/dev/binder驅動的主設備號:
第三、Android中Binder通信機制
先來看一張圖,我們可以大體的了解到了客戶端和服務端通過Binder驅動進行通信
首先不管是客戶端進程還是服務端進程都是在用戶空間的,而binder驅動是在內核空間的,通信的數據是有規定格式也叫作IPC數據,既然是一種通信機制,肯定是需要協議,數據格式等基礎結構信息的:
上面在分析了ServiceManager的啟動的時候說到了,第一步是打開驅動程序,具體打開函數在binder.c中:
在使用一個驅動之前,肯定要先打開驅動,然後把驅動程序映射到內存中,接著借助IPCTreadState.cpp和binder驅動進行通信了:
所以看到這裡IPCThreadState也是需要進入後台進行監聽的,處理來自客戶端和服務端的數據傳輸消息
最後再來看一下通信時序圖。
到這裡我們就介紹完了Binder機制了,關於Binder機制最好不要看太深,因為越深你覺得越復雜越難理解,其實你只要了解到他是一個通信工具,通信采用的是驅動操作,通過傳輸IPC數據來進行通信即可。其他的關於他的詳細數據格式和通信協議,感興趣的同學可以了解一下,但是太過復雜而且在實際中也沒多大用途,所以這裡就不介紹了。
1、理解遠程服務通信機制
通過案例先了解到本地端和服務端跨進程通信,主要就是借助Binder進行功能調用,而在這裡主要有兩個核心類,一個是Stub類,這個類是繼承了Binder類具備了將遠程傳遞的Binder對象轉化成本地實際對象asInterface方法即可,同時實現了IXXX接口,需要實現AIDL中的功能方法,還有一個類就是Proxy類,實現了IXXX接口,同時內部保留著遠端傳遞的Binder對象,然後通過這個對象調用遠端方法。這裡Stub類就是服務端的中間者,而Proxy就是本地端的中間者。
2、系統服務調用流程
通過分析了跨進程通信機制原理之後,再去看看Android系統中在使用一些服務的時候,通過getSystemService方法獲取服務對象,其實這內部就是通過跨進程獲取到了遠端服務的Binder對象,然後轉化成系統服務對象給應用調用,而這些系統服務的Binder對象在系統啟動的時候服務會自動注冊到ServiceManager中。
3、服務大管家ServiceManager
在整個遠程服務調用過程中兩個重要對象,一個是Binder對象,一個就是ServiceManager類,這個類是管理系統服務的類,他可以注冊服務,查詢服務,系統服務在系統啟動的時候會通過addService進行服務注冊,然後應用就可以通過getService進行服務查詢,而在這個過程中,底層會維護一個這些服務的binder鏈表結構,同時每個服務的binder對象都一個句柄handle,通過這個句柄來表示通信標識,這樣通信才不會紊亂。
4、底層通信核心Binder
最後分析了底層真正實現跨進程通信的機制Binder,其實是通過虛擬驅動程序/dev/binder進行通信的。一個通信機制肯定有通信協議,傳輸的數據結構,但是這裡並沒有介紹這些知識,原因是我們後面的需求並不會用到這些,其次是這些知識點太詳細介紹也不好,因為會越看越亂。
九、總結
本文介紹的東東有點多,但是如果掌握了Android中的Binder機制和遠程服務調用機制對後面攔截系統api做了鋪墊,說到結束了才告訴大家為什麼要介紹這個知識點,是因為最近在研究如何攔截系統啟動Activity的事,那麼就必須了解Activity的啟動流程,但是在這個過程中有一個對象就是ActivityManagerService,而他就和Binder以及遠程服務調用機制緊密聯系了,如果不了解Binder機制,後面工作是沒辦法進行的,好了,說到最後再來一張神圖算是總結了本文內容:
這張圖非常好的表達了Android中應用使用系統服務的一個流程,也是最好的最全的解釋了。看懂這張圖之後,那麼對Android中的binder機制和遠程服務調用機制就可以掌握了,可以進行後續的攔截操作了。
輸入要查詢的城市名稱,點擊查詢按鈕後,依次出現七天的天氣情況。出現時有動畫效果二、實現過程(一)獲取天氣預報數據1、首先搞定天氣預報數據來源的問題,提高天氣預報服務的有很
上一篇文章中我們講解了android產品研發過程中的代碼Review。通過代碼Review能夠提高產品質量,增強團隊成員之間的溝通,提高開發效率,所以良好的產品開發迭代過
游戲碰撞的大致可以分為這幾種類 1.主角與邊界的碰撞,限制主角不能走出手機屏幕外。 2.主角與物理層的碰撞,與地圖中的房子 桌子 椅子等
1、打開手機迅雷點擊切換到我的迅雷個人中心,點擊左上角[登錄],已登錄請跳過2、在手機迅雷個人中心的最下面,倒數第二項[常用設置]點擊進行設置3、繼續在常用