編輯:關於Android編程
通過對嵌入式企鵝圈原創團隊成員degao之前發表的《Android Small插件化框架源碼分析》的學習,對Android使用的插件化技術有了初步的了解,但還是有很多需要認真學習的地方,特別是大部分知識都需要結合虛擬機和Androidframwork的原理才能慢慢理解。比如,文中作者提到了插件化框架要解決的三個核心問題:
1)插件類的加載;
2)插件資源的處理;
3)Activity注冊和生命周期問題;
其中第3點作者是這樣解釋的,“大部分插件化框架解決辦法都是采用在宿主工程裡預先注冊Activity占坑,然後通過占坑Activity將生命周期回調傳回給插件Activity的方式。這裡Small處理的比較有特色,通過替換 ActivityThread 裡的mInstrumentation,在Instrumentation的newActivty實現裡面實例化了插件Activity,通過較小改動就能完全解決生命周期回調的問題。”。
這裡面提到的ActivityThread、Instrumentation、newActivity是什麼東西呢?該如何理解“預先注冊Activity占坑”、“替換 ActivityThread 裡的mInstrumentation”這些步驟呢?帶著這些問題,我學習了Android framework中啟動Activity的源代碼,基本上搞懂了Small插件化框架是如何處理和控制Activity生命周期的了,在這裡總結一下這個學習過程。
我們App中Activity的生命周期由ActivityManagerService來管理,它決定著什麼時候該調用onCreate、onResume這些生命周期的回調函數,把Activity放在哪個棧裡,上下文之間的關系是怎樣的等等。當我們把一個插件apk用classLoader加載進宿主應用後,插件apk裡的Activity並不能被ActivityManagerService所管理,因為它只是作為一般的class被加載到宿主的進程空間中,而沒有在AndroidManifest.xml中聲明,因此通過startActivity啟動插件裡的activity時會拋出異常:
“android.content.ActivityNotFoundException: Unable to find explicit activity class… … … …
have you declared this activity in your AndroidManifest.xml?”
顯然,宿主應用自己來管理插件中activity的生命周期有很大難度,最好還是把這項工作交給ActivityManagerService。可以預先在AndroidManifest.xml中定義一些空的Activity,給它們預先起上特定的名字(ProxyActivty$1),當要啟動插件裡的activity(PluginActivity1)時,Small框架就先啟動ProxyActivty$1,這樣它的生命周期就會交由AMS去管理了,在真正構造和加載Activity的地方,再用PluginActivity1這個類的實例去替換ProxyActivty$1的實例。這樣一來,AMS中管理的是ProxyActivty$1這個activity,但實際上在宿主應用進程中實例化的卻是插件裡的PluginActivity1,它裡面的onCreate、onResume等函數會被依次調用,生命周期等問題就交由AMS去控制了,相當於欺騙了AMS和系統。
這就是解決Activity注冊和生命周期問題的方法。要理解這個過程,需要分三步走:
1)App所在的客戶端進程和AMS所在的system_server進程之間是如何交互的;
2)搞清啟動一個Activity的前前後後,特別是從客戶端進程進入到AMS的入口,和從AMS返回到客戶端進程的入口,從中找到activity是怎樣被交給AMS的,activity是在哪兒實例化的;
3)要關注上面這個過程中具體涉及到的類和方法,從中選擇合適的Hook點,用Java反射的方法來進行動態替換,自己實現偷梁換柱的動作。
一、App和AMS通過Binder交互
1. AMS的初始化
ActivityManagerService運行在system_server進程中,在SystemServer啟動後完成初始化工作,然後加入到ServiceManager中。ServiceManager管理所有的Android系統服務,客戶端應用如果要使用系統服務,調用getSystemService接口,ServiceManager就會返回給客戶端應用對應的系統服務的IBinder對象。
在SystemServer的run函數中會調用startBootstrapServices先啟動AMS、PMS這樣關鍵的系統服務,它裡面會初始化AMS。先調SystemServiceManager的startService方法,注意傳入的參數不是ActivityManagerService.class,而是它的一個靜態內部類Liftcycle。
startService會調用同名的函數,用反射構造出傳入class的實例,也就是ActivityManagerService.Liftcycle的實例並返回。
然後再調Lifecycle的getService函數得到ActivityManagerService的實例,把它賦給SystemServer中的mActivityManagerService。
在startBootstrapServices最後會調setSystemProcess把AMS加到ServiceManager中。
2. AMS在服務端的代理ActivityManagerNative
Android Binder機制在客戶端和服務端各有一個代理Proxy和Stub,它們之間通過transact和onTransact進行進程間通信,AMS在服務端的代理是ActivityManagerNative,它和客戶端的代理ActivityManagerProxy進行Binder通信,調用AMS提供的各種功能。AMS對外提供的接口是IActivityManager,它裡面包含了AMS對外提供服務的全部函數,有startActivity、startService、registerReceiver等等,ActivityManagerNative實現了IActivityManager,但它只是一個抽象類,只用於和客戶端的Binder通信,startActivity等的具體實現在它的子類ActivityManagerService中完成。
3.AMS在客戶端的代理ActivityManagerProxy
應用App使用AMS提供的功能,比如startActivity,是通過AMS在客戶端的代理ActivityManagerProxy發起的。這裡ActivityManagerNative的getDefault方法返回就是ActivityManagerProxy的實例。
getDefault返回的是ActivityManagerNative中的一個單例gDefault,它通過ServiceManager獲取到AMS的IBinder,然後再通過asInterface得到ActivityManagerProxy。
ActivityManagerNative的asInterface方法在客戶端和服務端都會被調用。在客戶端,因為IBinder在native的實現BpInterface沒有重載queryLocalInterface,所以返回它的缺省實現,缺省為null,走new ActivityManagerProxy的分支。可見,ActivityManagerProxy的asBinder返回的就是從ServiceManager中取到的AMS的IBinder對象。
4. ActivityThread在客戶端的代理ApplicationThreadNative
與服務端相似,App在客戶端進程中完成實例化Activity、調用onCreate等生命周期函數的功能,也不能被AMS直接調用,而是通過自己在客戶端的代理ApplicationThreadNative來處理。雖然代理的名字有Thread字樣,但它並不表示App所在的進程,ActivityThread才是描述客戶端進程的類。也就是說當新創建一個應用進程時,系統就會為我們新構造一個ActivityThread對象。IApplicationThread是個接口,裡面有scheduleLaunchActivity、scheduleCreateService等需要在客戶端進程中調用的方法。ApplicationThreadNative是個Binder,負責與它在服務端的代理ApplicationThreadProxy通信,同時也實現了IApplicationThread接口,但具體的實現放在了它的子類ApplicationThread中。
ApplicationThread對象被客戶端應用進程ActivityThread所持有,ActivityThread與AMS的交互實際上遵循了設計模式中的代理模式:
üIApplicationThread是抽象對象角色,提供了要使用的業務邏輯的抽象接口。
üActivityThread是目標對象角色,AMS不能直接與它交互、直接使用它的功能,都是通過它的代理類來進行。
üApplicationThread是代理對象角色,是ActivityThread的代理類,實現了具體的業務邏輯。與一般的代理模式不同,它不是直接持有ActivityThead的一個引用,而是把處理的請求發到ActivityThread內部的一個Handler上。
5.ActivityThread在服務端的代理ApplicationThreadProxy
與獲取ActivityManagerNative在客戶端的代理ActivityManagerProxy的過程類似,ApplicationThreadNative在服務端,也就是AMS所在的system_server進程中的代理,也是通過調用一個叫asInterface的函數來獲得的。
可見返回的ApplicationThreadProxy對象是IApplicationThread這個接口的類型,在ActivityManagerNative.java的onTransact中會用到這個客戶端的代理,比如在App中調用startActivity時,ActivityManagerNative在服務端的onTransact函數裡會調用AMS的startActivity,這時就通過ApplicationThreadNative.asInterface得到ApplicationThreadProxy,把它作為startActivity的參數傳給AMS。ApplicationThreadProxy在這兒的用處是作為app的代理,代表這客戶端的ActivityThread,因為AMS要處理多個客戶端進程的請求,所以通過這個代理可以得到客戶端的pid、uid、ProcessRecord等信息,還會通過它發起對客戶端的Binder調用。
6.小結
Android的命名可以幫助我們區分這些類。ActivityThread代表了應用所在的進程,xxxNative是在本進程內的Binder代理類,xxxProxy是在對方進程的Binder代理類,熟悉Binder機制的話可以想到Binder在C++層的實現也是這樣命名BnXXX和BpXXX的。
二、startActivity的過程
1.從App進入AMS的過程
調用startActivity有兩種情況,一是Context(實際上是它的具體實現ContextImpl)調startActivity,一是Activity調startActivity。最終它們都會調用Instrumentation的execStartActivity。圖15是ContextImpl調用的代碼,mMainThread就是ActivityThread,Instrumentation是它裡面的一個成員。圖16是Activity調用的代碼,startActivity先調用了startActivityForResult,在startActivityForResult中再調用了Instrumentation的execStartActivity。注意,在Context中調用startActivity時,因為上下文中沒有Activity的棧,所以要加上FLAG_ACTIVITY_NEW_TASK這個flag。Instrumentation是工具、插樁的意思,顧名思義Activity只是在執行時經過Instrumentation,以便它起到監控和測試的功能。
execStartActivity中通過調用ActivityManagerNative.getDefault()獲得AMS在客戶端的代理對象ActivityManagerProxy,它是ActivityManagerNative類中的一個靜態單例對象。然後調用它的startActivity方法,實際上是通過binder的transact發出命令,由AMS客戶端的代理ActivityManagerNative的onTransact來處理,
App請求AMS的其他函數調用過程與此相似,無一例外的都是經過ActivityManagerNative裡面的單例對象gDefault,它是從客戶端進程發起調用進入AMS的中轉站,這為我們提供了一個hook點,按照一開始所設想的那樣,原來startActivity啟動的是插件中的PluginActivity1,在這裡可以hook gDefault,這樣所有客戶端向AMS發起的調用,比如startService之類的都可以被我們截獲,然後就可以修改其中的參數,替換成含有AndroidManifest.xml中預先占坑的ProxyActivity$1的Intent了。對宿主應用來說,它啟動的是PluginActivity1,對AMS來說,它管理的activity是ProxyActivity$1。
2.在AMS中處理的過程
AMS中處理startActivity的過程比較復雜,主要涉及了ActivityManagerService、ActivityStackSupervisor、ActivityStack、PackageManagerService、WindowManagerService等幾個類。
在ActivityManagerService中,startActivity先是調用了startActivityAsUser,然後在startActivityAsUser中調用了ActivityStackSupervisor的startActivityMayWait。注意,第一個參數caller就是我們前面說過的app在服務端的代理ApplicationThreadProxy,它是一個Binder對象,實現了IApplicationThread。轉到ActivityStackSupervisor中後,又在它和ActivityStack之間來回調用了許多次,主要是進行棧和Task的管理、解析Activity的信息,准備窗口的切換之類的工作,最後回到了ActivityStackSupervisor中,調用realStartActivityLocked函數。
在realStartActivityLocked函數中,app是ProcessRecord類型,app.thread是IApplicationThread類型,也就是從客戶端的代理ApplicationThreadProxy,在這兒調用了它的scheduleLaunchActivity方法,接下來就會回到app的進程空間裡。
我們無法在AMS中去hook類和方法,因為它在system_server進程中,我們沒辦法跨越進程的鴻溝,當然也沒有權限注入到system_server進程中。而且,AMS中的處理流程非常復雜,都是棧的管理之類的邏輯,很難找到hook點,來把占坑的Activity替換回插件裡的activity,所以我們還是得回到app進程中。
3.重新回到app進程
前面說過在ApplicationThreadNative中會處理AMS對ActivityThread的binder調用,它的onTransact函數會調用scheduleLaunchActivity,其具體實現在ApplicationThread中。ApplicationThread是ActivityThread裡的一個內部類,它的scheduleLaunchActivity的實現就是發一個LAUNCH_ACTIVITY類型的message到ActivityThread中的一個handler上。
這個名為H的handler中用handleLaunchActivity函數來處理AMS發起的scheduleLaunchActivity調用。handleLaunchActivity裡又調用了performLaunchActivity。
performLaunchActivity中又用到了Instrumentation類,調它的newActivity函數構造出activity對象。newActivity函數很簡單,直接用classLoader加載了Activity類,然後用反射調它的構造函數newInstance出activity實例。
然後再回到performLaunchActivity中,在通過netActivity得到activity的實例後,接下來就該調用它的生命周期函數onCreate了,照舊還是通過Instrumentation來完成,調用它的callActivityOnCreate函數。
在callActivityOnCreate裡會調用Activity的performCreate函數,它裡面調用的就是我們熟悉的onCreate了。
到此為止,我們簡單地跟蹤了startActivity的整個流程,根據前面的設想,這裡的Activity還是占坑的ProxyActivity$1,要是能把它替換回插件的PluginActivity1就好了。從前面的流程可以看出,Instrumentation這個類是startActivity整個過程中的必經之路,無論是從app到AMS,還是從AMS回到app都會經過它,要是能hook它就好了,因為它是ActivityThread裡的一個成員mInstrumentation,所以我們在客戶端進程中可以通過反射拿到ActivityThread對象,也可以拿到mInstrumentation。
三、利用反射完成動態代理
前面我們梳理了startActivity的整個調用流程,發現在app和AMS之間來回調用的過程中有兩個可以hook的點,下面我們來具體分析一下:
1)從app進程進入到AMS中,都是通過AMS在客戶端進程的代理ActivityManagerNative類裡的靜態單例對象gDefault來發起binder調用的,它是ActivityManagerProxy類型。我們可以在這個地方換掉startActivity傳入的參數,把占坑的ProxyActivity$1傳給AMS。但是從AMS返回到app裡,調用scheduleLaunchActivity等一系列動作卻不會經過它,而是經過客戶端的代理ApplicationThreadNative發起對客戶端的binder調用,但如果hook ApplicationThreadNative也不能解決問題,因為最後真正構造出activity實例的步驟是在Instrumentation的newActivity函數中,ApplicationThreadNative對象離著它還很遠。
2)參考第一部分我們分析的流程,從app進入AMS和從AMS返回到app,在客戶端進程中都要經過Instrumentation這個類。從app進入AMS時,調用它的execStartActivity函數,在execStartActivity中再調ActivityManagerNative.getDefault().startActivity,發起binder調用。從AMS返回app時,調用它的newActivity,用發射構造出activity的實例,所以Instrumentation才是我們需要hook的類,可以在進入AMS時把插件的PluginActivity1換成占坑的ProxyActivity$1,在回到app構造真正的activity時,new出插件的PluginActivity1。這樣一來,對AMS來說它管理的是ProxyActivity$1,對宿主應用來說,插件PluginActivity1在它的進程裡已經被實例化和運行了
現在我們來看Small框架中的代碼是處理的。
ActivityThread中有一個單例對象sCurrentActivityThread,它是在attche時被賦值的。前面說過Instrumentation在ActivityThread中的實例是其成員mInstrumentation,所以我們先通過ActivityThread的靜態方法currentActivityThread得到它的實例sCurrentActivityThread,然後找到它的成員mInstrumentation,把它的引用對象換成自己實現的InstrumentationWrapper。InstrumentationWrapper也是Instrumentation類型,它裡面實現了execStartActivity和newActivity這兩個方法,用來替換掉原來的函數調用。
其中,execStartActivity函數在不同android版本中的參數有所不同,wrapIntent函數完成了替換Intent的動作,把PluginActivity1的Intent替換成了ProxyActivity$1的Intent。最後在ReflectAccelerator中完成原來函數的調用。
newActivity中的unwrapIntent是wrapIntent的反向工作,得到的className是插件中的PluginActivity1的類名,最後再調用原來的newActivity完成activity的構造。
這樣最終完成了用PluginActivity1替換AndroidManifest.xml中的ProxyActivity$1的偷天換日的工作,插件裡的PluginActivity1既可以被正常的加載運行也不用擔心它的生命周期之類的管理了。
本文實例為大家分享了Android實現單項、多項選擇操作的相關代碼,供大家參考,具體內容如下1、單項選擇1.1.布局<?xml version=1.0 en
AppWidget通常被稱為桌面插件,是Android系統應用開發層上的重要部分,是把一個進程的控件嵌入到別外一個進程的窗口裡的一種方法,是與客戶端程序交互的組件程序。
這篇博客我們來介紹一下解釋器模式(Interpreter Pattern),也是行為型設計模式之一,是一種用的比較少的設計模式,其提供了一種解釋語言的語法或表達式的方式,
ImageView有scaleType屬性可以縮放圖片,讓圖片鋪滿屏幕寬度,但是會出現壓縮或裁剪的情況。ImageView的scaleType的屬性分別是ma