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

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;
        Activity.NonConfigurationInstances lastNonConfigurationInstances;
        boolean paused;
        boolean stopped;
        boolean hideForNow;
        Configuration newConfig;
        Configuration createdConfig;
        Configuration overrideConfig;
        // Used for consolidating configs before sending on to Activity.
        private Configuration tmpConfig = new Configuration();
        ActivityClientRecord nextIdle;

        ProfilerInfo profilerInfo;

        ActivityInfo activityInfo;
        CompatibilityInfo compatInfo;
        LoadedApk packageInfo;

        List<ResultInfo> pendingResults;
        List<ReferrerIntent> pendingIntents;

        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的處理跟前面的廣播沒啥大區別,都是反射建立,然後回撥方法,當然還剩下最後一個ContentProcider,其實這玩意也是反射建立,當然了這個ContentProcider流程比較複雜,

  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;
    }

    @Override
    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;
    }

    @Override
    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這個字串。

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

今天就寫到這裡吧,小弟水平有限,不足之處敬請指出,感謝大家閱讀。

相關推薦

Android執行(ActivityThread)原始碼分析

在寫這篇部落格之前,先丟擲一個問題,安卓應用程式的入口是什麼呢?我想不少人可能回答說:application的onCreate方法,其實並不是的,即使是application,也有一個方法比onCreate先執行,這個方法就是attachBaseContext(Context

Android執行---AsyncTask原始碼分析

Android多執行緒—AsyncTask使用原始碼分析 一、前言 上篇文章對 HandlerThread 的原始碼進行了分析,相信大家對 HandlerThread 有了一定的認識。如果我們需要執行一個非同步任務,並且執行完畢之後在主執行緒中更新

Android執行向子執行中傳送資訊

主要用到了Handler類,Looper類和Message類 先介紹下這幾個類 Looper類,是用來為一個執行緒開啟一個訊息佇列,預設情況下Android下新開啟的執行緒沒有開啟訊息佇列的,除了主執行緒外,主執行緒系統會預設為其開啟一個訊息佇列;looper是通過MessageQueu

android執行中Looper.loop()為什麼不會造成程式ANR

程式入口為ActivityThread的main方法,原始碼如下: frameworks/base/core/java/android/app/ActivityThread.java public static void main(String[] args) { S

[.net 多執行]ConcurrentBag原始碼分析

ConcurrentBag根據操作執行緒,對不同執行緒分配不同的佇列進行資料操作。這樣,每個佇列只有一個執行緒在操作,不會發生併發問題。其內部實現運用了net4.0新加入的ThreadLocal執行緒本地儲存功能。各個佇列間通過連結串列維護。 其內部結構如下:   1、獲取執行緒本地佇列:

tomcat的NIO執行模型原始碼分析

1 tomcat8的併發引數控制 這種問題其實到官方文件上檢視一番就可以知道,tomcat很早的版本還是使用的BIO,之後就支援NIO了,具體版本我也不記得了,有興趣的自己可以去查下。本篇的tomcat版本是tomcat8.5。可以到這裡看下tomcat8.5的配置引數 我們先來簡單回顧下目前一般的N

關於android執行不能訪問網路異常NetworkOnMainThreadException

獲取網路圖片: //圖片處理 ImageGetter imgGetter2 = new Html.ImageGetter() { public Drawable getDrawable(Stri

Android執行中延時處理

Android對UI主執行緒開啟了實時監聽,Activity Manager和WindowManager系統服務一旦監聽到主執行緒超過10秒沒有響應操作,就會丟擲ANR,所以,在UI主執行緒中不能直接呼叫Thread.sleep方法去延時,超過10秒就根本不會執

Android執行不能訪問網路異常解決辦法 NetworkOnMainThreadException錯誤

Android Activity主執行緒預設情況下不允許訪問網路 轉自http://www.cnblogs.com/lyroge/p/3837902.html 從兩個方面說下這個問題: 1. 不讓訪問網路的原因 2. 解決該問題的辦法 不讓訪問網路的原因: 由於對於網

關於redis的單執行與後臺執行原始碼分析

前言: 通常大家都會說redis是單執行緒的,這個理解其實也沒錯。 redis的命令處理基本上都是單執行緒處理的,除了個別任務會fork子程序進行處理。 分析版本:redis 3.0.7 其實r

基於Netty3的RPC架構筆記3之執行模型原始碼分析

      隨著使用者量上升,專案的架構也在不斷的升級,由最開始的MVC的垂直架構(傳統專案)到RPC架構(webservice,rest,netty,mina),再到SOA模型(dubbo),再到最近的微服務,又比如Tomcat6之前的IO模型都是BIO 也就是阻塞IO,

android執行和子執行的區別

android 主執行緒和子執行緒有什麼區別本文較為深入的分析了android中UI主執行緒與子執行緒。分享給大家供大家參考。具體如下:在一個Android 程式開始執行的時候,會單獨啟動一個Process。預設的情況下,所有這個程式中的Activity或者Service(S

Android執行裡不允許網路操作

Keywords: Android UI執行緒/主執行緒 PENALTY_DEATH_ON_NETWORKAndroid API > 9 (Honeycomb及之後)版本里,對UI執行緒/主執行緒裡是不允許聯網操作的,如果有網路操作,會丟擲NetworkOnMainTh

執行池之ThreadPoolExecutor執行原始碼分析筆記

1.執行緒池的作用 一方面當執行大量非同步任務時候執行緒池能夠提供較好的效能,在不使用執行緒池的時候,每當需要執行非同步任務時候是直接 new 一執行緒進行執行,而執行緒的建立和銷燬是需要開銷的。使用執行緒池時候,執行緒池裡面的執行緒是可複用的,不會每次執行非同步任務時候都重新建立和銷燬執行緒。 另一方面

執行池之ScheduledThreadPoolExecutor執行原始碼分析筆記

1.ScheduledThreadPoolExecutor 整體結構剖析。 1.1類圖介紹   根據上面類圖圖可以看到Executor其實是一個工具類,裡面提供了好多靜態方法,根據使用者選擇返回不同的執行緒池例項。可以看到ScheduledThreadPoolExecutor&n

執行原始碼分析

## 概述 在 java 中,執行緒池 ThreadPoolExecutor 是一個繞不過去的類,它是享元模式思想的體現,通過在容器中建立一定數量的執行緒加以重複利用,從而避免頻繁建立執行緒帶來的額外開銷。一個設定合理的執行緒池可以提高任務響應的速度,並且避免執行緒數超過硬體能力帶來的意外情況。 在本文,

Android ActivityThread 執行或UI執行 簡介

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

[android原始碼日記]-android執行為什麼不會因為Looper.loop()裡面的死迴圈卡死?

一下是按照我自己的理解做的一個記錄。 顯而易見,在我們提出這個問題的時候,我們知道安卓主執行緒(又叫UI執行緒)在應用程式啟動ActivityThread的時候,就依次呼叫 Looper.prepareMainLooper(); Looper.loop();了

原始碼分析之: WebSocket 和 是如何將收發到的訊息投遞給cocos執行

-->websocket的3種使用場景: 1)h5瀏覽器中websocket由瀏覽器提供 2)node.js中,可以使用ws模組寫伺服器 3)native app中,可以使用c++版本的websocket匯出c++介面給cocos creator客戶端使用  

我的android執行程式設計之路(2)之RxJava Schedulers原始碼分析

寫在伊始 上一篇介紹了執行緒的一些基礎知識和工作這麼久以後對於多執行緒部分的使用經驗之路,這篇主要對RxJava執行緒控制部分進行分析。 RxJava(本文就RxJava2.0分析) 說實話,近一年多一直在用rxjava進行專案架構的編寫及封裝及一些非