Android 进阶-设计模式-静态代理和动态代理(aop原理)

可以从小明通过代购买电脑的例子来描述静态代理和动态代理的实现过程
1、使用静态代理

1、定义代理对象和真实对象的公共接口;
2、真实对象实现公共接口中的方法;
3、代理对象实现公共接口中的方法,并把方法的逻辑转发给真实对象

1.1、定义代理对象和真实对象的公共接口
/**
 * 公共接口
 */
public interface IComputer {
    void buy();//买电脑
}

1.2、真实对象实现公共接口中的方法;
/**
 * //被代理人,想要买电脑的小明
 */
public class Xiaoming implements IComputer{
    @Override
    public void buy() {
        Log.i("TAG","买了电脑");
    }
}

1.3、代理对象实现公共接口中的方法,并把方法的逻辑转发给真实对象

/**
 * 代理人  代理小明买电脑
 */
public class ComputerAngcy implements IComputer {

 private   IComputer xiaoming;//持有一个被代理人(小明)的引用

    public ComputerAngcy(IComputer computer){
        this.xiaoming=computer;
    }

    @Override
    public void buy() {
        xiaoming.buy();
    }
}

        Xiaoming xiaoming = new Xiaoming();
        ComputerAngcy computerAngcy = new ComputerAngcy(xiaoming);
        computerAngcy.buy();

2、 使用动态代理

1、定义代理对象和真实对象的公共接口;(与静态代理步骤相同)
2、真实对象实现公共接口中的方法;(与静态代理步骤相同)
3、定义一个实现了InvocationHandler接口的动态代理类;
4、通过Proxy类的newProxyInstance方法创建代理对象,调用代理对象的方法。

2、3步骤1和步骤2 和静态代理一样直接从步骤3 说起 ( 实现了InvocationHandler接口的动态代理类)

/**
 * 实现了InvocationHandler接口的动态代理类
 */
public class DynamicProxy<T>implements InvocationHandler {

    private T mObject;//真实对象的引用

    public DynamicProxy(T mObject){
        this.mObject=mObject;
    }

    @Override
    public Object invoke(Object o, Method method, Object[] args) throws Throwable {
        //通过反射调用真实对象的方法
        Object result = method.invoke(mObject, args);
        return result;
    }
}
2、4通过Proxy类的newProxyInstance方法创建代理对象,调用代理对象的方法。

     IComputer xiaoming = new Xiaoming();
        //构造一个动态代理
        InvocationHandler dynamicProxy = new DynamicProxy(xiaoming);
        //获取被代理类小明的ClassLoader
        ClassLoader classLoader = xiaoming.getClass().getClassLoader();
        //1、通过Proxy类的newProxyInstance方法动态构造一个代理人房产中介
        IComputer computerAgency = (IComputer) Proxy.newProxyInstance(classLoader, new Class[]{IComputer.class}, dynamicProxy);
        //调用代理对象的方法
        computerAgency.buy();

Proxy的newProxyInstance方法会根据传入的类加载器动态生成代理对象实例,生成的代理对象会继承Proxy类并实现传入的接口列表,这里的类加载器是小明的ClassLoader,即真实对象的类加载器,而接口列表则是IComputer,传入的IComputer的Class对象,除了这个两个参数,还传入了动态代理类InvocationHandler实例,这样Proxy类在创建代理对象的实例时就会把这个InvocationHandler引用传给代理对象,接下来当我们调用代理对象的方法时,这个方法的处理逻辑就会委托给InvocationHandler实例的invoke方法执行,invoke方法中就会通过反射调用我们真实对象的方法。

Aop原理就是动态代理, 可以在invoke方法体 在正真代理对象执行方法前后去执行想要的操作,如:登录拦截

登录接口ILogin 验证登录 只有登陆后才能执行该方法

package uidemo.amn.com.aopdemo;

public interface ILogin {
    /**
     * 验证登录 只有登陆后才能执行该方法
     */
    void isLogin();
}

创建MyInvocationHandler 来监听代理类 方法的执行

public class MyInvocationHandler implements InvocationHandler {

    Context mContext;
    Object target;//持有要代理的对象,通过构造传递过来

    public  MyInvocationHandler (Object context){
        this.mContext= (Context) context;
        this.target=context;
    }
    /**
     * 监听代理类 方法的执行
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object invoke=null;
    //  在method.invoke()方法执行前后可以加入自己想要的操作,日志打印,登录判断拦截
        if(Constains.isLogin){
            //已经登录
            invoke=method.invoke(target,args);
        }else {
            //没登录
            Intent intent =new Intent();
            intent.setClass(mContext,LoginActivity.class);
            mContext.startActivity(intent);
        }
        return invoke;
    }
}



//简单存储登录状态
public class Constains {
    public static boolean isLogin=false;
}

在测试的activity中 使用:


public class MainActivity extends AppCompatActivity  implements ILogin {

    ILogin proxy;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //通过这个API创建一个代理对象,第一个类加载器,该类对象一定是第二个参数的实现类,
        // 第二个参数,代理的目标类,第三个回调处理 可以监听代理类每个方法的执行
        proxy = (ILogin) Proxy.newProxyInstance(MainActivity.this.getClassLoader(),
                new Class[]{ILogin.class},new MyInvocationHandler (this));
    }


    public void jumpActivity(View view) {
        proxy.isLogin();
    }



    @Override
    public void isLogin() {
        Intent intent =new Intent();
        intent.setClass(this,TestActivity.class);
        startActivity(intent);
    }
}

通常做APO拦截一般使用 aspectj 。

app gradle 配置

    buildscript {
        repositories {
            mavenCentral()
        }
        dependencies {
            classpath 'org.aspectj:aspectjtools:1.8.8'
            classpath 'org.aspectj:aspectjweaver:1.8.8'
        }
    }

    implementation 'org.aspectj:aspectjrt:1.8.11'


 final def variants = project.android.applicationVariants
    variants.all { variant ->
        if (!variant.buildType.isDebuggable()) {
            log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
            return;
        }

        JavaCompile javaCompile = variant.javaCompile
        javaCompile.doLast {
            String[] args = ["-showWeaveInfo",
                             "-1.8",
                             "-inpath", javaCompile.destinationDir.toString(),
                             "-aspectpath", javaCompile.classpath.asPath,
                             "-d", javaCompile.destinationDir.toString(),
                             "-classpath", javaCompile.classpath.asPath,
                             "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
            log.debug "ajc args: " + Arrays.toString(args)

            MessageHandler handler = new MessageHandler(true);
            new Main().run(args, handler);
            for (IMessage message : handler.getMessages(null, true)) {
                switch (message.getKind()) {
                    case IMessage.ABORT:
                    case IMessage.ERROR:
                    case IMessage.FAIL:
                        log.error message.message, message.thrown
                        break;
                    case IMessage.WARNING:
                        log.warn message.message, message.thrown
                        break;
                    case IMessage.INFO:
                        log.info message.message, message.thrown
                        break;
                    case IMessage.DEBUG:
                        log.debug message.message, message.thrown
                        break;
                }
            }
        }
    }

在 project gradle

    classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4'

Login

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public  @interface Login {
    int loginDefine() default 0;
}

LoginAspect


@Aspect
public class LoginAspect {

    @Pointcut("execution(@com.csair.pdac.anotation.Login * * (..))")
    public void pointcutLogin(){}


    @Around("pointcutLogin()")
    public void aroundLogin(ProceedingJoinPoint joinPoint) throws Throwable{

        Log.i("aaa","调用前");
        //先得到方法的签名methodSignature,然后得到@Login注解,如果注解为空,就不再往下走。
        Signature signature = joinPoint.getSignature();
        if (!(signature instanceof MemberSignature)){
            throw new RuntimeException("该注解只能用于方法上");
        }

        MethodSignature methodSignature = (MethodSignature) signature;
        Login login = methodSignature.getMethod().getAnnotation(Login.class);
        if (login == null){
            return;
        }

        //判断是否登录,登录了就会继续执行方法体调用方法直到完成,如果没有登录,就拦截跳到登录页面
//AopUtil.getInstance().isLogin
        final Context context = AopUtil.getInstance().getContext();
        User user = SpUtils.getObject(context, User.class);
        if (user != null) {
            //方法执行
            joinPoint.proceed();
        } else {

            switch (login.loginDefine()) {
                case 0:
                    Intent intent = new Intent(context, LoginActivity.class);
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK );
                    context.startActivity(intent);
                    break;
                    // case1 case2 后面预留
                case 1:
                    Toast.makeText(context, "您还没有登录,请登陆后执行", Toast.LENGTH_SHORT).show();
                    break;

                case 2:
                    Toast.makeText(context, "操作失败,因为您还没有登录!", Toast.LENGTH_SHORT).show();

                    break;
            }

        }
    }


}

使用

   /**
     * 已登录 则直接执行打印,没登陆就会拦截
     * @param context
     */

    @Login(loginDefine = 0)
    public void jumpLogin(Activity context) {
        Log.i("aaa", "login");

    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容