Hook startActivity()函數追加一條日誌

目的

希望程序員每次調用startActivity()的時候,可以多打印一條日誌。

實現原理

startActivity() 執行過程中,會調用到ActivityThread的成員對象mInstrumentation的 execStartActivity()函數,於是我們可以自定義一個EvilInstrumentation(extends Instrumentation )類,重寫execStartActivity()函數,追加實現自己的邏輯,然後通過反射把ActivityThread的成員對象mInstrumentation替換爲我們自己寫的EvilInstrumentation的對象,達到偷樑換柱的目的。

代碼實現

App.java

package com.qunar.yuzhiyun.hook;

import android.app.Application;
import android.app.Instrumentation;
import android.content.Context;

import java.lang.reflect.Field;

/**
 * Created by yuzhiyun on 17/8/11.
 */

public class App extends Application {

    @Override
    protected void attachBaseContext(Context base)  {
        super.attachBaseContext(base);

        // 先獲取到當前的ActivityThread對象
        Class activityThreadClass = null;
        try {
            activityThreadClass = Class.forName("android.app.ActivityThread");

            Field currentActivityThreadField = activityThreadClass.getDeclaredField("sCurrentActivityThread");
            currentActivityThreadField.setAccessible(true);
            //傳給get和set方法的參數必須是擁有該字段的實例。如果字段是靜態字段(public static …),則傳null作爲get和set方法的參數
            Object currentActivityThread = currentActivityThreadField.get(null);
            // 拿到原始的 mInstrumentation字段
            Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation");
            mInstrumentationField.setAccessible(true);
            Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread);
            // 創建代理對象
            Instrumentation evilInstrumentation = new EvilInstrumentation(mInstrumentation);
            // 偷樑換柱
            mInstrumentationField.set(currentActivityThread, evilInstrumentation);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }

    }
}

EvilInstrumentation.java

package com.qunar.yuzhiyun.hook;

import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;

import java.lang.reflect.Method;

/**
 * Created by yuzhiyun on 17/8/11.
 */

public class EvilInstrumentation extends Instrumentation {
    private static final String TAG = "EvilInstrumentation";

    // ActivityThread中原始的對象, 保存起來
    Instrumentation mBase;

    public EvilInstrumentation(Instrumentation base) {
        mBase = base;
    }

    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {

        // Hook之前, XXX到此一遊!
        Log.d(TAG, "\n執行了startActivity, 參數如下: \n" + "who = [" + who + "], " +
                "\ncontextThread = [" + contextThread + "], \ntoken = [" + token + "], " +
                "\ntarget = [" + target + "], \nintent = [" + intent +
                "], \nrequestCode = [" + requestCode + "], \noptions = [" + options + "]");

        // 開始調用原始的方法, 調不調用隨你,但是不調用的話, 所有的startActivity都失效了.
        // 由於這個方法是隱藏的,因此需要使用反射調用;首先找到這個方法
        try {
            Method execStartActivity = Instrumentation.class.getDeclaredMethod(
                    "execStartActivity",
                    Context.class, IBinder.class, IBinder.class, Activity.class,
                    Intent.class, int.class, Bundle.class);
            execStartActivity.setAccessible(true);
            return (ActivityResult) execStartActivity.invoke(mBase, who,
                    contextThread, token, target, intent, requestCode, options);
        } catch (Exception e) {
            // 某該死的rom修改了  需要手動適配
            throw new RuntimeException("do not support!!! pls adapt it");
        }
    }

}

參考自http://www.jianshu.com/p/b30ea19c444b 非常感謝,樓主講解的非常清晰!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章