zoukankan      html  css  js  c++  java
  • Android使用AOP

    这里不讲aop的概念,网上资料很多,这里只讲如何配置aop和自定义plugin。
    1、使用场景
    在android中,有些业务是公共的,例如:登录判断、获取权限、网络判断等一些公用的业务逻辑,这些都可以使用aop编程。在未使用aop的时候,登录可能会像下面那样写:

    工具类:ASUtils
    /**
     * 如果未登录,返回true,并且跳转到登录页
     *
     * @return boolean
     */
    public static boolean isUnLogined() {
        if (AppContext.component().userManager().hasUser()) { // 是否已登录
            return false;
        }
        ToastHelper.showMessage(R.string.login_required);
        IntentUtils.actionWeChatLogin(AppManager.currentActivity(), IReturnType.Mine);
        return true;
    }
    
    Activity或者Fragment调用:
    private void toUserPage() {
        if (ASUtils.isUnLogined()) return;
        IntentUtils.startActivity(getActivity(), UserActivity.class);
    }
    
    

    而使用Aop后,编码会变得简洁很多,只需要在方法上添加@LoginFilter即可:

    @LoginIntercept
    private void toUserPage() {
        IntentUtils.startActivity(getActivity(), UserActivity.class);
    }
    

    2、配置Aspectj
    在module或者app中配置Aspectj会比较麻烦,目前有人已经在github开源了支持Android的Aspectj库(Hugo),这里不讲它的使用,具体可以去github查看使用方法。
    首先,在根目录build.gradle添加aspectj的支持:

    buildscript {
        
        repositories {
            google()
            jcenter()
        }
        dependencies {
            classpath 'com.android.tools.build:gradle:3.1.0'
            classpath 'org.aspectj:aspectjtools:1.8.13'    // add
            classpath 'org.aspectj:aspectjrt:1.8.13'       // add
            // NOTE: Do not place your application dependencies here; they belong
            // in the individual module build.gradle files
        }
    }
    

    其次,在app目录下的build.gradle添加支持

    import org.aspectj.bridge.IMessage
    import org.aspectj.bridge.MessageHandler
    import org.aspectj.tools.ajc.Main
    
    final def log = project.logger
    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;
                }
            }
        }
    }
    dependencies {
        implementation 'org.aspectj:aspectjrt:1.8.13'
    }
    

    上面log是为了方便在build project的时候查看相关日志,定位是否build failed的信息。如果需要单独一个module来做aop的lib话,在module下build.gradle添加

    import org.aspectj.bridge.IMessage
    import org.aspectj.bridge.MessageHandler
    import org.aspectj.tools.ajc.Main
    project.android.libraryVariants.all { variant ->
        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)]
    
            MessageHandler handler = new MessageHandler(true);
            new Main().run(args, handler)
    
            def log = project.logger
            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:
                    case IMessage.INFO:
                        log.info message.message, message.thrown
                        break;
                    case IMessage.DEBUG:
                        log.debug message.message, message.thrown
                        break;
                }
            }
        }
    }
    dependencies {
        implementation 'org.aspectj:aspectjrt:1.8.13'
    }
    

    你会发现,app和module的配置都差不多,唯一的区别在于一个是applicationVariants
    ,一个是libraryVariants,其它都是一样。Aspectj配置已经完成,可以Sync Now是否Success。
    参数说明

    参数 说明
    -showWeaveInfo 输入AJC 编译信息
    -1.8 声明AJC 支持AspectJ 1.5 版本
    -inpath 需要编译的.class 文件目录(包含Jar文件)
    -classpath 指定哪里可以找到用户的class 文件
    -aspectpath aspect 编译的Jar文件或者目录路径
    -d 编译后输出的目录
    -bootclasspath 编译时修改本地的bootcloasspath

    3、定义登录aop
    添加LoginFilter的注解类

    package com.fomin.aop.login.aspect;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * Created by Fomin on 2018/8/30.
     */
    @SuppressWarnings("CheckStyle")
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface LoginIntercept {
        int actionDefine() default 0;
    }
    

    注解相关的AspectJ的实现类

    package com.fomin.aop.login.aspect;
    
    import android.content.Context;
    import android.util.Log;
    
    import com.fomin.aop.execption.AnnotationException;
    import com.fomin.aop.execption.NoInitException;
    import com.fomin.aop.login.core.ILogin;
    import com.fomin.aop.login.core.LoginAssistant;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.Signature;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    
    /**
     * Created by Fomin on 2018/8/30.
     */
    @Aspect
    public class LoginInterceptAspect {
    
        private static final String TAG = "LoginInterceptAspect";
    
        @Pointcut("execution(@com.fomin.aop.login.aspect.LoginIntercept private * *..*.*(..))")//这里使用private是因为方法中全部是private,也可以去除
        public void loginFilter() {
        }
    
        @SuppressWarnings("CheckStyle")
        @Around("loginFilter()")
        public void aroundLoginPoint(ProceedingJoinPoint joinPoint) throws Throwable {
            ILogin login = LoginSDK.getInstance().getLogin();
            if (login == null) {
                throw new NoInitException("LoginSDK没有初始化!");
            }
    
            Signature signature = joinPoint.getSignature();
            if (!(signature instanceof MethodSignature)) {
                throw new AnnotationException("LoginIntercept注解只能用于方法上");
            }
    
            MethodSignature methodSignature = (MethodSignature) signature;
            Log.d("Aspect", String.valueOf(methodSignature.getName()));
            Log.d("Aspect", String.valueOf(methodSignature.getMethod() == null));
            LoginIntercept loginIntercept = methodSignature.getMethod().getAnnotation(LoginIntercept.class);
            if (loginIntercept== null) {
                return;
            }
            Context param = LoginSDK.getInstance().getContext();
            if (login.isLogin(param)) {
                joinPoint.proceed();
            } else {
                login.login(param, loginFilter.actionDefine());
            }
        }
    }
    

    注意:在LoginFilterAspect 类中如果有用到Context,可直接使用joinPoint.getTarget()类型转换成Context,这里是由于项目使用了databinding,部分getTarget()获取到的值不能强转为Context,所以这里用的MyApplication获取的Context。
    ILogin接口:

    ILogin:接口
    package com.huaying.aop.login.core;
    
    import android.content.Context;
    
    /**
     * Created by Fomin on 2018/8/29.
     */
    public interface ILogin {
    
        /**
         * 登录事件接收
         * @param context Context
         * @param actionDefine 登录操作
         */
        void login(Context context, int actionDefine);
    
        /**
         * 判断是否登录
         * @param context Context
         * @return
         */
        boolean isLogin(Context context);
    }
    

    LoginSDK 类:

    package com.huaying.aop.login.core;
    
    import android.content.Context;
    
    /**
     * Created by Fomin on 2018/8/30.
     */
    public class LoginSDK {
        private static LoginSDK instance;
    
        private LoginSDK() {
        }
    
        public static LoginSDK getInstance() {
            if (instance == null) {
                synchronized (LoginSDK.class) {
                    if (instance == null) {
                        instance = new LoginSDK();
                    }
                }
            }
            return instance;
        }
    
        private ILogin login;
        private Context context;
        
        /**
         * 初始化
         *
         * @param context Context
         * @param iLogin  登录事件
         */
        public void init(Context context, ILogin iLogin) {
            this.context = context;
            this.login = iLogin;
        }
    
        public ILogin getLogin() {
            return login;
        }
    
        public Context getContext() {
            return context;
        }
    }
    
    

    相关aop lib已经编写完成,接下来就是使用,需要在application中初始化LoginSDK

    public class AppContext implements IModuleConfig, IAppLife {
        private static final String TAG = AppContext.class.getSimpleName();
        public AppContext() {
        }
    
        @Override
        public void attachBaseContext(Context base) {
        }
    
        @Override
        public void injectAppLifecycle(Context context, List<IAppLife> lifeList) {
            lifeList.add(this);
        }
    
        @Override
        public void onCreate(BaseApplication application) {
            LoginSDK.getInstance().init(application, login);
        }
    
    
        private ILogin login = new ILogin() {
            @Override
            public void login(Context context, int actionDefine) {
                switch (actionDefine) {
                    case 0:
                        ToastHelper.showMessage(R.string.login_required);
                        IntentUtils.actionWeChatLogin(AppManager.currentActivity(), IReturnType.Mine);
                        break;
                    case 1:
                        ToastHelper.showMessage(R.string.login_required);
                        break;
                }
            }
    
            @Override
            public boolean isLogin(Context context) {
                return component().userManager().hasUser();
            }
        };
    }
    

    在具体的业务中使用注解

    @LoginIntercept
    private void toUserPage() {
        IntentUtils.startActivity(getActivity(), UserActivity.class);
    }
    

    大功告成,可以优雅的使用登录拦截了,由于时间有限,像权限请求也像登录那样处理即可,可能会接下来的文章继续写权限请求Aop,还有一个需要注意点,在release包混淆中,不要把注解类混淆,不然会报NPE错误

    # aop注解
    -adaptclassstrings
    -keepattributes InnerClasses, EnclosingMethod, Signature, *Annotation*
    -keepnames @org.aspectj.lang.annotation.Aspect class * {
        ajc* <methods>;
    }
    -keepclassmembers class ** {
        @com.huaying.aop.login.aspect.LoginFilter <methods>;
    }
    
  • 相关阅读:
    在Linux下删除文件及文件夹(rm)
    修改Linux文件权限
    文件分页显示(ls -al |more)
    linux的文件权限
    Linux中的重启(reboot)
    linux关机前同步数据(sync)
    hdu4990 Reading comprehension 矩阵快速幂
    hdu4965 Fast Matrix Calculation 矩阵快速幂
    hdu4847 Wow! Such Doge! KMP
    hdu4705 Y 树形DP
  • 原文地址:https://www.cnblogs.com/fomin/p/9665400.html
Copyright © 2011-2022 走看看