Android主執行緒(ActivityThread)原始碼分析
來自轉載:
在寫這篇部落格之前,先丟擲一個問題,安卓應用程式的入口是什麼呢?我想不少人可能回答說:application的onCreate方法,其實並不是的,即使是application,也有一個方法比onCreate先執行,這個方法就是attachBaseContext(Context context)方法:一般情況下,可以在這個方法中進行多dex的分包注入,比如下面的程式碼:
- @Override
- protected void attachBaseContext(Context base) {
- MultiDex.install(base);
-
super.attachBaseContext(base);
- try {
- HandlerInject.hookHandler(base);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
當然了我這是舉例說明這個方法的作用,這個方法是application初始化之後會立即被執行的方法,其次才是onCreate方法,當然application並不是安卓程式的入口,安卓應用程式作為一個控制類程式,跟Java程式類似,都是有一個入口的,而這個入口就是ActivityThread,ActiviyThread也有一個main方法,這個main方法是安卓應用程式真正的入口,下面我將詳細分析ActivityThread以及一些問題的非常規方法,文章比較長,請大家有空慢慢看,相信看完你會對安卓主執行緒有一個全新的認識。
首先要明白,ActivityThread有什麼作用呢?ActivityThread的作用很多,但最主要的作用是根據AMS(ActivityManagerService的要求,通過IApplicationTHread的介面)負責排程和執行activities、broadcasts和其它操作。在Android系統中,四大元件預設都是執行在主執行緒上的,接下來的程式碼分析你會看到這些元件的管理。
首先是ActivityThread入口的主要程式碼如下:
- //初始化Looper
- Looper.prepareMainLooper();
-
//建立ActivityThread物件,並繫結到AMS
- ActivityThread thread = new ActivityThread();
- //一般的應用程式都不是系統應用,因此設定為false,在這裡面會繫結到AMS
- thread.attach(false);
- if (sMainThreadHandler == null) {
- sMainThreadHandler = thread.getHandler();
- }
- if (false) {
- Looper.myLooper().setMessageLogging(new
- LogPrinter(Log.DEBUG, "ActivityThread"));
- }
- // End of event ActivityThreadMain.
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- //開啟迴圈
- Looper.loop();
- throw new RuntimeException("Main thread loop unexpectedly exited");
上面的程式碼都不難,但正是這些程式碼,讓我們明白了2個問題:1.我們之所以可以在Activity用Handler handler=new Handler()直接創建出來就預設繫結到主執行緒了,是因為上面的程式碼為我們做了繫結主執行緒的Looper的事情,2.主執行緒的Looper是不能在程式中呼叫退出的,最後一句程式碼看到沒,如果呼叫的話,就會丟擲異常,退出主執行緒的迴圈是框架層在呼叫退出應用程式的時候才呼叫的,這個下面會講到。
- //IBinder物件,AMS持有此物件的代理物件,從而通知ActivityThread管理其他事情
- final ApplicationThread mAppThread = new ApplicationThread();
- final Looper mLooper = Looper.myLooper();
- final H mH = new H();
- //儲存了所有的Activity,以IBinder作為key,IBinder是Activity在框架層的唯一表示
- final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
- //儲存了所有的Service
- final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
- //ActivityThread物件,拿到這個物件,可以反射呼叫這個類的需要的方法
- private static ActivityThread sCurrentActivityThread;
以上程式碼短短几句,卻可以看出ActivityThread的重要作用, 與AMS互動並且管理Activity和Service,那麼問一個實際問題,一個app中,如何知道使用者去過哪些頁面呢?當然方法有多種,但通過上面的程式碼,或許聰明的你想到了去找到mActivities這個欄位就好了,是的,因為這個欄位儲存了所有的Activity物件,拿到這個欄位的值就可以知道有哪些Activity了,而那些Activity都是使用者停留過的,這也是一個解決方法。
- //儲存的Activity的表示物件ActivityClientRecord
- static final class ActivityClientRecord {
- //唯一表示
- IBinder token;
- int ident;
- Intent intent;
- String referrer;
- IVoiceInteractor voiceInteractor;
- Bundle state;
- PersistableBundle persistentState;
- //這裡儲存了真正的Activity物件
- Activity activity;
- Window window;
- Activity parent;
- String embeddedID;
- boolean paused;
- boolean stopped;
- boolean hideForNow;
- Configuration newConfig;
- Configuration createdConfig;
- Configuration overrideConfig;
- private Configuration tmpConfig = new Configuration();
- ActivityClientRecord nextIdle;
- ProfilerInfo profilerInfo;
- ActivityInfo activityInfo;
- CompatibilityInfo compatInfo;
- LoadedApk packageInfo;
- boolean startsNotResumed;
- boolean isForward;
- int pendingConfigChanges;
- boolean onlyLocalRequest;
- View mPendingRemoveWindow;
- WindowManager mPendingRemoveWindowManager;
- }
ActivityClientRecord是ActivtityThread的一個內部類,這個ActivityClientRecord是傳入AMS的一個標誌來的,裡面攜帶了很多資訊,上面的程式碼可以看出,其中有一個Activity物件,裡面的Activity就是真正的Activity例項了,通過或者它,可以知道使用者去哪些頁面,當然可能比較繁瑣,但這種方法是可行的。
下面重點分析ApplicationThread,首先它不是一個執行緒,而是一個Binder物件,
- private class ApplicationThread extends ApplicationThreadNative {
- //省略一些程式碼
- //準備暫停Activity
- public final void schedulePauseActivity(IBinder token, boolean finished,
- boolean userLeaving, int configChanges, boolean dontReport) {
- sendMessage(
- finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
- token,
- (userLeaving ? 1 : 0) | (dontReport ? 2 : 0),
- configChanges);
- }
- public final void scheduleStopActivity(IBinder token, boolean showWindow,
- int configChanges) {
- sendMessage(
- showWindow ? H.STOP_ACTIVITY_SHOW : H.STOP_ACTIVITY_HIDE,
- token, 0, configChanges);
- }
- //省略一些程式碼
- public abstract class ApplicationThreadNative extends Binder
- implements IApplicationThread
- /**
- * Cast a Binder object into an application thread interface, generating
- * a proxy if needed.
- */
- static public IApplicationThread asInterface(IBinder obj) {
- if (obj == null) {
- return null;
- }
- IApplicationThread in =
- (IApplicationThread)obj.queryLocalInterface(descriptor);
- if (in != null) {
- return in;
- }
- //不同程序,生成一個代理物件具體幹活
- return new ApplicationThreadProxy(obj);
- }
看到這裡,我想熟悉Binder機制的人都知道是怎麼回事了吧,沒錯,ApplicationThread經過包裝之後變成代理物件ApplicationThreadProxy,也許你會問你怎麼知道是ApplicationThreadProxy呢,其實這個嘛,debug一下就知道的啦,下面是圖片
看到紅色框框的嗎,IApplictionThread在AMS就是ApplicationThreadProxy物件啦,呵呵
然後傳遞到AMS,而AMS有了這個物件,就可以呼叫裡面的方法了,schedulePauseActivity這個方法就是一個Activity被暫停而執行的方法了,可見ActivityThread與AMS的互動是一次IPC呼叫,當然這裡要搞清楚些,AMS呼叫ActivityThread是通過ApplicationThreadProxy物件,而ActivityThread呼叫AMS的方法卻是ActivityManagerProxy,這也是一個IPC過程呼叫,在結尾會有更詳細的程式碼,好了,現在你應該明白了AMS呼叫ActivityThread方法是通過ApplicationThreadProxy物件了,裡面的方法一般都是schedule開頭,比如scheduleDestroyActivity,scheduleReceiver等等,一個問題,Activity被暫停之後執行的第一個方法是schedulePauseActivity,之後通過訊息分發機制呼叫handlePauseActivity->performPauseActivity->callActivityOnPause->Activity.onPause()方法,也就是說Activity的裡面的生命週期方法其實是比較晚呼叫了,由AMS排程,ActivityThread執行,再到Activity本身。
接下里分析Android中非常重要的訊息分發機制在ActivityThread的呼叫過程,訊息分發機制在整個Android系統中佔據了非常重要的地位,Android系統也是依靠訊息分發機制來實現系統的運轉的,訊息分發機制也是Android面試中的必問知識點,在ActivityThread中是H這個繼承了Handler類,下面是一些變數:
- private class H extends Handler {
- //啟動Activity
- public static final int LAUNCH_ACTIVITY = 100;
- //暫停Activity
- public static final int PAUSE_ACTIVITY = 101;
- public static final int PAUSE_ACTIVITY_FINISHING= 102;
- public static final int STOP_ACTIVITY_SHOW = 103;
- public static final int STOP_ACTIVITY_HIDE = 104;
- public static final int SHOW_WINDOW = 105;
- ..........
- }
可以看到,裡面的常量表示的是具體操作的what值,當然了,其他操作也有對應的,不一一列出了,然後重寫了handMessage方法,下面是程式碼:
- public void handleMessage(Message msg) {
- if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
- switch (msg.what) {
- case LAUNCH_ACTIVITY: {
- Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
- final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
- r.packageInfo = getPackageInfoNoCheck(
- r.activityInfo.applicationInfo, r.compatInfo);
- handleLaunchActivity(r, null);
- Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
- }
當然了,其他都是這樣處理的,不一一列出。看到沒,LAUNCH_ACTIVITY這個是處理啟動Activity的,接著呼叫了handleLaunchActivity方法,handleLaunchActivity呼叫了建立Activity的方法performLaunchActivity,
- Activity activity = null;
- try {
- java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
- activity = mInstrumentation.newActivity(
- cl, component.getClassName(), r.intent);
- StrictMode.incrementExpectedActivityCount(activity.getClass());
- r.intent.setExtrasClassLoader(cl);
- r.intent.prepareToEnterProcess();
- if (r.state != null) {
- r.state.setClassLoader(cl);
- }
- }
看到沒,Activity其實就一個普普通通的Java物件,利用反射建立,然後由ClassLoader載入進去,之後由框架層的呼叫,從而具有了生命週期,成為了一個元件,從而也可以知道在外掛化中,僅僅載入Activity是不行的,還必須交給框架層去呼叫才具有生命力,不然沒意義,當然了,不僅是Activity,其實,Service,BroadCase,等都是這樣由反射建立,然後載入由框架層呼叫的,無一例外
看到沒,建立Activity之後,會呼叫attach方法繫結,然後判斷是否設定主題,如果有的話就設定主題,然後再呼叫mInstrumentation的callActivityOnCreate,這裡其實就是呼叫了Activity的onCreate方法,Activity的建立也是由Instrumentation建立的,有關的Instrumentation的說明,請看我的上一篇文章:
至此,onCreate被呼叫了,
- if (!r.activity.mFinished) {
- //執行onStart方法
- activity.performStart();
- r.stopped = false;
- }
- if (!r.activity.mFinished) {
- if (r.isPersistable()) {
- if (r.state != null || r.persistentState != null) {
- mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
- r.persistentState);
- }
- } else if (r.state != null) {
- mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
- }
- }
- if (!r.activity.mFinished) {
- activity.mCalled = false;
- if (r.isPersistable()) {
- //OnPostCreate方法也是一個週期方法,只是一般開發用不到
- mInstrumentation.callActivityOnPostCreate(activity, r.state,
- r.persistentState);
- } else {
- mInstrumentation.callActivityOnPostCreate(activity, r.state);
- //這裡把Activity儲存起來,可以知道拿到mActivities就可以知道有哪些Activity了,也就是我上面提到的問題,如何統計使用者去過多少頁面呢,這個方法是可以實現的。
- mActivities.put(r.token, r);
上面可以看到執行完onCreate方法之後,執行onStart方法,然後執行onPostOnCreate方法
回到上面的呼叫handleLaunchActivity方法,在執行完Activity的onCreate,onStart生命週期方法之後,就來到
handleResumeActivity方法了,
- if (r.window == null && !a.mFinished && willBeVisible) {
- r.window = r.activity.getWindow();
- View decor = r.window.getDecorView();
- //一開始執行onResume的時候其實頁面是不可見的
- decor.setVisibility(View.INVISIBLE);
- ViewManager wm = a.getWindowManager();
- WindowManager.LayoutParams l = r.window.getAttributes();
- a.mDecor = decor;
- l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
- l.softInputMode |= forwardBit;
- if (a.mVisibleFromClient) {
- a.mWindowAdded = true;
- //這裡非常重要,把頂級devorView新增進去
- wm.addView(decor, l);
- }
- .......
- if (r.activity.mVisibleFromClient) {
- //執行到這裡,Activity才是真正對使用者可見
- r.activity.makeVisible();
- }
- // Tell the activity manager we have resumed.
- if (reallyResume) {
- try {
- //這裡通知AMS,Activity已經Resume了
- ActivityManagerNative.getDefault().activityResumed(token);
- } catch (RemoteException ex) {
- }
- }
看到沒,在執行onResume的時候,其實那時候是不可見的,直到執行完r.activity.mVisibleFromClient這個條件為真的時候才真正可見,從程式碼我們也可以看出,其中Activity就一個View新增到視窗而已,因而Activity本質上只是一個視窗而已,至此,Activity才真正對使用者可見,可以看到,上面的呼叫棧其實都是Handler訊息分發機制實現而已,一步一步來進行,通過IPC跟AMS通訊,有沒有覺得Android系統博大精深呢,本人以為正是由於Binder機制的存在,Android呼叫遠端程序如同呼叫本地程序一樣,來回自由,不得不佩服谷歌那幫神一般存在的程式設計師,不知道你有沒有這種想法呢?當然了,其他Handler分發訊息的呼叫都是一樣的,各位讀者可以自己去分析其他訊息的呼叫,其實,所有Activity的生命週期,包括其他元件的呼叫都由H 來負責分發呼叫。
- //獲取ActivityThread物件
- public static ActivityThread currentActivityThread() {
- return sCurrentActivityThread;
- }
- //獲取當前包名
- public static String currentPackageName() {
- ActivityThread am = currentActivityThread();
- return (am != null && am.mBoundApplication != null)
- ? am.mBoundApplication.appInfo.packageName : null;
- }
- //獲取當前程序名
- public static String currentProcessName() {
- ActivityThread am = currentActivityThread();
- return (am != null && am.mBoundApplication != null)
- ? am.mBoundApplication.processName : null;
- }
- //獲取當前Application物件
- public static Application currentApplication() {
- ActivityThread am = currentActivityThread();
- return am != null ? am.mInitialApplication : null;
- }
- //獲取LoadApk,LoadApk是一個Apk在記憶體中的表示,這個跟外掛化有關
- public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
- int flags) {
- return getPackageInfo(packageName, compatInfo, flags, UserHandle.myUserId());
- }
- 上面的4個方法,其實作用很有用,在一些特殊場合的情況下用得到,後面的問題中會有這些方法的呼叫。
下面分析廣播在ActivityThread的呼叫,當然了廣播也一樣,首先通過IPC呼叫到AMS,然後由AMS排程返回到ActivityThread處理,下面是程式碼:
- private void handleReceiver(ReceiverData data) {
- LoadedApk packageInfo = getPackageInfoNoCheck(
- data.info.applicationInfo, data.compatInfo);
- IActivityManager mgr = ActivityManagerNative.getDefault();
- BroadcastReceiver receiver;
- try {
- java.lang.ClassLoader cl = packageInfo.getClassLoader();
- data.intent.setExtrasClassLoader(cl);
- data.intent.prepareToEnterProcess();
- data.setExtrasClassLoader(cl);
- //看到沒,廣播也是由反射建立的,跟Activity一樣
- receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
- }
- ContextImpl context = (ContextImpl)app.getBaseContext();
- sCurrentBroadcastIntent.set(data.intent);
- receiver.setPendingResult(data);
- //大家都知道的廣播回撥方法
- receiver.onReceive(context.getReceiverRestrictedContext(),
- data.intent);
- }
從上面可以看到,廣播本質跟Activity一樣,都是反射建立,唯一不同的是,Activity需要經過框架層的呼叫並具有了生命週期方法,而廣播沒有生命方法的概念,只是回調了一個onReceive方法,所以相對Activity,廣播比較容易理解,這裡先留下一個問題,為什麼廣播能夠跨程序呼叫呢,而EventBus,OTTO等事件匯流排就不能,其實根本原因在於廣播是由AMS系統程序統一管理,我們本地程序只是負責執行而已,而EventBus,OTTO那些事件匯流排的處理都是建立在同個程序之間的,因此他們不能實現跨程序傳送訊息,而廣播卻可,當然了AMS處理廣播也比較複雜,今天暫時不討論。
下面分析Service在ActivityThread的處理,程式碼如下:
- private void handleCreateService(CreateServiceData data) {
- LoadedApk packageInfo = getPackageInfoNoCheck(
- data.info.applicationInfo, data.compatInfo);
- Service service = null;
- try {
- java.lang.ClassLoader cl = packageInfo.getClassLoader();
- //看到沒,Service跟BroadCast,Activity一樣反射建立
- service = (Service) cl.loadClass(data.info.name).newInstance();
- }
- }
- try {
- if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
- ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
- context.setOuterContext(service);
- Application app = packageInfo.makeApplication(false, mInstrumentation);
- service.attach(context, this, data.info.name, data.token, app,
- ActivityManagerNative.getDefault());
- //執行onCreate方法
- service.onCreate();
- //把Service儲存起來
- mServices.put(data.token, service);
- }
- }
可以看到Service的處理跟前面的廣播沒啥大區別,都是反射建立,然後回撥方法,當然還剩下最後一個ContentProvider,其實這玩意也是反射建立,當然了這個ContentProvider流程比較複雜,
- private IActivityManager.ContentProviderHolder installProvider(Context context,
- IActivityManager.ContentProviderHolder holder, ProviderInfo info,
- boolean noisy, boolean noReleaseNeeded, boolean stable) {
- ContentProvider localProvider = null;
- IContentProvider provider;
- if (holder == null || holder.provider == null) {
- if (DEBUG_PROVIDER || noisy) {
- Slog.d(TAG, "Loading provider " + info.authority + ": "
- + info.name);
- }
- Context c = null;
- ApplicationInfo ai = info.applicationInfo;
- if (context.getPackageName().equals(ai.packageName)) {
- c = context;
- } else if (mInitialApplication != null &&
- mInitialApplication.getPackageName().equals(ai.packageName)) {
- c = mInitialApplication;
- } else {
- try {
- c = context.createPackageContext(ai.packageName,
- Context.CONTEXT_INCLUDE_CODE);
- } catch (PackageManager.NameNotFoundException e) {
- // Ignore
- }
- }
- if (c == null) {
- Slog.w(TAG, "Unable to get context for package " +
- ai.packageName +
- " while loading content provider " +
- info.name);
- return null;
- }
- try {
- final java.lang.ClassLoader cl = c.getClassLoader();
- //這裡是重點反射建立
- localProvider = (ContentProvider)cl.
- loadClass(info.name).newInstance();
- provider = localProvider.getIContentProvider();
- //執行attachInfo方法
- localProvider.attachInfo(c, info);
- }
可以看到四大元件都是反射建立,並執行生命週期方法,而排程是由AMS負責,ActivityThread主執行緒負責執行,所以說AMS是排程者,主執行緒是執行者,就像一個公司裡面,AMS是董事會,而ActivityThread是CEO,負責執行具體的事情同時報告給AMS,AMS備份,以有案可查。
OK,以上便是ActivityThread的主要負責的事情,當然了,那些停止,銷燬Activity跟啟動Activity的流程是一樣的,跟AMS都是一個IPC過程呼叫,下面給出ActivityThread的工作流程(AMS呼叫ActivityThread方法):
這個是AMS呼叫ActivityThread的方法,當然了ActivityThread呼叫AMS都差不多,只是物件換成了ActivityManagerProxy物件而已了。
最後是一些問題在特殊情況下的非常規方法,有興趣的可以看看:
1.任何時候,任何地方,任何邏輯,如何獲取全域性Application物件
對於這個問題,也許很多人會說,在自定義的application中定義一個方法,然後在onCreate中賦值,這樣就可以獲取了,沒錯,絕大部分的人是這樣做,而且好像也從來都沒有出錯過,是的,這種方法是目前大部分的人都會用,然而,這種方法其實是由限制的,當然了在普通的開發中還是沒問題的,然而如果不能接觸程式邏輯本身的話(在逆向等比較特殊的情況下),那麼上面的方法就無效了,那麼是不是就沒辦法了呢,不是的,上面的程式碼中曾經說過有個方法:
- public static Application currentApplication() {
- ActivityThread am = currentActivityThread();
- return am != null ? am.mInitialApplication : null;
- }
- 可以看到是可以獲取的,可是ActivityThread是一個隱藏類,那怎麼辦呢,反射吧,下面是程式碼:
- private void getGlobalApplication()throws Exception{
- Class<?> clazz=Class.forName("android.app.ActivityThread");
- Method method=clazz.getDeclaredMethod("currentApplication");
- Application application= (Application) method.invoke(null);
- Log.d("[app]","通過反射主執行緒獲取的pplication為:"+application);
- }
結果如下:
可以看到成功獲取了,問題解決!
2.如何獲取Activity例項
可能有人會說不是可以getApplication物件嗎,那個方法獲取的是上下文物件,並不是真正的activity例項,那麼如何獲取呢,下面是其中一個方法:
首先在基類定義一個Activity變數,然後在onCreate中賦值給具體的子類,這樣子類獲取的Activity例項。
這種方法是最簡單,也是一般情況下用到的。
第二種方法:利用application的生命週期監聽方法ActivityLifecycleCallbacks,下面是具體的程式碼:
OK,也成功獲取了當前ActivityThread例項了。
第三種方法:
我們剛才在看ActivityThread的原始碼的時候,發現有這麼一個方法:
- public final Activity getActivity(IBinder token) {
- return mActivities.get(token).activity;
- }
你沒有看錯,這裡也有一個方法直接獲取Activity例項的,可是傻眼了,引數必須是IBinder型別,就是activity身份的唯一標誌,那麼這個IBinder該如何獲取呢?額,我想想哈,在Activity啟動的時候,我們曾經說過在執行完onResume的時候,會報告給AMS,方法為activityResumed,而這個方法的引數剛好就是IBinder型別的,這個引數就代表了當前Activity的toke,那麼只要有了這個token,不就可以呼叫上面的程式碼來獲取Activity了嗎,可是activityResumed這個方法是AMS執行的啊,AMS可是執行在系統程序裡面的哦,怎麼辦呢?那就來點暴力點的吧,我們直接Hook AMS,上面說過ActivityThread呼叫AMS的方法的時候,也是用了Binder機制,具體點說是用了ActivitymanagerProxy這個代理物件進行呼叫的,而這個類是ActivityManagerNative的子類ActivityManagerService在本地程序的一個代理物件而已(個人認為Binder機制在Java層是很好理解的,只需要記住,不同程序之間都是拿對方的代理物件進行幹活的),聽不懂是吧,下面看看程式碼吧:
- public abstract class ActivityManagerNative extends Binder implements IActivityManager
- {
- /**
- * Cast a Binder object into an activity manager interface, generating
- * a proxy if needed.
- */
- static public IActivityManager asInterface(IBinder obj) {
- if (obj == null) {
- return null;
- }
- IActivityManager in =
- (IActivityManager)obj.queryLocalInterface(descriptor);
- if (in != null) {
- //如果是本程序的話,之間返回來
- return in;
- }
- //如果是不同程序的話,就生成一個ActivityManagerProxy代理物件
- return new ActivityManagerProxy(obj);
- }
- private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
- protected IActivityManager create() {
- //通過ServiceManager獲取到了真正的ActivityManagerService服務
- IBinder b = ServiceManager.getService("activity");
- if (false) {
- Log.v("ActivityManager", "default service binder = " + b);
- }
- //這裡看到沒,呼叫asInterface把AMS傳進去轉換了一下了
- IActivityManager am = asInterface(b);
- if (false) {
- Log.v("ActivityManager", "default service = " + am);
- }
- //因此這裡返回來的是ActivityManagerProxy物件了,如果是不同程序的話,就是Binder機制中
- return am;
- }
- };
- }
我們只需要替換為我們自己的代理物件進行幹活,然後在activityResumed方法中進行攔截就好了,好了,說幹就幹,
- public static void hookActivityManagerService() throws Throwable {
- Class<?> activityManagerNativeClass=Class.forName("android.app.ActivityManagerNative");
- //4.0以後,ActivityManagerNative有gDefault單例來進行儲存,這個程式碼中一看就知道了
- Field gDefaultField=activityManagerNativeClass.getDeclaredField("gDefault");
- gDefaultField.setAccessible(true);
- Object gDefault=gDefaultField.get(null);
- Class<?> singleton=Class.forName("android.util.Singleton");
- //mInstance其實就是真正的一個物件
- Field mInstance=singleton.getDeclaredField("mInstance");
- mInstance.setAccessible(true);
- //真正的物件,就是幹活的物件啦,其實就是ActivityManagerProxy而已啦
- Object originalIActivityManager=mInstance.get(gDefault);
- Log.d("[app]","originalIActivityManager="+originalIActivityManager);
- //通過動態代理生成一個介面的物件
- Class<?> iActivityManagerInterface=Class.forName("android.app.IActivityManager");
- Object object= Proxy.newProxyInstance(iActivityManagerInterface.getClassLoader(),
- new Class[]{iActivityManagerInterface},new IActivityManagerServiceHandler(originalIActivityManager));
- //這裡偷樑換柱,替換為我們自己的物件進行幹活就好了
- mInstance.set(gDefault,object);
- Log.d("[app]","Hook AMS成功");
- }
IActivityManagerServiceHandler實現類動態代理介面InvocationHandler,在裡面攔截了activityResumed方法而已,攔截之後拿到Token,然後呼叫反射方法就可以獲取Activity的例項啦,下面是具體的程式碼,比較簡單,
- public class IActivityManagerServiceHandler implements InvocationHandler {
- private Object base;
- public IActivityManagerServiceHandler(Object base) {
- this.base = base;
- }
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- //判斷是不是activityResumed,如果是的話,那麼攔截引數,然後反射獲取例項就好
- if (method.getName().equals("activityResumed")){
- //這裡拿到想要的IBinder啦,就是token
- IBinder iBinder= (IBinder) args[0];
- Log.d("[app]","執行activityResumed方法了,引數toke為"+iBinder);
- Class<?> clazz=Class.forName("android.app.ActivityThread");
- Method method1=clazz.getDeclaredMethod("currentActivityThread");
- Object object=method1.invoke(null);
- Method getActivity=clazz.getDeclaredMethod("getActivity",IBinder.class);
- Activity mActivity= (Activity) getActivity.invoke(object,iBinder);
- Log.d("[app]","Hook AMS以後:當前的Activity為:"+mActivity);
- }
- return method.invoke(base,args);
- }
- }
然後在application中進行注入就好了,好了,下面列印看看吧,
可以看到成功了吧,呵呵,欺騙系統的感覺如何啊,順便說句,360外掛化的核心思想就是瞞天過海,欺騙系統,通過把系統完的團團轉,從而實現外掛系統的天衣無縫,當然需要處理一些相容性問題,OK,上面的問題也解決了。
3.有辦法干預Activity的啟動或者其他過程嗎,常規下是沒辦法的,因為這是框架層的排程,再呼叫Activity生命週期方法的時候,其實已經是很晚時機了,已經沒辦法再進一步操作了,也就是說在應用層上是無法干預這些行為的,當然了,你可以在框架層排程的時候,半路殺出個程咬金來就好了,具體的見我的上一篇文章:
4.有辦法在啟動Activity的時候彈出一個土司嗎,或者做其他的事情,就是在執行LaunchActivity方法的時候彈土司或者其他事情?
額,常規下是沒辦法的,有人會說我在onCreate中彈出來,呵呵,這就違背了我的條件,是在LaunchActivity的時候,就是在AMS給ActivityThread發訊息啟動Activiy的時候做一些事情,我們都知道這個過程是由Handler傳送訊息來實現的,可是通過Handler處理訊息的程式碼來看,其實是有順序的,下面是Handler處理訊息的程式碼:
- /**
- * Handle system messages here.
- */
- public void dispatchMessage(Message msg) {
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- if (mCallback != null) {
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- handleMessage(msg);
- }
- }
看到了嗎,handler處理訊息的時候,首先去檢查是否實現了callback介面,如果有實現的的話,那麼會直接執行介面方法,然後才是handleMessage方法,最後才是執行重寫的handleMessage方法,我們一般大部分時候都是重寫了handleMessage方法,而ActivityThread主執行緒用的正是重寫的方法,這種方法的優先順序是最低的,我們完全可以實現介面來替換掉系統Handler的處理過程,下面請看程式碼:
- public static void hookHandler(Context context) throws Exception {
- Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
- Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
- currentActivityThreadMethod.setAccessible(true);
- //獲取主執行緒物件
- Object activityThread = currentActivityThreadMethod.invoke(null);
- //獲取mH欄位
- Field mH = activityThreadClass.getDeclaredField("mH");
- mH.setAccessible(true);
- //獲取Handler
- Handler handler = (Handler) mH.get(activityThread);
- //獲取原始的mCallBack欄位
- Field mCallBack = Handler.class.getDeclaredField("mCallback");
- mCallBack.setAccessible(true);
- //這裡設定了我們自己實現了介面的CallBack物件
- mCallBack.set(handler, new CustomHandler(context, handler));
- }
當然還有CustomHandler類,這個類實現了Callback介面,一樣可以攔截方法
- public class CustomHandler implements Callback {
- //這個100一般情況下最好也反射獲取,當然了你也可以直接寫死,跟系統的保持一致就好了
- public static final int LAUNCH_ACTIVITY = 100;
- private Handler origin;
- private Context context;
- public CustomHandler(Context origin, Handler context) {
- this.context = origin;
- this.origin = context;
- }
- public boolean handleMessage(Message msg) {
- if (msg.what == LAUNCH_ACTIVITY) {
- //這樣每次啟動的時候便會彈出土司來
- Toast.makeText(
- context.getApplicationContext(),
- "hello,I am going launch", Toast.LENGTH_SHORT).show();
- }
- origin.handleMessage(msg);
- return false;
- }
- }
寫完之後在application中注入就好了,OK,你可以去測試一下,看看是不是每次啟動Activity的時候都會彈出hello,I am going launch這個字串。
當然除了這些比較特殊的問題之外,一些時候,特別是外掛化的時候,經常遇到這種問題,這些問題往往在應用層是無法攔截的,因為到達應用層的時候,呼叫鏈已經呼叫完畢,沒機會去插手了,這種時候就可以考慮這些比較特殊的方法了,當然啦,開發中,哪種最快解決問題用哪種吧。