成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專(zhuān)欄INFORMATION COLUMN

Android 插件化原理學(xué)習(xí) —— Hook 機(jī)制之動(dòng)態(tài)代理

gekylin / 3239人閱讀

摘要:什么樣的對(duì)象容易找到靜態(tài)變量和單例。在一個(gè)進(jìn)程之內(nèi),靜態(tài)變量和單例變量是相對(duì)不容易發(fā)生變化的,因此非常容易定位,而普通的對(duì)象則要么無(wú)法標(biāo)志,要么容易改變。

前言

為了實(shí)現(xiàn) App 的快速迭代更新,基于 H5 Hybrid 的解決方案有很多,由于 webview 本身的性能問(wèn)題,也隨之出現(xiàn)了很多基于 JS 引擎實(shí)現(xiàn)的原生渲染的方案,例如 React Native、weex 等,而國(guó)內(nèi)一線大廠基本上主要還是 Android 插件化解決大部分的更新問(wèn)題,對(duì)于部分是采用 webview 或者 React Native 這種方案,而對(duì)于 Android 插件化采用的技術(shù)對(duì)于 Android Framewrok 的理解要求很高,真正實(shí)現(xiàn)落地的方案都還是有難度,對(duì)于非 Android Native 開(kāi)發(fā)的人員更是有技術(shù)門(mén)檻。插件化可以很好的解決 Android 運(yùn)行的一些問(wèn)題,本文站在學(xué)習(xí)者的角度去嘗試?yán)斫獠寮降捉鉀Q了什么問(wèn)題。

插件化框架

如下是主流的插件化框架之間的對(duì)比:

特性 DynamicLoadApk DynamicAPK Small DroidPlugin VirtualAPK
支持四大組件 只支持 Activity 只支持 Activity 只支持 Activity 全支持 全支持
無(wú)需在宿主 manifest 中預(yù)注冊(cè) ×
插件可以依賴(lài)宿主 ×
支持 PendingIntent × × ×
Android 特性支持 大部分 大部分 大部分 幾乎全部 幾乎全部
兼容性適配 一般 一般 中等
插件構(gòu)建 無(wú) 部署 aapt Gradle 插件 無(wú) Gradle 插件
代理模式

代理模式是為一個(gè)對(duì)象提供一個(gè)代用品或占位符,以便控制對(duì)它的訪問(wèn)。使用代理可以屏蔽內(nèi)部實(shí)現(xiàn)細(xì)節(jié),后續(xù)內(nèi)部有變動(dòng)對(duì)于外部調(diào)用者來(lái)說(shuō)是封閉的,符合開(kāi)放-封閉原則。用戶可以放心地請(qǐng)求代理,他只關(guān)心是否能得到想要的結(jié)果。在任何使用本體的地方都可以替換成使用代理,從而實(shí)現(xiàn)實(shí)現(xiàn)和調(diào)用松耦合。

不用代理模式:

使用代理模式:

靜態(tài)代理

例如我們有兩個(gè)接口:

// Subject1.java
public interface Subject1 {
  void method1();
  void method2();
}

// Subject2.java
public interface Subject2 {
  void method1();
  void method2();
  void method3();
}

我們分別實(shí)現(xiàn)這兩個(gè)接口:

// RealSubject1.java
public class RealSubject1 implements Subject1 {
  @Override
  public void method1() {
    Logger.i(RealSubject1.class, "我是RealSubject1的方法1");
  }

  @Override
  public void method2() {
    Logger.i(RealSubject1.class, "我是RealSubject1的方法2");
  }
}

// RealSubject2.java
public class RealSubject2 implements Subject2 {
  @Override
  public void method1() {
    Logger.i(RealSubject2.class, "我是RealSubject2的方法1");
  }

  @Override
  public void method2() {
    Logger.i(RealSubject2.class, "我是RealSubject2的方法2");
  }

  @Override
  public void method3() {
    Logger.i(RealSubject2.class, "我是RealSubject2的方法3");
  }
}

如果不使用代理模式,我們一般會(huì)直接實(shí)例化 RealSubject1 和 RealSubject2 類(lèi)對(duì)象。使用代理,我們一般都需要建立一個(gè)代理類(lèi)。在 Java 等語(yǔ)言中,代理和本體都需要顯式地實(shí)現(xiàn)同一個(gè)接口,一方面接口保證了它們會(huì)擁 有同樣的方法,另一方面,面向接口編程迎合依賴(lài)倒置原則,通過(guò)接口進(jìn)行向上轉(zhuǎn)型,從而避開(kāi) 編譯器的類(lèi)型檢查,代理和本體將來(lái)可以被替換使用。

/**
 * 靜態(tài)代理類(lèi)(為了保持行為的一致性,代理類(lèi)和委托類(lèi)通常會(huì)實(shí)現(xiàn)相同的接口)
 * ProxySubject1.java
 */
public class ProxySubject1 implements Subject1 {
  private Subject1 subject1;

  public ProxySubject1(Subject1 subject1) {
    this.subject1 = subject1;
  }

  @Override
  public void method1() {
    Logger.i(ProxySubject1.class, "我是代理,我會(huì)在執(zhí)行實(shí)體方法1之前先做一些預(yù)處理的工作");
    subject1.method1();
  }

  @Override
  public void method2() {
    Logger.i(ProxySubject1.class, "我是代理,我會(huì)在執(zhí)行實(shí)體方法2之前先做一些預(yù)處理的工作");
    subject1.method2();
  }
}

使用代理后我們對(duì) RealSubject1 的操作換成對(duì) ProxySubject1 對(duì)象的操作,如下:

ProxySubject1 proxySubject1 = new ProxySubject1(new RealSubject1());
proxySubject1.method1();
proxySubject1.method2();

結(jié)果:
[ProxySubject1] : 我是代理,我會(huì)在執(zhí)行實(shí)體方法1之前先做一些預(yù)處理的工作
[RealSubject1] : 我是RealSubject1的方法1
[ProxySubject1] : 我是代理,我會(huì)在執(zhí)行實(shí)體方法2之前先做一些預(yù)處理的工作
[RealSubject1] : 我是RealSubject1的方法2
[ProxySubject2] : 我是代理,我會(huì)在執(zhí)行實(shí)體方法1之前先做一些預(yù)處理的工作
[RealSubject2] : 我是RealSubject2的方法1
[ProxySubject2] : 我是代理,我會(huì)在執(zhí)行實(shí)體方法2之前先做一些預(yù)處理的工作
[RealSubject2] : 我是RealSubject2的方法2

顯然當(dāng)我們想代理 RealSubject2 按照這種方式我們?nèi)匀恍枰⒁粋€(gè)類(lèi)去處理,這也是靜態(tài)代理的局限性。如果寫(xiě)一個(gè)代理類(lèi)就能對(duì)上面兩個(gè)都能代理就好了,動(dòng)態(tài)代理就解決了這個(gè)問(wèn)題。

動(dòng)態(tài)代理

在 java 的動(dòng)態(tài)代理機(jī)制中,有兩個(gè)重要的類(lèi)或接口,一個(gè)是 InvocationHandler(Interface)、另一個(gè)則是 Proxy(Class),這一個(gè)類(lèi)和接口是實(shí)現(xiàn)我們動(dòng)態(tài)代理所必須用到的。

動(dòng)態(tài)代理的步驟:

寫(xiě)一個(gè) InvocationHandler 的實(shí)現(xiàn)類(lèi),并實(shí)現(xiàn) invoke 方法,return method.invoke(...);。

/**
  * @param proxy 指代我們所代理的那個(gè)真實(shí)對(duì)象
  * @param method 指代的是我們所要調(diào)用真實(shí)對(duì)象的某個(gè)方法的Method對(duì)象
  * @param args 指代的是調(diào)用真實(shí)對(duì)象某個(gè)方法時(shí)接受的參數(shù)
  */
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

每一個(gè)動(dòng)態(tài)代理類(lèi)都必須要實(shí)現(xiàn) InvocationHandler 這個(gè)接口,并且每個(gè)代理類(lèi)的實(shí)例都關(guān)聯(lián)到了一個(gè) handler,當(dāng)我們通過(guò)代理對(duì)象調(diào)用一個(gè)方法的時(shí)候,這個(gè)方法的調(diào)用就會(huì)被轉(zhuǎn)發(fā)為由 InvocationHandler 這個(gè)接口的 invoke 方法來(lái)進(jìn)行調(diào)用。

使用 Proxy 類(lèi)的 newProxyInstance 方法生成一個(gè)代理對(duì)象。例如: 生成 Subject1 的代理對(duì)象,注意第三個(gè)參數(shù)中要將一個(gè)實(shí)體對(duì)象傳入。

/**
  * @param loader 一個(gè)ClassLoader對(duì)象,定義了由哪個(gè)ClassLoader對(duì)象來(lái)對(duì)生成的代理對(duì)象進(jìn)行加載
  * @param interfaces 一個(gè)Interface對(duì)象的數(shù)組,表示的是我將要給我需要代理的對(duì)象提供一組什么接口,如果我提供了一組接口給它,那么這個(gè)代理對(duì)象就宣稱(chēng)實(shí)現(xiàn)了該接口(多態(tài)),這樣我就能調(diào)用這組接口中的方法了
  * @param h 一個(gè)InvocationHandler對(duì)象,表示的是當(dāng)我這個(gè)動(dòng)態(tài)代理對(duì)象在調(diào)用方法的時(shí)候,會(huì)關(guān)聯(lián)到哪一個(gè)InvocationHandler對(duì)象上
  */
public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) throws IllegalArgumentException

例如:

Proxy.newProxyInstance(
  Subject1.class.getClassLoader(),
  new Class[] {Subject1.class},
  new DynamicProxyHandler(new RealSubject1())
);

Proxy 這個(gè)類(lèi)的作用就是用來(lái)動(dòng)態(tài)創(chuàng)建一個(gè)代理對(duì)象的類(lèi),它提供了許多的方法,但是我們用的最多的就是 newProxyInstance 這個(gè)方法。

使用動(dòng)態(tài)代理完成上述靜態(tài)代理中的功能:

public class DynamicProxyHandler implements InvocationHandler {
  private Object object;

  public DynamicProxyHandler(Object object) {
    this.object = object;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    Logger.i(DynamicProxyHandler.class, "我正在動(dòng)態(tài)代理[" + object.getClass().getSimpleName() + "]的[" + method.getName() + "]方法");
    return method.invoke(object, args);
  }

  /**
    * 調(diào)用Proxy.newProxyInstance即可生成一個(gè)代理對(duì)象
    *
    * @param object
    * @return
    */
  public static Object newProxyInstance(Object object) {
    // 傳入被代理對(duì)象的classloader實(shí)現(xiàn)的接口, 還有DynamicProxyHandler的對(duì)象即可。
    return Proxy.newProxyInstance(object.getClass().getClassLoader(),
      object.getClass().getInterfaces(),
      new DynamicProxyHandler(object));
  }
}

動(dòng)態(tài)代理調(diào)用如下:

Subject1 dynamicProxyHandler1 = (Subject1) DynamicProxyHandler.newProxyInstance(new RealSubject1());
dynamicProxyHandler1.method1();
dynamicProxyHandler1.method2();
初識(shí) Hook 機(jī)制

上述我們對(duì)一個(gè)方法的調(diào)用采用了動(dòng)態(tài)代理的辦法,如果我們自己創(chuàng)建代理對(duì)象,然后把原始對(duì)象替換為我們的代理對(duì)象,那么就可以在這個(gè)代理對(duì)象為所欲為了,修改參數(shù),替換返回值,我們稱(chēng)之為 Hook。下面我們 Hook 掉 startActivity 這個(gè)方法,使得每次調(diào)用這個(gè)方法之前輸出一條日志;當(dāng)然,這個(gè)輸入日志有點(diǎn)點(diǎn)弱,只是為了展示原理;只要你想,你想可以替換參數(shù),攔截這個(gè) startActivity 過(guò)程,使得調(diào)用它導(dǎo)致啟動(dòng)某個(gè)別的 Activity,指鹿為馬!

首先我們得找到被 Hook 的對(duì)象,我稱(chēng)之為 Hook 點(diǎn);什么樣的對(duì)象比較好 Hook 呢?自然是容易找到的對(duì)象。什么樣的對(duì)象容易找到?靜態(tài)變量和單例。在一個(gè)進(jìn)程之內(nèi),靜態(tài)變量和單例變量是相對(duì)不容易發(fā)生變化的,因此非常容易定位,而普通的對(duì)象則要么無(wú)法標(biāo)志,要么容易改變。我們根據(jù)這個(gè)原則找到所謂的 Hook 點(diǎn)。

對(duì)于 startActivity 過(guò)程有兩種方式:Context.startActivity 和 Activity.startActivity。這里暫不分析其中的區(qū)別,以 Activity.startActivity 為例說(shuō)明整個(gè)過(guò)程的調(diào)用棧。

Activity 中的 startActivity 最終都是由 startActivityForResult 來(lái)實(shí)現(xiàn)的。

Activity#startActivityForResult:

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options) {
  // 一般的 Activity 其 mParent 為 null,mParent 常用在 ActivityGroup 中,ActivityGroup 已廢棄
  if (mParent == null) {
      options = transferSpringboardActivityOptions(options);
      // 這里會(huì)啟動(dòng)新的Activity,核心功能都在 mMainThread.getApplicationThread() 中完成
      Instrumentation.ActivityResult ar =
          mInstrumentation.execStartActivity(
              this, mMainThread.getApplicationThread(), mToken, this,
              intent, requestCode, options);
      if (ar != null) {
          mMainThread.sendActivityResult(
              mToken, mEmbeddedID, requestCode, ar.getResultCode(),
              ar.getResultData());
      }
      if (requestCode >= 0) {
          mStartedActivity = true;
      }
      cancelInputsAndStartExitTransition(options);
  } else {
      if (options != null) {
          mParent.startActivityFromChild(this, intent, requestCode, options);
      } else {
          // Note we want to go through this method for compatibility with
          // existing applications that may have overridden it.
          mParent.startActivityFromChild(this, intent, requestCode);
      }
  }
}

可以發(fā)現(xiàn),真正打開(kāi) activity 的實(shí)現(xiàn)在 Instrumentation 的 execStartActivity 方法中。

Instrumentation#execStartActivity:

public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
    // 核心功能在這個(gè)whoThread中完成,其內(nèi)部scheduleLaunchActivity方法用于完成activity的打開(kāi)
    IApplicationThread whoThread = (IApplicationThread) contextThread;
    Uri referrer = target != null ? target.onProvideReferrer() : null;
    if (referrer != null) {
        intent.putExtra(Intent.EXTRA_REFERRER, referrer);
    }
    if (mActivityMonitors != null) {
        synchronized (mSync) {
            final int N = mActivityMonitors.size();
            for (int i=0; i= 0 ? am.getResult() : null;
                    }
                    break;
                }
            }
        }
    }
    try {
        intent.migrateExtraStreamToClipData();
        intent.prepareToLeaveProcess(who);
        // 這里才是真正打開(kāi) Activity 的地方,核心功能在 whoThread 中完成。
        int result = ActivityManager.getService()
            .startActivity(whoThread, who.getBasePackageName(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()),
                    token, target != null ? target.mEmbeddedID : null,
                    requestCode, 0, null, options);
        // 這個(gè)方法是專(zhuān)門(mén)拋異常的,它會(huì)對(duì)結(jié)果進(jìn)行檢查,如果無(wú)法打開(kāi)activity,
            // 則拋出諸如ActivityNotFoundException類(lèi)似的各種異常
        checkStartActivityResult(result, intent);
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
    return null;
}

如果我們想深入了解 Activity 啟動(dòng)過(guò)程我們需要接著 Android 源碼看下去,但是對(duì)于本文中我們初步了解 Hook 機(jī)制足以。

我們的目的是替換掉系統(tǒng)默認(rèn)邏輯,對(duì)于 Activity#startActivityForResult 的方法里面核心邏輯就是 mInstrumentation 屬性的 execStartActivity 方法,而這里的 mInstrumentation 屬性在 Activity 類(lèi)中恰好是一個(gè)單例,在 Activity 類(lèi)的 attach 方法里面被賦值,我們可以在 attach 之后使用反射機(jī)制對(duì) mInstrumentation 屬性進(jìn)行重新賦值。attach() 方法調(diào)用完成后,就自然而然的調(diào)用了 Activity 的 onCreate() 方法了。

我們需要修改 mInstrumentation 這個(gè)字段為我們的代理對(duì)象,我們使用靜態(tài)代理實(shí)現(xiàn)這個(gè)代理對(duì)象。這里我們使用 EvilInstrumentation 作為代理對(duì)象。

public class EvilInstrumentation extends Instrumentation {
    private Instrumentation instrumentation;

    public EvilInstrumentation(Instrumentation instrumentation) {
        this.instrumentation = instrumentation;
    }

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

        StringBuilder sb = new StringBuilder();
        sb.append("who = [").append(who).append("], ")
        .append("contextThread = [").append(contextThread).append("], ")
        .append("token = [").append(token).append("], ")
        .append("target = [").append(target).append("], ")
        .append("intent = [").append(intent).append("], ")
        .append("requestCode = [").append(requestCode).append("], ")
        .append("options = [").append(options).append("]");;
        Logger.i(EvilInstrumentation.class, "執(zhí)行了startActivity, 參數(shù)如下: " + sb.toString());

        try {
            Method execStartActivity = Instrumentation.class.getDeclaredMethod(
                    "execStartActivity",
                    Context.class,
                    IBinder.class,
                    IBinder.class,
                    Activity.class,
                    Intent.class,
                    int.class,
                    Bundle.class);
            return (ActivityResult) execStartActivity.invoke(instrumentation, who, contextThread, token, target, intent, requestCode, options);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }
}

采用反射直接修改 Activity 中的 mInstrumentation 屬性,從而實(shí)現(xiàn)偷梁換柱——用代理對(duì)象替換原始對(duì)象。

// 拿到原始的 mInstrumentation字段
Field mInstrumentationField = Activity.class.getDeclaredField("mInstrumentation");
mInstrumentationField.setAccessible(true);

// 創(chuàng)建代理對(duì)象
Instrumentation originalInstrumentation = (Instrumentation) mInstrumentationField.get(activity);
mInstrumentationField.set(activity, new EvilInstrumentation(originalInstrumentation));

這段 Hook 的邏輯放在 Activity 的 onCreate 里面即可生效。

對(duì)于 Context 類(lèi)的 startActivity 方法的 Hook 實(shí)現(xiàn)可以參考 weishu 大神的 Android 插件化原理解析——Hook 機(jī)制之動(dòng)態(tài)代理,本文也是基于 weishu 大神的文章在學(xué)習(xí)過(guò)程記錄的內(nèi)容。

Activity 啟動(dòng)過(guò)程

上述例子中我們只是完成了一個(gè)最基礎(chǔ)的 Hook 功能,然而大部分插件化框架提供了十分豐富的功能,例如:插件化支持首先要解決的一點(diǎn)就是插件里的 Activity 并未在宿主程序的 AndroidMainfest.xml 注冊(cè)。常規(guī)方法肯定無(wú)法直接啟動(dòng)插件的 Activity,這個(gè)時(shí)候就需要去了解 Activity 的啟動(dòng)流程。

完整的流程如下:

注: 可以在 http://androidxref.com/ 在線查看 Android 源碼。

上圖列出的是啟動(dòng)一個(gè) Activity 的主要過(guò)程,具體步驟如下:

Activity 調(diào)用 startActivity,實(shí)際會(huì)調(diào)用 Instrumentation 類(lèi)的 execStartActivity 方法,Instrumentation 是系統(tǒng)用來(lái)監(jiān)控 Activity 運(yùn)行的一個(gè)類(lèi),Activity 的整個(gè)生命周期都有它的影子。

通過(guò)跨進(jìn)程的 Binder 調(diào)用,進(jìn)入到 ActivityManagerService 中,其內(nèi)部會(huì)處理 Activity 棧。之后又通過(guò)跨進(jìn)程調(diào)用進(jìn)入到需要調(diào)用的 Activity 所在的進(jìn)程中。

ApplicationThread 是一個(gè) Binder 對(duì)象,其運(yùn)行在 Binder 線程池中,內(nèi)部包含一個(gè) H 類(lèi),該類(lèi)繼承于類(lèi) Handler。ApplicationThread 將啟動(dòng)需要調(diào)用的 Activity 的信息通過(guò) H 對(duì)象發(fā)送給主線程。

主線程拿到需要調(diào)用的 Activity 的信息后,調(diào)用 Instrumentation 類(lèi)的 newActivity 方法,其內(nèi)通過(guò) ClassLoader 創(chuàng)建 Activity 實(shí)例。

下面介紹如何通過(guò) hook 的方式啟動(dòng)插件中的 Activity,需要解決以下兩個(gè)問(wèn)題:

插件中的 Activity 沒(méi)有在 AndroidManifest 中注冊(cè),如何繞過(guò)檢測(cè)。

如何構(gòu)造 Activity 實(shí)例,同步生命周期。

我們這里使用最簡(jiǎn)單的一種實(shí)現(xiàn)方式:先在 Manifest 中預(yù)埋 StubActivity,啟動(dòng)時(shí) hook 上圖第 1 步,將 Intent 替換成 StubActivity。

// StubActivity.java
public class StubActivity extends Activity {
    public static final String TARGET_COMPONENT = "TARGET_COMPONENT";
}

我們上面在 EvilInstrumentation 類(lèi)里面實(shí)現(xiàn)了 execStartActivity 方法,現(xiàn)在我們?cè)谶@里再加一些額外的邏輯。

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

    StringBuilder sb = new StringBuilder();
    sb.append("who = [").append(who).append("], ")
    .append("contextThread = [").append(contextThread).append("], ")
    .append("token = [").append(token).append("], ")
    .append("target = [").append(target).append("], ")
    .append("intent = [").append(intent).append("], ")
    .append("requestCode = [").append(requestCode).append("], ")
    .append("options = [").append(options).append("]");;
    Logger.i(EvilInstrumentation.class, "執(zhí)行了startActivity, 參數(shù)如下: " + sb.toString());

    // 在此處先將 intent 原本的 Component 保存起來(lái), 然后創(chuàng)建一個(gè)新的 intent。
    // 使用 StubActivity 并替換掉原本的 Activity, 以達(dá)通過(guò) AMS 驗(yàn)證的目的,然后等 AMS 驗(yàn)證通過(guò)后再將其還原。
    Intent replaceIntent = new Intent(target, StubActivity.class);
    replaceIntent.putExtra(StubActivity.TARGET_COMPONENT, intent);
    intent = replaceIntent;

    try {
        Method execStartActivity = Instrumentation.class.getDeclaredMethod(
                "execStartActivity",
                Context.class,
                IBinder.class,
                IBinder.class,
                Activity.class,
                Intent.class,
                int.class,
                Bundle.class);
        return (ActivityResult) execStartActivity.invoke(instrumentation, who, contextThread, token, target, intent, requestCode, options);
    } catch (Exception e) {
        e.printStackTrace();
    }

    return null;
}

通過(guò)這種"移花接木"的方式繞過(guò) AMS 驗(yàn)證,但是這里我們并沒(méi)有完成對(duì)我們?cè)拘枰嬲蜷_(kāi)的 Activity 的創(chuàng)建。這里我們需要監(jiān)聽(tīng) Activity 的創(chuàng)建過(guò)程,然后在適當(dāng)?shù)倪m合將原本需要打開(kāi)的 Activity 還原回來(lái)。

在 ActivityThread 類(lèi)中有一個(gè)重要的消息處理的方法 sendMessage。

2644    private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
2645        if (DEBUG_MESSAGES) Slog.v(
2646            TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
2647            + ": " + arg1 + " / " + obj);
2648        Message msg = Message.obtain();
2649        msg.what = what;
2650        msg.obj = obj;
2651        msg.arg1 = arg1;
2652        msg.arg2 = arg2;
2653        if (async) {
2654            msg.setAsynchronous(true);
2655        }
2656        mH.sendMessage(msg);
2657    }

最終都會(huì)落實(shí)到 mH.sendMessage(msg); 的調(diào)用,繼續(xù)追蹤這個(gè) mH 對(duì)象,我們會(huì)發(fā)現(xiàn)是 H 對(duì)象的實(shí)例化對(duì)象。

final H mH = new H();
    private class H extends Handler {
        public void handleMessage(Message msg) {
1585            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
1586            switch (msg.what) {
1587                case LAUNCH_ACTIVITY: {
1588                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
1589                    final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
1590
1591                    r.packageInfo = getPackageInfoNoCheck(
1592                            r.activityInfo.applicationInfo, r.compatInfo);
1593                    handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
1594                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
1595                } break;
        ...
        }
    }

我們知道 Handler 消息機(jī)制用于同進(jìn)程的線程間通信, Handler 是工作線程向 UI 主線程發(fā)送消息,工作線程通過(guò) mHandler 向其成員變量 MessageQueue 中添加新 Message,主線程一直處于 loop() 方法內(nèi),當(dāng)收到新的 Message 時(shí)按照一定規(guī)則分發(fā)給相應(yīng)的 handleMessage() 方法來(lái)處理。

類(lèi)似于對(duì)上述 mInstrumentation 實(shí)例化對(duì)象 hook 一樣,這里我們可以對(duì) mH 對(duì)象進(jìn)行 hook。

/**
 * 將替換的activity在此時(shí)還原回來(lái)
 */
public static void doHandlerHook() {
    try {
        Class activityThreadClass = Class.forName("android.app.ActivityThread");
        Method currentActivityThread = activityThreadClass.getDeclaredMethod("currentActivityThread");
        Object activityThread = currentActivityThread.invoke(null);

        Field mHField = activityThreadClass.getDeclaredField("mH");
        mHField.setAccessible(true);
        Handler mH = (Handler) mHField.get(activityThread);

        Field mCallbackField = Handler.class.getDeclaredField("mCallback");
        mCallbackField.setAccessible(true);
        mCallbackField.set(mH, new ActivityThreadHandlerCallback(mH));
    } catch (Exception e) {
        e.printStackTrace();
    }
}

對(duì)于 Handler.Callback 的 hook 實(shí)現(xiàn)如下:

public class ActivityThreadHandlerCallback implements Handler.Callback {
    private Handler mBaseHandler;

    public ActivityThreadHandlerCallback(Handler mBaseHandler) {
        this.mBaseHandler = mBaseHandler;
    }

    @Override
    public boolean handleMessage(Message msg) {
        Logger.i(ActivityThreadHandlerCallback.class, "接受到消息了msg:" + msg);

        if (msg.what == 100) {
            try {
                Object obj = msg.obj;
                Field intentField = obj.getClass().getDeclaredField("intent");
                intentField.setAccessible(true);
                Intent intent = (Intent) intentField.get(obj);

                Intent targetIntent = intent.getParcelableExtra(StubActivity.TARGET_COMPONENT);
                intent.setComponent(targetIntent.getComponent());
                Log.e("intentField", targetIntent.toString());
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        mBaseHandler.handleMessage(msg);
        return true;
    }
}

我們?cè)O(shè)置在 handleMessage 里面還原我們最開(kāi)始替換的 Activity,至此我們就實(shí)現(xiàn)了對(duì)于 startActivity 的完整 hook,但是這個(gè)過(guò)程中仍然存在很多問(wèn)題,我們需要進(jìn)一步去深入探索才能去理解和更好實(shí)現(xiàn)插件化框架的內(nèi)容。

學(xué)習(xí)案例

本文學(xué)習(xí)案例地址:android-plugin-framework

參考

Android 博客周刊專(zhuān)題之#插件化開(kāi)發(fā)#

VirtualAPK Wiki

DroidPlugin Wiki

understand-plugin-framework

Android 插件化原理解析——Hook 機(jī)制之動(dòng)態(tài)代理

Android 源碼分析-Activity 的啟動(dòng)過(guò)程

APP 的啟動(dòng)過(guò)程

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/71647.html

相關(guān)文章

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<