hook 应用程序的入口 Activity

# 问题
假如你现在要开发一个 SDK,比如说广告SDK吧,你希望在应用的入口 Activity 之前插入你的 Activity,在你的 Activity 中显示广告, 怎么做? 我看过联通沃商店的做法, 要求接入方声明自己的 Ativity 为入口 Activity,这种做法显然不符合一个技术人的追求的。那么我们能不能在接入方无感知的情况把我们的 Activity 强制插入到入口 Activity创建前来显示呢? 


**问题再简化一下:如何 hook 一个应用的入口 Activity,插入自己的Activity?**
# 思路
 我们首先要知道一个应用的入口Activiy是怎么被创建,然后被现实出来的?如果看过插件化的相关实现文章就不难理解了,我们首先来看看用户点击启动图标之后是怎么进入第一个Activity的:
[图片上传失败...(image-b0898e-1512827933284)]


明白了Activity的创建和方法调用流程我们就可以决定在哪个环节 hook 了,不难发现,我们 hook  的最佳时间是 Activity 的 onCreate 方法被调用前, 也就是Instrumentation 的 callActivityOnCreate 方法,我们把它拦截掉, 怎么做?
你需要知道个知识点: **反射**和**代理**。我假定读我的文章的人都熟悉反射和代理了。
## 反射获取 Instrumentation


首先通过反射获取应用进程的 Instrumentation 对象,再来看看 Instrumentation 相关的引用关系:
ActivityThread -> Instrumentation
见 ActivityThread.java 中代码:


    Instrumentation mInstrumentation;
现在我们想办法拿到 ActivityThread 的实例对象:


    public static final ActivityThread currentActivityThread() {
     return sThreadLocal.get();
    }
所以我们可以先反射 currentActivityThread() 方法拿到 ActivityThread 当前实例, 再反射 mInstrumentation 拿到 Instrumentation 对象。


          //获取当前的ActivityThread对象
            Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
            Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
            currentActivityThreadMethod.setAccessible(true);
            Object currentActivityThread = currentActivityThreadMethod.invoke(null);


            //拿到在ActivityThread类里面的原始mInstrumentation对象
            Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation");
            mInstrumentationField.setAccessible(true);
            Instrumentation mInstrumentation = (Instrumentation) 
          mInstrumentationField.get(currentActivityThread);




获取了 Instrumentation 对象怎么拦截里面的方法?如果是接口实现类直接使用 JDK 的动态代理就能实现拦截, 这里只能使用静态代理, 我们自己创建一个 Instrumentation 的子类 ,就叫 InstrumentationProxy 吧,InstrumentationProxy 代替原来的 Instrumentation。
    
    


       //包装代理
            Instrumentation evilInstrumentation = new InstrumentationProxy(mInstrumentation);


            // 偷梁换柱


            mInstrumentationField.set(currentActivityThread, evilInstrumentation);




InstrumentationProxy 的实现:


    public class InstrumentationProxy extends Instrumentation {


public static final String TAG = "InstrumentationProxy";
public Instrumentation target;
     
//通过构造函数来传递对象
public InstrumentationProxy(Instrumentation mInstrumentation) {
target = mInstrumentation;
}
@Override
public void callActivityOnCreate(Activity activity, Bundle icicle) {
       // 就在这儿,干你想干的坏事

target.callActivityOnCreate(activity, icicle);
}
}


# 设计实现


以上算是完成了可行性技术预研, 接下来回调我们开头提的那个业务问题,就是针对问题的设计了, 首次启动时 hook 入口Acitvity。
  
 ![首次启动注入广告 Activity](http://upload-images.jianshu.io/upload_images/1344733-be92f178f685398e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)


# demo
https://github.com/liuguangli/HookLaunchActivity
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值