代理(静态代理与动态代理)、Hook

本文大量运用了Java反射,没了解过的可先看一下:初识Java反射

代理(Proxy)

代理是一种常用的设计模式,其核心就是代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。分为静态代理动态代理

举一个例子讲清楚什么是代理?

你在租房子的时候,由于自己掌握的资源不够,为了租到比较适合自己的房子,你就需要找中介(代理)帮你完成租房子的工作。

下面以租房子为例讲一讲静态代理与动态代理。

静态代理

  1. 定义一个租房子接口
public interface RentAHouse {
    public void method();
}
  1. 定义一个委托类,委托类想要租房子,所以需要实现租房子接口
public class MyClient implements RentAHouse {
    @Override
    public void method() {
        Log.d("Client", "租房子!");
    }
}
  1. 定义一个代理类,代理类帮助委托类租房子,所以也需要实现租房子接口。除此之外,代理类里面也应有一个Client类型的成员变量,也就是具体是帮谁租房子。
public class MyProxy implements RentAHouse {

    private MyClient client;

    public MyProxy(MyClient client) {
        this.client = client;
    }

    @Override
    public void method() {
        Log.d("MyProxy", "代理之前");
        this.client.method();
        Log.d("MyProxy", "代理之后");
    }
}
  1. 在main()方法中测试
    MyClient client = new MyClient();
    MyProxy proxy = new MyProxy(client);
    proxy.method();

这样我们就完成了一个简单的静态代理案例。

但是静态代理存在较为明显的缺点:

  1. 代理类与委托类实现同一个接口,代码重复率高。
  2. 代理类无法复用,只能完成单一工作。如果需要购买商品,也需要找个代理帮忙代购,还需要再写一个代理类。

因此,就有了动态代理的出现。

动态代理

代理类不再受委托类的限制(仅仅实现与之相同的接口),而是实现一个InvocationHandler接口,我们只需要重写invoke() 方法,在其中动态调用某个委托类的方法。这里的动态体现在委托类的非单一性,即一个代理类可以为多种委托类服务。

  1. 同样的,定义一个租房子接口
public interface RentAHouse {
    public void method();
}
  1. 定义一个委托类,委托类想要租房子,所以需要实现租房子接口
public class MyClient implements RentAHouse {
    @Override
    public void method() {
        Log.d("MyClient", "租房子!");
    }
}
  1. 定义一个代理类,代理类实现InvocationHandler接口,并如下重写invoke()方法
public class MyProxy implements InvocationHandler {

    public Object client;

    public MyProxy(Object client) {
        this.client = client;
    }

    @Override
    /*InvocationHandler接口的方法,o表示代理,method表示原对象被调用的方法,objects表示方法的参数*/
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        Log.d("MyProxy", "代理之前");
        Object object = method.invoke(client, objects);
        Log.d("MyProxy", "代理之后");
        return object;
    }
}
  1. 在main()方法中测试
	//委托者
    RentAHouse client = new MyClient();

    //需要传进函数的handler
    InvocationHandler handler = new MyProxy(client);

    //生成新的代理对象
    RentAHouse newProxyObject = (RentAHouse) Proxy.newProxyInstance(client.getClass().getClassLoader(),
            client.getClass().getInterfaces(), handler);

    //新的代理对象调用方法
    newProxyObject.method();

完成动态代理后,我们回过头再想想,如果需要购买商品,也需要找个代理帮忙代购,如何用静态代理和动态代理分别实现?

  1. 静态代理中的每一步都需要再写一遍。
  2. 动态代理可以省去第三步代理类的定义。

Hook

在 Android 操作系统中系统维护着自己的一套事件分发机制。应用程序,包括应用触发事件和后台逻辑处理,也是根据事件流程一步步地向下执行。Hook,就是在事件传送到终点前截获并监控事件的传输,处理一些自己特定的事件

具体而言,Hook就是我们创建一个代理对象替换原来的对象,我们就可以通过操作代理对象完成原来对象的工作以及我们添加的工作

Android 中API Hook的实例

现有MainAcitivity,里面有一个TextView,点击TextView跳转到SecondActivity,我们要做的就是Hook实现跳转的startActivity()这个方法

  1. 由于我们想要Hook的startActivity()是在MainActivity中调用,我们应该在MainActivity执行之前先Hook,在我们的印象中,MainActivity是程序的入口,那该在哪Hook才可以赶在MainActivity之前呢?那我们可以从AndroidManifest文件中答案。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.hookstartactivity">

    <application
        android:name="MyApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.HookStartActivity">
        <activity android:name=".SecondActivity"></activity>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

可以看出application是在activity之前,所以我们可以在创建一个MyApplication(注意上面代码中android:name="MyApplication"这一行)继承Application。在MyApplication类中完成我们的Hook。

  1. 其次,我们去看源码可以发现,startAcitivy最源头是Instrumentation类中的execStartActivity()方法实现的。所以我们可以自己创建一个InstrumentationProxy类(继承Instrumentation),实例化一个InstrumentationProxy对象来替换原来的Instrumentation对象。
public class InstrumentationProxy extends Instrumentation {

    public static final String TAG = "InstrumentationProxy";
    public static final String EXEC_START_ACTIVITY = "execStartActivity";

    public Instrumentation oldInstrumentation;

    public InstrumentationProxy(Instrumentation mInstrumentation) {
        this.oldInstrumentation = mInstrumentation;
    }

    //这个方法是由于原始方法里面的Instrumentation有execStartActivity方法来定的
    public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {
        Log.d(TAG, "这里可以做你在打开StartActivity方法之前的事情");

        //由于这个方法是隐藏的,所以需要反射来调用,先找到这方法
        try {
            Method execStartActivity = Instrumentation.class.getDeclaredMethod(EXEC_START_ACTIVITY,
                    Context.class, IBinder.class, IBinder.class, Activity.class, Intent.class, int.class, Bundle.class);
            execStartActivity.setAccessible(true);
            return (ActivityResult) execStartActivity.invoke(oldInstrumentation, who, contextThread,
                    token, target, intent, requestCode, options);
        } catch (Exception e) {
            //如果你在这个类的成员变量Instrumentation的实例写成mInstrumentation,代码会执行到这里来
            throw new RuntimeException("如果你在这个类的成员变量Instrumentation的实例写成mInstrumentation,代码会执行到这里来");
        }
    }
}
  1. 如前面所说,创建MyApplication类(继承Application类),在里面实例化一个InstrumentationProxy对象来替换原来的Instrumentation对象。
public class MyApplication extends Application {

    public static final String TAG = "MyApplication";
    public static final String ACTIVITY_THREAD = "android.app.ActivityThread";
    public static final String CURRENT_ACTIVITY_THREAD = "currentActivityThread";
    public static final String INSTRUMENTATION = "mInstrumentation";

    @Override
    public void onCreate() {
        super.onCreate();
        try {
            attachContext();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void attachContext() throws Exception {
        //获取当前的ActivityThread对象->主线程(UI线程)
        Class<?> activityThreadClass = Class.forName(ACTIVITY_THREAD);
        Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod(CURRENT_ACTIVITY_THREAD);
        currentActivityThreadMethod.setAccessible(true);
        Object currentActivityThread = currentActivityThreadMethod.invoke(null);

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

        //构建我们的代理对象->静态代理
        Instrumentation evilInstrumentation = new InstrumentationProxy(mInstrumentation);

        //通过反射,换掉字段
        mInstrumentationField.set(currentActivityThread, evilInstrumentation);

        Log.i(TAG, "has go in MyApplication attachContext method");
    }
}
  1. 最后,MainActivity中为TextView对象添加OnClick事件监听器,调用startActivity。
textView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent = new Intent(MainActivity.this, SecondActivity.class);
        Log.d(TAG, "StartActivity Before");
        startActivity(intent);
        Log.d(TAG, "StartActivity After");
    }
});

参考:https://www.jianshu.com/p/4f6d20076922
https://www.cnblogs.com/baizhanshi/p/6611164.html
https://www.bilibili.com/video/BV1Pb411V72k?p=3

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值