Agente dinámico y aplicación práctica

Primero revisemos el modelo de proxy

El modo de proxy se divide en proxy dinámico y proxy estático. Es relativamente sencillo de escribir. Primero, agregue el código:

public interface Goal {
    
    
    void sayHello();//定义一个接口,声明好要做的事儿
}

Y luego lograr su verdadero propósito

public class RealGoal implements Goal {
    
    

    @Override
    public void sayHello() {
    
    
        System.out.println("hello! think you very much!");
    }
}

En este momento queremos imprimir ¡hola! ¡Te lo creo !, solo necesitamos nuevo RealGoal (). SayHello (); para que solo podamos ejecutar el código, pero como está en modo proxy, definitivamente no podemos hágalo de esta manera y busque un agente para ejecutarlo. Así que cree un agente

public class ProxyGoal implements Goal {
    
    

    private Goal goal;

    public ProxyGoal(Goal goal) {
    
    
        this.goal = goal;
    }

    @Override
    public void sayHello() {
    
    
        goal.sayHello();
    }
}

¿Cómo aplicarlo específicamente?

public class Test {
    
    

    public static void main(String[] args) {
    
    

        Goal realGoal = new RealGoal();

        Goal proxyGoal = new ProxyGoal(realGoal);
        proxyGoal.sayHello();
    }
}

Resultado de la ejecución:
Inserte la descripción de la imagen aquí
** Cuando comencé a aprender, me pregunto si ustedes tienen la sensación de quitarse los pantalones y tirarse un pedo sin hacer nada como yo. Como queremos ejecutar sayHello (), solo usamos realGoal.sayHello () y se acabó. ¿Por qué? ¿Quieres crear una clase de proxy? ? ? ? ? ? ? ? ** Antes de responder esta pregunta, ¡echemos un vistazo a cómo escribir un proxy dinámico!

public class GoalHandler implements InvocationHandler {
    
    

    Object object;

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

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        System.out.println("I'm Proxy, I'm invoking...");
        method.invoke(object, args);
        System.out.println("invoke end!");
        return null;
    }
}

Ejecutar prueba

    public static void main(String[] args) {
    
    

        Goal realGoal = new RealGoal();

        InvocationHandler goalHandler = new GoalHandler(realGoal);

        Goal dynamicProxyGoal = (Goal) Proxy.newProxyInstance(realGoal.getClass().getClassLoader(), realGoal.getClass().getInterfaces(), goalHandler);
        dynamicProxyGoal.sayHello();
    }

De hecho, los agentes dinámicos son los mismos y son superfluos, por lo que el patrón de diseño debe combinarse con los escenarios de uso para que tenga sentido. Este modelo de agencia es muy simple. Como sugiere el nombre, un agente adicional equivale a cambiar directamente de un empleado de base a un líder. Por ejemplo, si desea enviar un mensajero, debe tratar con el mensajero usted mismo, pero el modelo de agencia te permite tener un Bro (proxy), si tienes algo, solo pídele a tu hermano pequeño que lo maneje, y luego a tu hermano pequeño que busque al mensajero; por supuesto, esto también puede ser comprando cigarrillos, no importa cuál sea la cosa específica, el punto es que hay un hermano pequeño adicional, y este hermano pequeño está completamente en tus manos.

Utilice el escenario 1: Proxy dinámico + reflexión para implementar la tecnología de gancho

La tecnología de gancho es muy simple. El llamado punto de gancho significa buscar una puerta trasera. Siempre que encuentre el punto de gancho, puede simular una escena. Hay un ActivityManagerNative en Android. Este objeto es el objeto que se comunica con AMS. Todos los métodos startActivity están al final. Debe enviarse a AMS a través del objeto de carpeta que contiene. Los estudiantes que no hayan leído el código fuente pueden no entender esta parte, pero no importa. Este no es el punto; el punto Es que este tipo de objetos normalmente están fuera de nuestro alcance y no podemos usarlos directamente. Ellos; pero a través del proxy dinámico + reflexión, podemos pasar por la puerta trasera, podemos escuchar alguna información de startActvity, para hacer nuestra propia manos y pies, primero para escribir una pequeña
estructura de código de demostración es muy simple, el sistema representa el nivel del sistema Clase, no podemos usarlo en el desarrollo normal; solo podemos usar la actividad de la capa de aplicación. Como se muestra en la figura,
Inserte la descripción de la imagen aquí
La actividad es simular la actividad android.app.Activity en nuestro Android. Veamos primero la estructura, en system

public interface IActivityManager {
    
    

    int startActivity(String intent);
}

public class ActivityManagerNative {
    
    

    public static IActivityManager binder = new ActivityManagerImpl();

    public static IActivityManager gDefault() {
    
    
        return binder;
    }
}
public class Log {
    
    

    public static void i(String tag, String value) {
    
    
        System.out.println(tag+" : "+value);
    }
}
public class ActivityManagerImpl implements IActivityManager {
    
    
    @Override
    public int startActivity(String intent) {
    
    
        System.out.println("我是AMS,将要启动activity对象是: "+intent);
        return 0;
    }
}

Veamos la actividad en el paquete de la aplicación.

public class Activity {
    
    

    public void statActivity(String i) {
    
    
        ActivityManagerNative.gDefault().startActivity(i);
    }

}

Las anteriores son todas las clases existentes. En este momento, personalizamos una actividad y ejecutamos el método startActvity para decirle al sistema qué actividad queremos iniciar.

public class MainActivity extends Activity {
    
    

    public static void main(String[] args) {
    
    

        MainActivity mainActivity = new MainActivity();
        mainActivity.statActivity("MainActivity");

    }
}

Resultados del:Inserte la descripción de la imagen aquí

¡Entonces aquí viene el problema! , ¿Cómo cambio el resultado de ejecución cuando no afecta el desarrollo normal? Luego escriba una clase, que contenga una lógica para detectar si la actividad a iniciar contiene NeedLogin, si contiene NeedLogin, imprimirá LoginActivity directamente, si no lo contiene, imprimirá normalmente sin cambios.

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class HookUtil {
    
    

    private static final String TAG = "HookUtil";

    /**
     * 获取IActivityManager对象  将其替换掉
     * 这里需要动态代理;
     * 动态代理也是个时髦的技术;
     */
    public void hookActivityStart() {
    
    
        try {
    
    
            //获取ActivityManager类
            Class<?> activityManagerClass = Class.forName("com.example.myapplication.system.ActivityManagerNative");
            //获取其IActivityManagerSingleton属性
            Field iActivityManagerSingleton = activityManagerClass.getDeclaredField("binder");
            iActivityManagerSingleton.setAccessible(true);

            Object binder = iActivityManagerSingleton.get(activityManagerClass);

            HookHandler hookHandler = new HookHandler(binder);
            Object proxyInstance = Proxy.newProxyInstance(binder.getClass().getClassLoader(), binder.getClass().getInterfaces(), hookHandler);

            //proxyInstance代理IActivityManager
            iActivityManagerSingleton.set(activityManagerClass, proxyInstance);
        } catch (ClassNotFoundException e) {
    
    
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
    
    
            e.printStackTrace();
        } catch (IllegalAccessException e) {
    
    
            e.printStackTrace();
        }
    }

    class HookHandler implements InvocationHandler {
    
    

        private Object object;

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

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
            //拦截startActivity()方法
            String name = method.getName();
            Log.i(TAG, "调用" + name + "方法了");
            if ("startActivity".contains(name)) {
    
    
//                Log.i(TAG, "调用startActivity方法了");
                //获取intent参数
                for (int i = 0; i < args.length; i++) {
    
    
                    Object arg = args[i];
                    if (arg instanceof String) {
    
    
                        if(((String) arg).contains("NeedLogin"))//此处判断逻辑,如果里面包含NeedLogin,则跳转LoginActivity,否则就正常执行
                            args[i] = "LoginActivity";
                    }
                }
            }

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

}

Luego, cuando escribamos la prueba, escríbelo así

public class MainActivity extends Activity {
    
    

    public static void main(String[] args) {
    
    

        HookUtil hookUtil = new HookUtil();
        hookUtil.hookActivityStart();

        MainActivity mainActivity = new MainActivity();
        mainActivity.statActivity("MainActivity");
        mainActivity.statActivity("MineActivityNeedLogin");

    }
}

Resultados del:
Inserte la descripción de la imagen aquí

Podemos ver que el conjunto de resultados de ejecución que contiene NeedLogin en la cadena ha cambiado por completo. Este es el final del gancho implementado por el típico proxy dinámico + reflexión, plug-in general, reparación en caliente o ciertos escenarios.

AOP (Programación Orientada a Aspectos)

El nombre completo de AOP es Programación orientada a aspectos, es decir, programación orientada a aspectos (también conocida como programación orientada a aspectos). Es un complemento de la programación orientada a objetos (POO) y se ha convertido en un método de programación relativamente maduro. Es una especie de pensamiento de programación. La inyección de código es una parte importante de AOP: AOP se puede usar para enterrar registros, monitorear el desempeño, controlar el acceso dinámico e incluso depurar código. Una forma de realizar AOP es el modelo de proxy dinámico, que continuará ...

Supongo que te gusta

Origin blog.csdn.net/english111999/article/details/114044025
Recomendado
Clasificación