代理模式及对startActivity的Hook应用

news2024/12/28 5:17:27
定义

简而言之,就是让代理类和目标类(被代理类)实现同一个接口,并让代理类持有一个目标类的对象,这样对代理类的访问,可以转移到目标类的访问中去。我们可以在不修改目标类的情况下,在代理类中实现一些功能的增强,从而间接的增强了目标类的功能。

UML图如下:
在这里插入图片描述
基本代码实现:

public interface Subject {
    void request()
}

===================================================================================
public class RealSubject implements Subject {
    @Override
    public void request() {
        // 完成一些事情
    }
}

===================================================================================\
public class Proxy implements Subject {
    private RealSubject realSubject;

    public Proxy(RealSubject subject) {
        this.realSubject = subject;
    }

    @Override
    public void request() {
        realSubject.request();
    }
}

代理模式的几种应用方式:

  1. 远程代理(AIDL)
    在这里插入图片描述

Proxy中的add方法把参数写入到data中,通过mRemote的transact方法,把data和reply发送到AIDL的另一端,在AIDL的另一端通过Stub类的onTransact方法把参数从data中取出来,计算完得到结果,再把结果通过reply这个回调函数传递回来。

  1. 保护代理(权限控制)
    老板出差期间把自己审核请假的权限临时授予秘书。

  2. 虚代理(图片占位)
    比如:微信图片查看,网络不好时,先用模糊的小图占位,等大图下载完成再替换之前的小图
    在这里插入图片描述
    4.协作开发(Mock Class)
    A要用到B中的方法,但是B还没有开发完,那么B就提供一个假的方法,返回写死的值,等开发完成再替换掉

5.添加日志
Class1中有一个doSomething方法,我们想在doSomething的执行前和执行后记录一行日志,我们可以设计一个Class1Proxy来做这个功能,这样就不会影响到Class1。

public interface Class1Interface {
    public void doSomething();
}
public class Class1 implements Class1Interface{
    @Override
    public void doSomething() {
        System.out.println("class1 doSomething");
    }
}
public class Class1Proxy implements Class1Interface{
    Class1 class1 = new Class1();

    @Override
    public void doSomething() {
        System.out.println("Begin log.");
        class1.doSomething();
        System.out.println("End log.");
    }
}

接下来,我们就可以用Class1Proxy来代替Class1了

Class1Proxy proxy = new Class1Proxy();
proxy.doSomething();

上述的实现方法是静态代理模式,它的缺点在于

  1. 静态代理如果接口新增一个方法,除了所有实现类(真实主题类)需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
  2. 代理对象只服务于一种类型的对象,如果要服务多类型的对象。必须要为每一种对象都进行代理,静态代理在程序规模稍大时就无法胜任了。

比如如果我们需要在Class2的doSomething的执行前打印日志,就需要再实现一个Class2Proxy类,Proxy类的数量会很多。

动态代理

上面我们所讲的静态代理,在代码运行前就已经存在了代理类的class文件;而动态代理则是在代码运行时通过发射来动态的生成代理类的对象,并确定到底来代理谁。
也就是说我们在编码阶段并不知道要代理谁,到底代理谁我们将在代码运行时决定。
Java提供了Proxy类的newProxyInstance方法来生成代理对象,也提供了动态的代理接口InvocationHandler来中转需要代理实现的方法。
我们来看看newProxyInstance方法的声明:

static Object newProxyInstance(
	ClassLoader loader,
	Class<?>[] interfaces,
	InvocationHandler h)

参数说明:

  • loader 设置为目标对象(被代理对象)对应的classLoader
  • interfaces 设置为目标对象(被代理对象)所实现的接口类型
  • h 设置为一个实现了InvocationHandler接口的类对象,我们通过它的构造函数把目标对象注入。
 Class1Interface class1 = new Class1();
 Class1Interface class1Proxy = (Class1Interface) Proxy.newProxyInstance(class1.getClass().getClassLoader(), class1.getClass().getInterfaces(), new InvocationHandlerForTest(class1));
 class1Proxy.doSomething();

================================================================================
public class InvocationHandlerForTest implements InvocationHandler {
    private Object realSubject; // 真实的主题对象

    public InvocationHandlerForTest(Object realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("日志开始");
        Object result = method.invoke(realSubject,args);
        System.out.println("日志结束");
        return result;
    }
}

通过Proxy.newProxyInstance方法创建的对象,是一个实现了Class1Interface接口的对象,如此我们就实现了Class1的代理对象,如果我们现在需要代理Class2,那么我们可以直接:

Class2Interface class2 = new Class2();
Class2Interface class2Proxy = (Class2Interface) Proxy.newProxyInstance(class2.getClass().getClassLoader(), class2.getClass().getInterfaces(), new InvocationHandlerForTest(class2));
class2Proxy.work();

注意,method.invoke(target,objects)这行代码,target就是class1对象,objects是class1的doSomthing方法所需要的参数,接下来调用class1Proxy的doSomething方法,就会调用class1类的doSomething方法。

动态代理的优点
  1. 可以通过一个代理类完成全部的代理功能,接口中声明的所有方法都被转移到调用处理器一个集中的方法中处理(InvocationHandler.invoke)。当接口方法数量较多时,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。
  2. 动态代理的应用使我们的类职责更加单一,复用性更强。
动态代理的缺点

不能对类进行代理,只能对接口进行代理,如果我们的类没有实现任何接口,那么就不能使用这种方式进行动态代理(因为$Proxy()这个类集成了Proxy,Java的集成不允许出现多个父类)。

我们可以利用动态代理Proxy.newProxyInstance方法生成的代理对象来替换掉原来的对象,这个技术在插件化中被称为Hook技术。

Hook应用

我们假设在MainActivity中启动一个SecondActivity,但是最终启动的却是ThirdActivity。
我们首先需要知道的是,Activity中执行startActivity的基本流程。

startActivity 方法的两种形式:

  1. 使用 Activity 自带的 startActivity
Intent intent= new Intent(MainActivity.this ,,SecondActivity.class) ;
startActivity(intent);
  1. 使用Context的startActivity 方法
Intent intent= new Intent(MainActivity.this ,,SecondActivity.class) ;
startActivity(intent);

两种方法殊途同归,都会执行到ActivitystartActivityForResult方法中,最终通过mInstrumentation.execStartActivity来调用,在execStartActivity方法中又交给了ActivityTaskManager.getService().startActivity来处理。
这是startActivity的上半场:

MainActivity通知ATMS,要启动SecondActivity

ATMS接受到MainActivity传过来的请求,然后通过mService.getLifecycleManager().scheduleTransaction方法回到APP进程,发送ActivityThread.H.EXECUTE_TRANSACTION消息,执行到ActivityThreadhandleLaunchActivity()方法,最终执行到ActivityThreadmInstrumentation.callActivityOnCreate();,在这个方法里面调用SecondActivityonCreate()完成启动。
这是startActivity的下半场:

ATMS通知APP进程启动SecondActivity

我们在上半场可以Hook的地方包括:

  • ActivitystartActivityForResult方法;
  • ActivitymInstrumentation字段;
  • ActivityTaskManager.getService()方法获取到的对象

在下半场可以Hook的地方包括:

  • ActivityThread的mInstrumentation对象,对应的newActivity方法和callActivityOnCreate方法。

重写Activity的startActivityForResult

为App的所有Activity创建一个基类Activity-BaseActivity,在BaseActivity中重写startActivityForResult方法

对Activity的mInstrumentation字段进行Hook

Activity中有一个mInstrumentation字段,Activity的startActivityForResult方法会调用mInstrumentation的executeStartActivity方法:

# Activity.java
private Instrumentation mInstrumentation;

public void startActivityForResult(Intent intent, int requestCode, Bundle options) {
    if(this.mParent == null) {
        ActivityResult ar = this.mInstrumentation.execStartActivity(this, this.mMainThread.getApplicationThread(), this.mToken, this, intent, requestCode, options);
        if(ar != null) {
            this.mMainThread.sendActivityResult(this.mToken, this.mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData());
        }

        if(requestCode >= 0) {
            this.mStartedActivity = true;
        }
    } else if(options != null) {
        this.mParent.startActivityFromChild(this, intent, requestCode, options);
    } else {
        this.mParent.startActivityFromChild(this, intent, requestCode);
    }

}

首先我们通过反射来获取这一私有变量。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 获取Activity中的Instrumentation对象,私有变量,通过反射获取
        Instrumentation instrumentation = (Instrumentation) RefInvoke.getFieldObject(Activity.class,this,"mInstrumentation");
        // 创建Instrumentation代理对象
        InstrumentationProxy instrumentationProxy = new InstrumentationProxy(instrumentation);
        // 替换掉MainActivity中的mInstrumentation对象为代理对象
        RefInvoke.setFieldObject(Activity.class,this,"mInstrumentation",instrumentationProxy);

        setContentView(R.layout.activity_main);
        findViewById(R.id.turn_to_other_activity).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                startActivity(intent);
            }
        });
    }
}
public class InstrumentationProxy extends Instrumentation {
    private static final String TAG = InstrumentationProxy.class.getSimpleName();
    private Instrumentation mInstrumentation;

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

    public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,
                                            Intent intent, int requestCode, Bundle options){
        Log.d(TAG, "XXX到此一游!");
        ComponentName componentName = intent.getComponent();
        String className = componentName.getClassName();
        // 判断目标activity是不是SecondActivity,替换SecondActivity为ThirdActivity
        if(className.equals("com.chinatsp.hookstartactivity.SecondActivity")){
            intent.setClassName(who,ThirdActivity.class.getCanonicalName());
        }
      
        // 由于这个方法是隐藏的,因此需要使用反射调用;
        Class[] p1 = {Context.class, IBinder.class,
                IBinder.class, Activity.class,
                Intent.class, int.class, Bundle.class};
        Object[] v1 = {who, contextThread, token, target,
                intent, requestCode, options};
        return (ActivityResult) RefInvoke.invokeInstanceMethod(
                mInstrumentation, "execStartActivity", p1, v1);
    }
}

这样我们就成功的Hook了startActivity方法,因为mInstrumentation是一个对象,所以这里采用静态代理的模式。

对ATMS的getService进行Hook

InstrumentaionexecStartActivity方法最终会走到ActivityTaskManager.getService().startActivity方法:

# Instrumentation.java

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

    try {
		// 流程就从Launcher进程进入到AMS所在的SystemServer进程了
        int result = ActivityTaskManager.getService().startActivity(whoThread,
                who.getBasePackageName(), who.getAttributionTag(), intent,
                intent.resolveTypeIfNeeded(who.getContentResolver()), token,
                target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
       ...
    }
    return null;
}

//=========ActivityTaskManager.java=========
public static IActivityTaskManager getService() {
    return IActivityTaskManagerSingleton.get();
}

@UnsupportedAppUsage(trackingBug = 129726065)
private static final Singleton<IActivityTaskManager> IActivityTaskManagerSingleton = new Singleton<IActivityTaskManager>() {
    @Override
    protected IActivityTaskManager create() {
        final IBinder b = ServiceManager.getService(Context.ACTIVITY_TASK_SERVICE);
        return IActivityTaskManager.Stub.asInterface(b);
    }
};


//=================Singleton.java=========
        /**
         * Singleton helper class for lazily initialization.
         * ......
         */
        public abstract class Singleton<T> {
            private T mInstance;

            protected abstract T create();

            public final T get() {
                synchronized (this) {
                    if (mInstance == null) {
                        mInstance = create();
                    }
                    return mInstance;
                }
            }
        }

ATMS的getService方法返回一个IActivityTaskManager类型,这是一个接口,我们可以使用Proxy.newProxyInstance方法把这个IActivityTaskManager接口类型的对象Hook成我们自定义的代理类AMSProxy.class生成的对象:

public class AMSHookHelper {
    private static final String TAG = AMSHookHelper.class.getSimpleName();

    public static void hookAMS() throws ClassNotFoundException,ClassCastException{
        Object singletonObject = null;
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P){
            singletonObject = RefInvoke.getStaticFieldObject("android.app.ActivityTaskManager","IActivityTaskManagerSingleton");
        }else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            singletonObject = RefInvoke.getStaticFieldObject("android.app.ActivityTaskManager","IActivityManagerSingleton");
        }else{
            // 获取AMN的gDefault单例,gDefault是final静态的
            singletonObject = RefInvoke.getStaticFieldObject("android.app.ActivityManagerNative","gDefault");
        }

        // singletonObject 是一个android.util.Singleton<T>对象; 我们取出这个单例里面的mInstance字段
        Object mInstance = RefInvoke.getFieldObject("android.util.Singleton", singletonObject, "mInstance");

        // 创建一个这个对象的代理对象MockClass1, 然后替换这个字段, 让我们的代理对象帮忙干活
        Class<?> activityManagerInterface;
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P){
            activityManagerInterface = Class.forName("android.app.IActivityTaskManager");
        }else {
            activityManagerInterface = Class.forName("android.app.IActivityManager");
        }

        // getInterfaces()方法和Java的反射机制有关。它能够获得这个对象所实现的接口。activityManager 本身就是activityManagerInterface 的Class
        Object activityManagerProxy = Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                new Class[]{ activityManagerInterface },
                new AMSProxy(mInstance));

        RefInvoke.setFieldObject("android.util.Singleton", singletonObject,"mInstance",activityManagerProxy);
        Log.d(TAG, "hook activity manager success");

    }
}

public class AMSProxy implements InvocationHandler {
    private static final String TAG = AMSProxy.class.getSimpleName();

    private Object mAMSProxy;

    public AMSProxy(Object mAMSProxy) {
        this.mAMSProxy = mAMSProxy;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Log.d(TAG,"hey, baby,you are hooked!!");
        Log.d(TAG,"method:"+method.getName()+" called with args:" + Arrays.toString(args));
        if("startActivity".equals(method.getName())){
            int index = 0;
            for (int i = 0; i < args.length; i++) {
                if (args[i] instanceof Intent) {
                    index = i;
                    break;
                }
            }
            // 原来的intent
            Intent intent = (Intent) args[index];
            Intent proxyIntent = new Intent(intent);
            // 实际跳转的页面Activity
            proxyIntent.setClassName("com.chinatsp.hookstartactivityamn", ThirdActivity.class.getCanonicalName());
            args[index] = proxyIntent;

            return method.invoke(mAMSProxy,args);
        }

        return method.invoke(mAMSProxy,args);
    }
}

最后在MainActivityattachBaseContext方法中执行AMSHookHelper.hookAMS方法:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
        try {
           AMSHookHelper.hookAMN();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.turn_to_other_activity).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                startActivity(intent);
            }
        });
    }
}

对ActivityThread的Instrumentation字段进行Hook

在Activity内部,有一个mInstrumentation字段,我们通过替换它为自定义的InstrumentationProxy类来实现Hook。

ActivityThread内部也有一个mInstrumentation字段,ActivityThread会调用mInstrumentation的newActivity方法生成Activity对象,然后调用mInstrumentation的callActivityOnCreate方法来启动这个activity。因此我们也可以替换这个mInstrumentation为自定义的代理对象来实现Hook。

public class InstrumentationInActivityThreadHookHelper {
    private static final String TAG = InstrumentationInActivityHookHelper.class.getSimpleName();
    public static void hookInstrumentationInActivity() throws ClassNotFoundException{
        // 先获取到当前的ActivityThread
        Object currentActivityThread = RefInvoke.invokeStaticMethod("android.app.ActivityThread","currentActivityThread");
        // 拿到原始的Instrumentation字段
        Instrumentation instrumentation = (Instrumentation) RefInvoke.getFieldObject(currentActivityThread,"mInstrumentation");
        // 创建代理对象
        InstrumentationProxy instrumentationProxy = new InstrumentationProxy(instrumentation);
        // 替换ActivityThread中的instrumentation为代理对象
        RefInvoke.setFieldObject(currentActivityThread,"mInstrumentation",instrumentationProxy);
    }
}
public class InstrumentationProxy extends Instrumentation {
    private static final String TAG = InstrumentationProxy.class.getSimpleName();
    // ActivityThread中原始的Instrumentation对象, 保存起来
    private Instrumentation mBase;

    public InstrumentationProxy(Instrumentation mBase) {
        this.mBase = mBase;
    }

    public Activity newActivity(ClassLoader cl, String className,
                                Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        // 原来的intent
        ComponentName componentName = intent.getComponent();
        Log.d(TAG, "到此一游!cl:"+cl+" ,className="+className
                + " ,component_packageName="+componentName.getPackageName()
                + " ,component_className="+componentName.getClassName());

        if("com.chinatsp.hookstartactivityamn.SecondActivity".equals(componentName.getClassName())){
            // 如果是跳转到SecondActivity,就替换为ThirdActivity
            className = ThirdActivity.class.getCanonicalName();
            intent.setComponent(new ComponentName(componentName.getPackageName(),"com.chinatsp.hookstartactivityamn.ThirdActivity"));
        }
        return mBase.newActivity(cl, className, intent);
    }

    public void callActivityOnCreate(Activity activity, Bundle bundle) {
        Log.d(TAG, "到此一游! bundle = " + bundle);

//        Class[] p1 = {Activity.class, Bundle.class};
//        Object[] v1 = {activity, bundle};
//        RefInvoke.invokeInstanceMethod(
//                mBase, "callActivityOnCreate", p1, v1);
        mBase.callActivityOnCreate(activity,bundle);
    }
}

callActivityOnCreate方法没有变动,主要的变动在于newActivity方法。

启动没有在AndroidManifest中声明的Activity

再回想一下启动Activity的时序图
在这里插入图片描述
在正常的流程当中,如果APP启动了一个没有在AndroidManifest中声明的Activity,AMS就会抛出一个Activity Not Found的异常。
AMS对Activity是否在AndroidManifest文件中声明的检查,是在第二步中完成的。基本思路:

  1. 在第一步中,发送要启动的Activity信息之前,把这个Activity替换为一个在AndroidManifest文件中声明过的StubActivity,这样就能绕过AMS的检查,在替换的过程中,要把原来的Activity信息存放在Bundle中。
  2. 在第五步中,AMS通知APP启动StubActivity时,我们将StubActivity替换为原先的Activity。原先的Activity信息存放在Bundle中,取出来即可。

第一步的代码实现,其实就是上节中Hook startActivity流程上半场时的几种方法都可以,这里选择HookATMS的方式:

public class AMSHookHelper {
    private static final String TAG = AMSHookHelper.class.getSimpleName();
    public static final String EXTRA_TARGET_INTENT = "extra_target_intent";

    /**
     * Hook AMS
     * 主要完成的操作是: 把真正要启动的Activity临时替换为在AndroidManifest.xml中声明好的Activity,进而骗过AMS
     */
    public static void hookAMN() throws ClassNotFoundException,NoSuchMethodException{
        // 获取AMN的gDefault对象实例
        Object singletonObject = null;
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P){
            singletonObject = RefInvoke.getStaticFieldObject("android.app.ActivityTaskManager","IActivityTaskManagerSingleton");
        }else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
            singletonObject = RefInvoke.getStaticFieldObject("android.app.ActivityTaskManager","IActivityManagerSingleton");
        }else{
            // 获取AMN的gDefault单例,gDefault是final静态的
            singletonObject = RefInvoke.getStaticFieldObject("android.app.ActivityManagerNative","gDefault");
        }

        // singletonObject 是一个 android.util.Singleton<T>对象; 我们取出这个单例里面的mInstance字段
        Object mInstance = RefInvoke.getFieldObject("android.util.Singleton", singletonObject, "mInstance");

        // 创建一个这个对象的代理对象MockClass1, 然后替换这个字段, 让我们的代理对象帮忙干活
        Class<?> activityManagerInterface;
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P){
            activityManagerInterface = Class.forName("android.app.IActivityTaskManager");
        }else {
            activityManagerInterface = Class.forName("android.app.IActivityManager");
        }

        Object proxy = Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(),
                new Class[]{ activityManagerInterface },
                new AMSProxy(mInstance));

        RefInvoke.setFieldObject("android.util.Singleton", singletonObject, "mInstance",proxy);
    }
}

public class AMSProxy implements InvocationHandler {
    private static final String TAG = AMSProxy.class.getSimpleName();
    private Object mActivityManager;

    public AMSProxy(Object mActivityManager) {
        this.mActivityManager = mActivityManager;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Log.d(TAG,method.getName());
        if("startActivity".equals(method.getName())){
            // 只拦截这个方法
            // 替换参数
            Intent raw;
            int index = 0;
            for(int i = 0;i<args.length;i++){
                if(args[i] instanceof Intent){
                    index = i;
                    break;
                }
            }
            raw = (Intent) args[index];
            Intent newIntent = new Intent();
            // 替身Activity的包名
            String stubPackage = raw.getComponent().getPackageName();
            // 把启动的Activity替换为StubActivity
            ComponentName componentName = new ComponentName(stubPackage, StubActivity.class.getName());
            newIntent.setComponent(componentName);

            // 把我们原始要启动的TargetActivity存起来
            newIntent.putExtra(AMSHookHelper.EXTRA_TARGET_INTENT,raw);
            // 替换掉intent
            args[index] = newIntent;
            Log.d(TAG,"hook success");
            return method.invoke(mActivityManager,args);
        }
        return method.invoke(mActivityManager,args);
    }
}

AMSProxy主要的工作是拦截startActivity方法,从参数中取出原有的Intent,替换为启动StubActivity的newIntent,同时把原有的Intent保存在newIntent中,后面把StubActivity换为TargetActivity需要用到。

第二步的实现代码,就是Hook startActivity 流程的下半场的方式,这里选择Hook ActivityThread的mInstrumentation对象:

public class AMSHookHelper {
    private static final String TAG = AMSHookHelper.class.getSimpleName();
    public static final String EXTRA_TARGET_INTENT = "extra_target_intent";
	...
    public static void hookInstrumentation(){
        // 先获取到当前的ActivityThread
        Object currentActivityThread = RefInvoke.invokeStaticMethod("android.app.ActivityThread","currentActivityThread");
        // 拿到原始的Instrumentation字段
        Instrumentation instrumentation = (Instrumentation) RefInvoke.getFieldObject(currentActivityThread,"mInstrumentation");
        // 创建代理对象
        InstrumentationProxy instrumentationProxy = new InstrumentationProxy(instrumentation);
        // 替换ActivityThread中的instrumentation为代理对象
        RefInvoke.setFieldObject(currentActivityThread,"mInstrumentation",instrumentationProxy);
    }
}
public class InstrumentationProxy extends Instrumentation {
    private static final String TAG = InstrumentationProxy.class.getSimpleName();
    // ActivityThread中原始的Instrumentation对象, 保存起来
    private Instrumentation mBase;

    public InstrumentationProxy(Instrumentation mBase) {
        this.mBase = mBase;
    }

    public Activity newActivity(ClassLoader cl, String className,
                                Intent intent)
            throws InstantiationException, IllegalAccessException,
            ClassNotFoundException {
        // 原来的intent
        ComponentName componentName = intent.getComponent();
        Log.d(TAG, "到此一游!cl:"+cl+" ,className="+className
                + " ,component_packageName="+componentName.getPackageName()
                + " ,component_className="+componentName.getClassName());
        // 获取存入的targetIntent,也就是本来要启动的intent
        Intent targetIntent = intent.getParcelableExtra(AMSHookHelper.EXTRA_TARGET_INTENT);
        // 为空则说明要启动的是一个在AndroidManifest中声明过的Activity,走正常流程
        if(targetIntent==null){
            return mBase.newActivity(cl,className,intent);
        }
       String newClassName = targetIntent.getComponent().getClassName();
        return mBase.newActivity(cl, newClassName, targetIntent);
    }

    public void callActivityOnCreate(Activity activity, Bundle bundle) {
        Log.d(TAG, "到此一游! bundle = " + bundle);

//        Class[] p1 = {Activity.class, Bundle.class};
//        Object[] v1 = {activity, bundle};
//        RefInvoke.invokeInstanceMethod(
//                mBase, "callActivityOnCreate", p1, v1);
        mBase.callActivityOnCreate(activity,bundle);
    }

}

InstrumentationProxy主要的工作是拦截newActivity方法,因为callActivityOnCreate方法中没有Intent参数,从Intent参数中取出原有的Intent,替换className和Intent参数为我们本来要启动的TargetActivity的className和Intent对象。

最后在MainActivity中的attachBaseContext方法中执行AMSHookHelper.hookAMN();AMSHookHelper.hookInstrumentation();

public class MainActivity extends AppCompatActivity {
    @Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
        try{
            AMSHookHelper.hookAMN();
            AMSHookHelper.hookInstrumentation();
        }catch (ClassNotFoundException | NoSuchMethodException e){
            e.printStackTrace();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.turn_to_other_activity).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                startActivity(intent);
            }
        });
    }
}

源码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/544730.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Cesium源码分享--标绘

Cesium标绘插件 在线api文档说明 在线体验地址1&#xff08;三维框架内&#xff09; 在线体验地址2 更多案例地址 免费gis数据 ps&#xff1a;如果可以的话&#xff0c;希望大家能给我个star&#xff0c;好让我有更新下去的动力&#xff1b; 实现原理&#xff1a; 其中实…

RSS订阅无需代码连接飞书自建的方法

RSS订阅用户使用场景&#xff1a; 公司为倡导员工阅读文化以及提高自身素质&#xff0c;通过RSS订阅一些书籍&#xff0c;然后由相关部分负责人每日推送到部门群&#xff0c;便员工们能够随时获取到最新的读书消息。但负责人时常会忘记&#xff0c;且人工手动复制粘贴多项信息比…

使用底层代码(无框架)实现卷积神经网络理解CNN逻辑

首先将数据集放入和底下代码同一目录中&#xff0c;然后导入一些相关函数的文件cnn_utils.py&#xff1a; import math import numpy as np import h5py import matplotlib.pyplot as plt import tensorflow as tf from tensorflow.python.framework import ops def load_data…

Win10搭建Docker Desktop并安装vim

Win10搭建Docker Desktop 1 介绍 Docker Desktop是适用于Windows的Docker桌面&#xff0c;是Docker设计用于在Windows 10上运行。它是一个本地 Windows 应用程序&#xff0c;为构建、交付和运行dockerized应用程序提供易于使用的开发环境。Docker Desktop for Windows 使用 Wi…

shell脚本基本操作及案例

本文介绍了shell脚本的基本建立过程&#xff0c;并举了4个案例。关键是例3的shell脚本检测局域网ip地址机器是否宕机&#xff0c;例4的shell脚本获取本机ip地址 一、第一个shell脚本 1、定义解释器 #&#xff01;/bin/bash echo "hello world" #! 是一个约定的标记…

今天面了个京东拿28K 出来的,让我见识到了测试界的天花板

今年的春招基本已经结束了&#xff0c;很多小伙伴收获不错&#xff0c;拿到了心仪的 offer。 各大论坛和社区里也看见不少小伙伴慷慨地分享了常见的软件测试面试题和八股文&#xff0c;为此咱这里也统一做一次大整理和大归类&#xff0c;这也算是划重点了。 俗话说得好&#…

【Android】基于Airtest实现大麦网app自动抢票程序

0x01 缘起 疫情结束的2023年5月&#xff0c;大家对出去玩都有点疯狂&#xff0c;歌手们也扎堆开演唱会。但演唱会多&#xff0c;票一点也不好抢&#xff0c;抢五月天的门票难度不亚于买五一的高铁票。所以想尝试找一些脚本来辅助抢票&#xff0c;之前经常用selenium和request做…

探索未来智能交通:网联汽车与汽车互联

网联汽车是指配备多种传感器和通信设备&#xff0c;并且能够接入互联网的汽车。这种汽车可以与外部环境进行交互&#xff0c;并利用各种技术&#xff08;如 GPS 导航、娱乐系统、诊断传感器和通信工具等&#xff09;实现数据的传输和接收。 网联汽车能够与其他车辆、交通基础设…

ChatGPT 引发AI服务器霸屏?AI服务器和普通服务器不同在哪?

​  近阶段&#xff0c;由于 ChatGPT 的横空问世&#xff0c;引发 AI 服务器再一次热潮来袭。随之而来的是&#xff0c;越来越多的企业和机构开始关注 AI 服务器和普通服务器之间的区别和对比。那么AI服务器到底是什么意思&#xff0c;它与普通服务器相比又有哪些差异呢? AI…

MySQL高级——第15章_锁

第15章_锁 1. 概述 锁是计算机协调多个进程或线程并发访问某一资源的机制。在程序开发中会存在多线程同步的问题&#xff0c;当多个线程并发访问某个数据的时候&#xff0c;尤其是针对一-些敏感的数据(比如订单、金额等)&#xff0c;我们就需要保证这个数据在任何 时刻最多只…

动态规划进阶

文章目录 状压dp小国王玉米田炮兵阵地 状压dp 小国王 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter;public class Main{static BufferedReader br new Bu…

Day25力扣刷題

131.分割回文串 此題思維和前幾題不一樣&#xff0c;思維容量更大&#xff0c;主要在於返回的時候&#xff0c;還是會取值。 運行代碼&#xff1a; class Solution:def partition(self, s: str) -> List[List[str]]:result[]tt[]def backtrack(s,index):if len(s)index:res…

node.js PM2部署项目

pm2 是什么 pm2 是一个守护进程管理工具,它能帮你守护和管理你的应用程序。通常一般会在服务上线的时候使用 pm2 进行管理。本文围绕以下重点进行讲解&#xff1a;安装pm2&#xff1b;命令行部署到PM2&#xff1b;PM2查看日志等命令&#xff1b;PM2进行负载均衡&#xff1b;PM…

Python心经(4)

这节记录一些内置模块的使用 目录 hashlib模块&#xff0c;&#xff0c;加密用 json模块 os模块 一个实用的案例&#xff1a; os模块的找文件的操作 随机生成random模块 时间相关模块 time&#xff0c;datetime hashlib模块&#xff0c;&#xff0c;加密用 以md5加密…

什么是Spring Cache?Spring项目如何使用?

前言 目前Spring Cloud微服务在Web项目中占据了主流地位&#xff0c;如果去面试关于Spring Cloud的岗位时&#xff0c;面试官一般都会提问你的项目是如何优化的&#xff0c;从哪些方面入手去优化。而缓存技术绝对是项目必不可少的&#xff0c;所以我们必须掌握好Java项目的缓存…

unity 3种办法实现血条效果并实现3d世界血条一直看向摄像机

普通血条栏: 渐变色血条栏: 缓冲血条栏: 3D场景血条栏跟随玩家移动: 普通血条栏: 在Canvas下创建一个空物体HP bar,在空物体下方创建3个Image,分别为血条框bar 黑色,最大HP maxHP 白色,和当前HP currentHP 红色。(PS:注意先后顺序以调整显示的图层) 效果: …

java中排序

1.传统比较器格式 2.stream 3.结果 4.源码 List<String> list Arrays.asList("201305", "200305", "199009", "200208");Collections.sort(list, new Comparator<String>() {Overridepublic int compare(String o1, Stri…

SpringMVC 万字通关

文章目录 1. 什么是 Spring MVC?1.1 MVC 定义1.2 MVC 和 Spring MVC 的关系 2. Spring MVC 有什么用 ?3. 如何学 Spring MVC ?3.1 Spring MVC 的创建3.2 实现连接功能3.2.1 RquestMapping 详解1. RequestMapping 支持什么请求?2. 请求限定3. GetMapping 和 PostMapping4. c…

Sharding-Sphere系列-主从配置和分库分表

主从配置和分库分表 Sharding-Sphere组成 Sharding-JDBC Sharding-Proxy Sharding-Sidecar&#xff08;TODO&#xff09; Sharding-JDBC表的概念 逻辑表 广播表 绑定表 Sharding-JDBC中的分片策略 自动分片算法 标准分片算法 复合分片算法 自定义分片算法 分布式…

Mybatis-Plus 自动属性填充与自定义Insert into语句顺序

前言&#xff1a;系统中使用了Mybatis-Plus 自动属性填充为实体统一进行属性的填值&#xff0c;在Mapper的xml 文件中 insert into 语句 使用 <if test"id ! null">id,</if> 进行判断会发现该属性是空的&#xff0c;明明已经为改字段进行了属性的自动填充…