1. 程式人生 > 實用技巧 >Android主執行緒(ActivityThread)原始碼分析

Android主執行緒(ActivityThread)原始碼分析

來自轉載:

在寫這篇部落格之前,先丟擲一個問題,安卓應用程式的入口是什麼呢?我想不少人可能回答說:application的onCreate方法,其實並不是的,即使是application,也有一個方法比onCreate先執行,這個方法就是attachBaseContext(Context context)方法:一般情況下,可以在這個方法中進行多dex的分包注入,比如下面的程式碼:

  1. @Override
  2. protected void attachBaseContext(Context base) {
  3. MultiDex.install(base);
  4. super.attachBaseContext(base);
  5. try {
  6. HandlerInject.hookHandler(base);
  7. } catch (Exception e) {
  8. e.printStackTrace();
  9. }
  10. }

當然了我這是舉例說明這個方法的作用,這個方法是application初始化之後會立即被執行的方法,其次才是onCreate方法,當然application並不是安卓程式的入口,安卓應用程式作為一個控制類程式,跟Java程式類似,都是有一個入口的,而這個入口就是ActivityThread,ActiviyThread也有一個main方法,這個main方法是安卓應用程式真正的入口,下面我將詳細分析ActivityThread以及一些問題的非常規方法,文章比較長,請大家有空慢慢看,相信看完你會對安卓主執行緒有一個全新的認識。

首先要明白,ActivityThread有什麼作用呢?ActivityThread的作用很多,但最主要的作用是根據AMS(ActivityManagerService的要求,通過IApplicationTHread的介面)負責排程和執行activities、broadcasts和其它操作。在Android系統中,四大元件預設都是執行在主執行緒上的,接下來的程式碼分析你會看到這些元件的管理。

首先是ActivityThread入口的主要程式碼如下:

  1. //初始化Looper
  2. Looper.prepareMainLooper();
  3. //建立ActivityThread物件,並繫結到AMS
  4. ActivityThread thread = new ActivityThread();
  5. //一般的應用程式都不是系統應用,因此設定為false,在這裡面會繫結到AMS
  6. thread.attach(false);
  7. if (sMainThreadHandler == null) {
  8. sMainThreadHandler = thread.getHandler();
  9. }
  10. if (false) {
  11. Looper.myLooper().setMessageLogging(new
  12. LogPrinter(Log.DEBUG, "ActivityThread"));
  13. }
  14. // End of event ActivityThreadMain.
  15. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
  16. //開啟迴圈
  17. Looper.loop();
  18. throw new RuntimeException("Main thread loop unexpectedly exited");
上面的程式碼都不難,但正是這些程式碼,讓我們明白了2個問題:1.我們之所以可以在Activity用Handler handler=new Handler()直接創建出來就預設繫結到主執行緒了,是因為上面的程式碼為我們做了繫結主執行緒的Looper的事情,2.主執行緒的Looper是不能在程式中呼叫退出的,最後一句程式碼看到沒,如果呼叫的話,就會丟擲異常,退出主執行緒的迴圈是框架層在呼叫退出應用程式的時候才呼叫的,這個下面會講到。
  1. //IBinder物件,AMS持有此物件的代理物件,從而通知ActivityThread管理其他事情
  2. final ApplicationThread mAppThread = new ApplicationThread();
  3. final Looper mLooper = Looper.myLooper();
  4. final H mH = new H();
  5. //儲存了所有的Activity,以IBinder作為key,IBinder是Activity在框架層的唯一表示
  6. final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
  7. //儲存了所有的Service
  8. final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
  9. //ActivityThread物件,拿到這個物件,可以反射呼叫這個類的需要的方法
  10. private static ActivityThread sCurrentActivityThread;

以上程式碼短短几句,卻可以看出ActivityThread的重要作用, 與AMS互動並且管理Activity和Service,那麼問一個實際問題,一個app中,如何知道使用者去過哪些頁面呢?當然方法有多種,但通過上面的程式碼,或許聰明的你想到了去找到mActivities這個欄位就好了,是的,因為這個欄位儲存了所有的Activity物件,拿到這個欄位的值就可以知道有哪些Activity了,而那些Activity都是使用者停留過的,這也是一個解決方法。

  1. //儲存的Activity的表示物件ActivityClientRecord
  2. static final class ActivityClientRecord {
  3. //唯一表示
  4. IBinder token;
  5. int ident;
  6. Intent intent;
  7. String referrer;
  8. IVoiceInteractor voiceInteractor;
  9. Bundle state;
  10. PersistableBundle persistentState;
  11. //這裡儲存了真正的Activity物件
  12. Activity activity;
  13. Window window;
  14. Activity parent;
  15. String embeddedID;
  16. Activity.NonConfigurationInstances lastNonConfigurationInstances;
  17. boolean paused;
  18. boolean stopped;
  19. boolean hideForNow;
  20. Configuration newConfig;
  21. Configuration createdConfig;
  22. Configuration overrideConfig;
  23. // Used for consolidating configs before sending on to Activity.
  24. private Configuration tmpConfig = new Configuration();
  25. ActivityClientRecord nextIdle;
  26. ProfilerInfo profilerInfo;
  27. ActivityInfo activityInfo;
  28. CompatibilityInfo compatInfo;
  29. LoadedApk packageInfo;
  30. List<ResultInfo> pendingResults;
  31. List<ReferrerIntent> pendingIntents;
  32. boolean startsNotResumed;
  33. boolean isForward;
  34. int pendingConfigChanges;
  35. boolean onlyLocalRequest;
  36. View mPendingRemoveWindow;
  37. WindowManager mPendingRemoveWindowManager;
  38. }

ActivityClientRecord是ActivtityThread的一個內部類,這個ActivityClientRecord是傳入AMS的一個標誌來的,裡面攜帶了很多資訊,上面的程式碼可以看出,其中有一個Activity物件,裡面的Activity就是真正的Activity例項了,通過或者它,可以知道使用者去哪些頁面,當然可能比較繁瑣,但這種方法是可行的。

下面重點分析ApplicationThread,首先它不是一個執行緒,而是一個Binder物件,

  1. private class ApplicationThread extends ApplicationThreadNative {
  2. //省略一些程式碼
  3. //準備暫停Activity
  4. public final void schedulePauseActivity(IBinder token, boolean finished,
  5. boolean userLeaving, int configChanges, boolean dontReport) {
  6. sendMessage(
  7. finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
  8. token,
  9. (userLeaving ? 1 : 0) | (dontReport ? 2 : 0),
  10. configChanges);
  11. }
  12. public final void scheduleStopActivity(IBinder token, boolean showWindow,
  13. int configChanges) {
  14. sendMessage(
  15. showWindow ? H.STOP_ACTIVITY_SHOW : H.STOP_ACTIVITY_HIDE,
  16. token, 0, configChanges);
  17. }
  18. //省略一些程式碼
  19. public abstract class ApplicationThreadNative extends Binder
  20. implements IApplicationThread
  21. /**
  22. * Cast a Binder object into an application thread interface, generating
  23. * a proxy if needed.
  24. */
  25. static public IApplicationThread asInterface(IBinder obj) {
  26. if (obj == null) {
  27. return null;
  28. }
  29. IApplicationThread in =
  30. (IApplicationThread)obj.queryLocalInterface(descriptor);
  31. if (in != null) {
  32. return in;
  33. }
  34. //不同程序,生成一個代理物件具體幹活
  35. return new ApplicationThreadProxy(obj);
  36. }

看到這裡,我想熟悉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類,下面是一些變數:

  1. private class H extends Handler {
  2. //啟動Activity
  3. public static final int LAUNCH_ACTIVITY = 100;
  4. //暫停Activity
  5. public static final int PAUSE_ACTIVITY = 101;
  6. public static final int PAUSE_ACTIVITY_FINISHING= 102;
  7. public static final int STOP_ACTIVITY_SHOW = 103;
  8. public static final int STOP_ACTIVITY_HIDE = 104;
  9. public static final int SHOW_WINDOW = 105;
  10. ..........
  11. }

可以看到,裡面的常量表示的是具體操作的what值,當然了,其他操作也有對應的,不一一列出了,然後重寫了handMessage方法,下面是程式碼:

  1. public void handleMessage(Message msg) {
  2. if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
  3. switch (msg.what) {
  4. case LAUNCH_ACTIVITY: {
  5. Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
  6. final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
  7. r.packageInfo = getPackageInfoNoCheck(
  8. r.activityInfo.applicationInfo, r.compatInfo);
  9. handleLaunchActivity(r, null);
  10. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
  11. }

當然了,其他都是這樣處理的,不一一列出。看到沒,LAUNCH_ACTIVITY這個是處理啟動Activity的,接著呼叫了handleLaunchActivity方法,handleLaunchActivity呼叫了建立Activity的方法performLaunchActivity,

  1. Activity activity = null;
  2. try {
  3. java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
  4. activity = mInstrumentation.newActivity(
  5. cl, component.getClassName(), r.intent);
  6. StrictMode.incrementExpectedActivityCount(activity.getClass());
  7. r.intent.setExtrasClassLoader(cl);
  8. r.intent.prepareToEnterProcess();
  9. if (r.state != null) {
  10. r.state.setClassLoader(cl);
  11. }
  12. }

看到沒,Activity其實就一個普普通通的Java物件,利用反射建立,然後由ClassLoader載入進去,之後由框架層的呼叫,從而具有了生命週期,成為了一個元件,從而也可以知道在外掛化中,僅僅載入Activity是不行的,還必須交給框架層去呼叫才具有生命力,不然沒意義,當然了,不僅是Activity,其實,Service,BroadCase,等都是這樣由反射建立,然後載入由框架層呼叫的,無一例外

看到沒,建立Activity之後,會呼叫attach方法繫結,然後判斷是否設定主題,如果有的話就設定主題,然後再呼叫mInstrumentation的callActivityOnCreate,這裡其實就是呼叫了Activity的onCreate方法,Activity的建立也是由Instrumentation建立的,有關的Instrumentation的說明,請看我的上一篇文章:

http://blog.csdn.net/shifuhetudi/article/details/52078445

至此,onCreate被呼叫了,

  1. if (!r.activity.mFinished) {
  2. //執行onStart方法
  3. activity.performStart();
  4. r.stopped = false;
  5. }
  6. if (!r.activity.mFinished) {
  7. if (r.isPersistable()) {
  8. if (r.state != null || r.persistentState != null) {
  9. mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
  10. r.persistentState);
  11. }
  12. } else if (r.state != null) {
  13. mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
  14. }
  15. }
  16. if (!r.activity.mFinished) {
  17. activity.mCalled = false;
  18. if (r.isPersistable()) {
  19. //OnPostCreate方法也是一個週期方法,只是一般開發用不到
  20. mInstrumentation.callActivityOnPostCreate(activity, r.state,
  21. r.persistentState);
  22. } else {
  23. mInstrumentation.callActivityOnPostCreate(activity, r.state);
  24. //這裡把Activity儲存起來,可以知道拿到mActivities就可以知道有哪些Activity了,也就是我上面提到的問題,如何統計使用者去過多少頁面呢,這個方法是可以實現的。
  25. mActivities.put(r.token, r);

上面可以看到執行完onCreate方法之後,執行onStart方法,然後執行onPostOnCreate方法

回到上面的呼叫handleLaunchActivity方法,在執行完Activity的onCreate,onStart生命週期方法之後,就來到
handleResumeActivity方法了,

  1. if (r.window == null && !a.mFinished && willBeVisible) {
  2. r.window = r.activity.getWindow();
  3. View decor = r.window.getDecorView();
  4. //一開始執行onResume的時候其實頁面是不可見的
  5. decor.setVisibility(View.INVISIBLE);
  6. ViewManager wm = a.getWindowManager();
  7. WindowManager.LayoutParams l = r.window.getAttributes();
  8. a.mDecor = decor;
  9. l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
  10. l.softInputMode |= forwardBit;
  11. if (a.mVisibleFromClient) {
  12. a.mWindowAdded = true;
  13. //這裡非常重要,把頂級devorView新增進去
  14. wm.addView(decor, l);
  15. }
  16. .......
  17. if (r.activity.mVisibleFromClient) {
  18. //執行到這裡,Activity才是真正對使用者可見
  19. r.activity.makeVisible();
  20. }
  21. // Tell the activity manager we have resumed.
  22. if (reallyResume) {
  23. try {
  24. //這裡通知AMS,Activity已經Resume了
  25. ActivityManagerNative.getDefault().activityResumed(token);
  26. } catch (RemoteException ex) {
  27. }
  28. }

看到沒,在執行onResume的時候,其實那時候是不可見的,直到執行完r.activity.mVisibleFromClient這個條件為真的時候才真正可見,從程式碼我們也可以看出,其中Activity就一個View新增到視窗而已,因而Activity本質上只是一個視窗而已,至此,Activity才真正對使用者可見,可以看到,上面的呼叫棧其實都是Handler訊息分發機制實現而已,一步一步來進行,通過IPC跟AMS通訊,有沒有覺得Android系統博大精深呢,本人以為正是由於Binder機制的存在,Android呼叫遠端程序如同呼叫本地程序一樣,來回自由,不得不佩服谷歌那幫神一般存在的程式設計師,不知道你有沒有這種想法呢?當然了,其他Handler分發訊息的呼叫都是一樣的,各位讀者可以自己去分析其他訊息的呼叫,其實,所有Activity的生命週期,包括其他元件的呼叫都由H 來負責分發呼叫。

  1. //獲取ActivityThread物件
  2. public static ActivityThread currentActivityThread() {
  3. return sCurrentActivityThread;
  4. }
  5. //獲取當前包名
  6. public static String currentPackageName() {
  7. ActivityThread am = currentActivityThread();
  8. return (am != null && am.mBoundApplication != null)
  9. ? am.mBoundApplication.appInfo.packageName : null;
  10. }
  11. //獲取當前程序名
  12. public static String currentProcessName() {
  13. ActivityThread am = currentActivityThread();
  14. return (am != null && am.mBoundApplication != null)
  15. ? am.mBoundApplication.processName : null;
  16. }
  17. //獲取當前Application物件
  18. public static Application currentApplication() {
  19. ActivityThread am = currentActivityThread();
  20. return am != null ? am.mInitialApplication : null;
  21. }
  22. //獲取LoadApk,LoadApk是一個Apk在記憶體中的表示,這個跟外掛化有關
  23. public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
  24. int flags) {
  25. return getPackageInfo(packageName, compatInfo, flags, UserHandle.myUserId());
  26. }
  • 上面的4個方法,其實作用很有用,在一些特殊場合的情況下用得到,後面的問題中會有這些方法的呼叫。

下面分析廣播在ActivityThread的呼叫,當然了廣播也一樣,首先通過IPC呼叫到AMS,然後由AMS排程返回到ActivityThread處理,下面是程式碼:

  1. private void handleReceiver(ReceiverData data) {
  2. LoadedApk packageInfo = getPackageInfoNoCheck(
  3. data.info.applicationInfo, data.compatInfo);
  4. IActivityManager mgr = ActivityManagerNative.getDefault();
  5. BroadcastReceiver receiver;
  6. try {
  7. java.lang.ClassLoader cl = packageInfo.getClassLoader();
  8. data.intent.setExtrasClassLoader(cl);
  9. data.intent.prepareToEnterProcess();
  10. data.setExtrasClassLoader(cl);
  11. //看到沒,廣播也是由反射建立的,跟Activity一樣
  12. receiver = (BroadcastReceiver)cl.loadClass(component).newInstance();
  13. }
  14. ContextImpl context = (ContextImpl)app.getBaseContext();
  15. sCurrentBroadcastIntent.set(data.intent);
  16. receiver.setPendingResult(data);
  17. //大家都知道的廣播回撥方法
  18. receiver.onReceive(context.getReceiverRestrictedContext(),
  19. data.intent);
  20. }

從上面可以看到,廣播本質跟Activity一樣,都是反射建立,唯一不同的是,Activity需要經過框架層的呼叫並具有了生命週期方法,而廣播沒有生命方法的概念,只是回調了一個onReceive方法,所以相對Activity,廣播比較容易理解,這裡先留下一個問題,為什麼廣播能夠跨程序呼叫呢,而EventBus,OTTO等事件匯流排就不能,其實根本原因在於廣播是由AMS系統程序統一管理,我們本地程序只是負責執行而已,而EventBus,OTTO那些事件匯流排的處理都是建立在同個程序之間的,因此他們不能實現跨程序傳送訊息,而廣播卻可,當然了AMS處理廣播也比較複雜,今天暫時不討論。

下面分析Service在ActivityThread的處理,程式碼如下:

  1. private void handleCreateService(CreateServiceData data) {
  2. LoadedApk packageInfo = getPackageInfoNoCheck(
  3. data.info.applicationInfo, data.compatInfo);
  4. Service service = null;
  5. try {
  6. java.lang.ClassLoader cl = packageInfo.getClassLoader();
  7. //看到沒,Service跟BroadCast,Activity一樣反射建立
  8. service = (Service) cl.loadClass(data.info.name).newInstance();
  9. }
  10. }
  11. try {
  12. if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);
  13. ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
  14. context.setOuterContext(service);
  15. Application app = packageInfo.makeApplication(false, mInstrumentation);
  16. service.attach(context, this, data.info.name, data.token, app,
  17. ActivityManagerNative.getDefault());
  18. //執行onCreate方法
  19. service.onCreate();
  20. //把Service儲存起來
  21. mServices.put(data.token, service);
  22. }
  23. }

可以看到Service的處理跟前面的廣播沒啥大區別,都是反射建立,然後回撥方法,當然還剩下最後一個ContentProvider,其實這玩意也是反射建立,當然了這個ContentProvider流程比較複雜,

  1. private IActivityManager.ContentProviderHolder installProvider(Context context,
  2. IActivityManager.ContentProviderHolder holder, ProviderInfo info,
  3. boolean noisy, boolean noReleaseNeeded, boolean stable) {
  4. ContentProvider localProvider = null;
  5. IContentProvider provider;
  6. if (holder == null || holder.provider == null) {
  7. if (DEBUG_PROVIDER || noisy) {
  8. Slog.d(TAG, "Loading provider " + info.authority + ": "
  9. + info.name);
  10. }
  11. Context c = null;
  12. ApplicationInfo ai = info.applicationInfo;
  13. if (context.getPackageName().equals(ai.packageName)) {
  14. c = context;
  15. } else if (mInitialApplication != null &&
  16. mInitialApplication.getPackageName().equals(ai.packageName)) {
  17. c = mInitialApplication;
  18. } else {
  19. try {
  20. c = context.createPackageContext(ai.packageName,
  21. Context.CONTEXT_INCLUDE_CODE);
  22. } catch (PackageManager.NameNotFoundException e) {
  23. // Ignore
  24. }
  25. }
  26. if (c == null) {
  27. Slog.w(TAG, "Unable to get context for package " +
  28. ai.packageName +
  29. " while loading content provider " +
  30. info.name);
  31. return null;
  32. }
  33. try {
  34. final java.lang.ClassLoader cl = c.getClassLoader();
  35. //這裡是重點反射建立
  36. localProvider = (ContentProvider)cl.
  37. loadClass(info.name).newInstance();
  38. provider = localProvider.getIContentProvider();
  39. //執行attachInfo方法
  40. localProvider.attachInfo(c, info);
  41. }

可以看到四大元件都是反射建立,並執行生命週期方法,而排程是由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中賦值,這樣就可以獲取了,沒錯,絕大部分的人是這樣做,而且好像也從來都沒有出錯過,是的,這種方法是目前大部分的人都會用,然而,這種方法其實是由限制的,當然了在普通的開發中還是沒問題的,然而如果不能接觸程式邏輯本身的話(在逆向等比較特殊的情況下),那麼上面的方法就無效了,那麼是不是就沒辦法了呢,不是的,上面的程式碼中曾經說過有個方法:

  1. public static Application currentApplication() {
  2. ActivityThread am = currentActivityThread();
  3. return am != null ? am.mInitialApplication : null;
  4. }
  • 可以看到是可以獲取的,可是ActivityThread是一個隱藏類,那怎麼辦呢,反射吧,下面是程式碼:
  1. private void getGlobalApplication()throws Exception{
  2. Class<?> clazz=Class.forName("android.app.ActivityThread");
  3. Method method=clazz.getDeclaredMethod("currentApplication");
  4. Application application= (Application) method.invoke(null);
  5. Log.d("[app]","通過反射主執行緒獲取的pplication為:"+application);
  6. }

結果如下:

可以看到成功獲取了,問題解決!

2.如何獲取Activity例項
可能有人會說不是可以getApplication物件嗎,那個方法獲取的是上下文物件,並不是真正的activity例項,那麼如何獲取呢,下面是其中一個方法:
首先在基類定義一個Activity變數,然後在onCreate中賦值給具體的子類,這樣子類獲取的Activity例項。
這種方法是最簡單,也是一般情況下用到的。

第二種方法:利用application的生命週期監聽方法ActivityLifecycleCallbacks,下面是具體的程式碼:



OK,也成功獲取了當前ActivityThread例項了。

第三種方法:

我們剛才在看ActivityThread的原始碼的時候,發現有這麼一個方法:

  1. public final Activity getActivity(IBinder token) {
  2. return mActivities.get(token).activity;
  3. }

你沒有看錯,這裡也有一個方法直接獲取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層是很好理解的,只需要記住,不同程序之間都是拿對方的代理物件進行幹活的),聽不懂是吧,下面看看程式碼吧:

  1. public abstract class ActivityManagerNative extends Binder implements IActivityManager
  2. {
  3. /**
  4. * Cast a Binder object into an activity manager interface, generating
  5. * a proxy if needed.
  6. */
  7. static public IActivityManager asInterface(IBinder obj) {
  8. if (obj == null) {
  9. return null;
  10. }
  11. IActivityManager in =
  12. (IActivityManager)obj.queryLocalInterface(descriptor);
  13. if (in != null) {
  14. //如果是本程序的話,之間返回來
  15. return in;
  16. }
  17. //如果是不同程序的話,就生成一個ActivityManagerProxy代理物件
  18. return new ActivityManagerProxy(obj);
  19. }
  20. private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
  21. protected IActivityManager create() {
  22. //通過ServiceManager獲取到了真正的ActivityManagerService服務
  23. IBinder b = ServiceManager.getService("activity");
  24. if (false) {
  25. Log.v("ActivityManager", "default service binder = " + b);
  26. }
  27. //這裡看到沒,呼叫asInterface把AMS傳進去轉換了一下了
  28. IActivityManager am = asInterface(b);
  29. if (false) {
  30. Log.v("ActivityManager", "default service = " + am);
  31. }
  32. //因此這裡返回來的是ActivityManagerProxy物件了,如果是不同程序的話,就是Binder機制中
  33. return am;
  34. }
  35. };
  36. }

我們只需要替換為我們自己的代理物件進行幹活,然後在activityResumed方法中進行攔截就好了,好了,說幹就幹,

  1. public static void hookActivityManagerService() throws Throwable {
  2. Class<?> activityManagerNativeClass=Class.forName("android.app.ActivityManagerNative");
  3. //4.0以後,ActivityManagerNative有gDefault單例來進行儲存,這個程式碼中一看就知道了
  4. Field gDefaultField=activityManagerNativeClass.getDeclaredField("gDefault");
  5. gDefaultField.setAccessible(true);
  6. Object gDefault=gDefaultField.get(null);
  7. Class<?> singleton=Class.forName("android.util.Singleton");
  8. //mInstance其實就是真正的一個物件
  9. Field mInstance=singleton.getDeclaredField("mInstance");
  10. mInstance.setAccessible(true);
  11. //真正的物件,就是幹活的物件啦,其實就是ActivityManagerProxy而已啦
  12. Object originalIActivityManager=mInstance.get(gDefault);
  13. Log.d("[app]","originalIActivityManager="+originalIActivityManager);
  14. //通過動態代理生成一個介面的物件
  15. Class<?> iActivityManagerInterface=Class.forName("android.app.IActivityManager");
  16. Object object= Proxy.newProxyInstance(iActivityManagerInterface.getClassLoader(),
  17. new Class[]{iActivityManagerInterface},new IActivityManagerServiceHandler(originalIActivityManager));
  18. //這裡偷樑換柱,替換為我們自己的物件進行幹活就好了
  19. mInstance.set(gDefault,object);
  20. Log.d("[app]","Hook AMS成功");
  21. }

IActivityManagerServiceHandler實現類動態代理介面InvocationHandler,在裡面攔截了activityResumed方法而已,攔截之後拿到Token,然後呼叫反射方法就可以獲取Activity的例項啦,下面是具體的程式碼,比較簡單,

  1. public class IActivityManagerServiceHandler implements InvocationHandler {
  2. private Object base;
  3. public IActivityManagerServiceHandler(Object base) {
  4. this.base = base;
  5. }
  6. @Override
  7. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  8. //判斷是不是activityResumed,如果是的話,那麼攔截引數,然後反射獲取例項就好
  9. if (method.getName().equals("activityResumed")){
  10. //這裡拿到想要的IBinder啦,就是token
  11. IBinder iBinder= (IBinder) args[0];
  12. Log.d("[app]","執行activityResumed方法了,引數toke為"+iBinder);
  13. Class<?> clazz=Class.forName("android.app.ActivityThread");
  14. Method method1=clazz.getDeclaredMethod("currentActivityThread");
  15. Object object=method1.invoke(null);
  16. Method getActivity=clazz.getDeclaredMethod("getActivity",IBinder.class);
  17. Activity mActivity= (Activity) getActivity.invoke(object,iBinder);
  18. Log.d("[app]","Hook AMS以後:當前的Activity為:"+mActivity);
  19. }
  20. return method.invoke(base,args);
  21. }
  22. }

然後在application中進行注入就好了,好了,下面列印看看吧,

可以看到成功了吧,呵呵,欺騙系統的感覺如何啊,順便說句,360外掛化的核心思想就是瞞天過海,欺騙系統,通過把系統完的團團轉,從而實現外掛系統的天衣無縫,當然需要處理一些相容性問題,OK,上面的問題也解決了。

3.有辦法干預Activity的啟動或者其他過程嗎,常規下是沒辦法的,因為這是框架層的排程,再呼叫Activity生命週期方法的時候,其實已經是很晚時機了,已經沒辦法再進一步操作了,也就是說在應用層上是無法干預這些行為的,當然了,你可以在框架層排程的時候,半路殺出個程咬金來就好了,具體的見我的上一篇文章:

http://blog.csdn.net/shifuhetudi/article/details/52078445

4.有辦法在啟動Activity的時候彈出一個土司嗎,或者做其他的事情,就是在執行LaunchActivity方法的時候彈土司或者其他事情?
額,常規下是沒辦法的,有人會說我在onCreate中彈出來,呵呵,這就違背了我的條件,是在LaunchActivity的時候,就是在AMS給ActivityThread發訊息啟動Activiy的時候做一些事情,我們都知道這個過程是由Handler傳送訊息來實現的,可是通過Handler處理訊息的程式碼來看,其實是有順序的,下面是Handler處理訊息的程式碼:

  1. /**
  2. * Handle system messages here.
  3. */
  4. public void dispatchMessage(Message msg) {
  5. if (msg.callback != null) {
  6. handleCallback(msg);
  7. } else {
  8. if (mCallback != null) {
  9. if (mCallback.handleMessage(msg)) {
  10. return;
  11. }
  12. }
  13. handleMessage(msg);
  14. }
  15. }

看到了嗎,handler處理訊息的時候,首先去檢查是否實現了callback介面,如果有實現的的話,那麼會直接執行介面方法,然後才是handleMessage方法,最後才是執行重寫的handleMessage方法,我們一般大部分時候都是重寫了handleMessage方法,而ActivityThread主執行緒用的正是重寫的方法,這種方法的優先順序是最低的,我們完全可以實現介面來替換掉系統Handler的處理過程,下面請看程式碼:

  1. public static void hookHandler(Context context) throws Exception {
  2. Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
  3. Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
  4. currentActivityThreadMethod.setAccessible(true);
  5. //獲取主執行緒物件
  6. Object activityThread = currentActivityThreadMethod.invoke(null);
  7. //獲取mH欄位
  8. Field mH = activityThreadClass.getDeclaredField("mH");
  9. mH.setAccessible(true);
  10. //獲取Handler
  11. Handler handler = (Handler) mH.get(activityThread);
  12. //獲取原始的mCallBack欄位
  13. Field mCallBack = Handler.class.getDeclaredField("mCallback");
  14. mCallBack.setAccessible(true);
  15. //這裡設定了我們自己實現了介面的CallBack物件
  16. mCallBack.set(handler, new CustomHandler(context, handler));
  17. }

當然還有CustomHandler類,這個類實現了Callback介面,一樣可以攔截方法

  1. public class CustomHandler implements Callback {
  2. //這個100一般情況下最好也反射獲取,當然了你也可以直接寫死,跟系統的保持一致就好了
  3. public static final int LAUNCH_ACTIVITY = 100;
  4. private Handler origin;
  5. private Context context;
  6. public CustomHandler(Context origin, Handler context) {
  7. this.context = origin;
  8. this.origin = context;
  9. }
  10. @Override
  11. public boolean handleMessage(Message msg) {
  12. if (msg.what == LAUNCH_ACTIVITY) {
  13. //這樣每次啟動的時候便會彈出土司來
  14. Toast.makeText(
  15. context.getApplicationContext(),
  16. "hello,I am going launch", Toast.LENGTH_SHORT).show();
  17. }
  18. origin.handleMessage(msg);
  19. return false;
  20. }
  21. }

寫完之後在application中注入就好了,OK,你可以去測試一下,看看是不是每次啟動Activity的時候都會彈出hello,I am going launch這個字串。

當然除了這些比較特殊的問題之外,一些時候,特別是外掛化的時候,經常遇到這種問題,這些問題往往在應用層是無法攔截的,因為到達應用層的時候,呼叫鏈已經呼叫完畢,沒機會去插手了,這種時候就可以考慮這些比較特殊的方法了,當然啦,開發中,哪種最快解決問題用哪種吧。