Android AOP拯救混乱的代码架构

这篇具有很好参考价值的文章主要介绍了Android AOP拯救混乱的代码架构。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

为什么要写这篇文章:

如今各大平台能搜xx框架如何使用的一大堆,但提及如何利用写出优雅的代码的文章却少之又少。所以本文主要提供一个思路来优化代码,也算抛砖引玉。若各位有不同看法或意见,可以在评论区提出,或者私信。博主看到会及时回复。

本文介绍:
  1. 拿过来直接可以运行

  1. 没有多余的废话,不会涉及到原理。先看到效果,用起来再去探究原理。

  1. 对小白友好

场景:

一个线上需要登录的App,许多函数在使用之前都要校验用户是否已登录。于是最直接的方案就是在这些函数中增加大量的if else来进行判断用户是登录......如下所示:

//需要验证的登录状态的函数
    private void sendMsg(String msg){
        //Manger.getInstance().getState() 获取登录状态
        switch (Manger.getInstance().getState()){
            case -1://用户被强制下线
                //提醒用户 账号密码可能已泄漏
                //然后跳转到登录页面
                return;
            case 0://用户未登录
                //跳转登录页面
                return;
            case 1://用户已登录
                sendImMsg(msg);//验证通过发送消息
                break;
        }
    }

这种使用if else用来控制权限,着实不太优雅。且随着项目业务逐渐增多,管理登录状态的类(Manger)会跟多处代码耦合,浸入量极大。要解决这个问题,我们可以使用AOP思想来实现对登录模块的控制。(若不清楚AOP思想,可以先去了解一下大概意思)这里我们使用Aspectj这个框架来优化整段代码。

引入插件与依赖
  1. 在Project中的build.gradle中引入

buildscript {
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:7.0.2"
        
        //引入Aspectj插件 非常重要!!
        classpath 'org.aspectj:aspectjtools:1.8.9'
        classpath 'org.aspectj:aspectjweaver:1.8.9'
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

2.在Module中的build.gradle中引入

dependencies {
    //引入Aspectj框架
    implementation 'org.aspectj:aspectjrt:1.8.13'
}

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;
            }
        }
    }
}
编写注解

每次调用函数时,我们需要先检查检查用户是否登录。如果用注解自动帮我们去检查,那么代码会优雅很多。这里我们定义一个注解,告诉Aspect 切入点在哪。

@CheckLogin 作用:加了@CheckLogin注解的函数运行之前都会自动去检查用户是否登录

//注意此处的包名 后面需要用,若你之间复制到自己项目中,包名改了,注解起不了作用
package com.chj.chjaj.loginmode.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/***
 * 作者:chj233
 * 时间:2023/3/17 23:16
 * 描述:用于检查登录
 */
@Target(ElementType.METHOD)//此注解只能写在函数上
@Retention(RetentionPolicy.RUNTIME)//注解生效 运行时
public @interface CheckLogin {
}

@LoginOut 作用:当校验未通过时(Manger.getInstance().getState() == -1),运行加了@LoginOut注解的函数

//注意此处的包名 后面需要用,若你之间复制到自己项目中,包名改了,注解起不了作用
package com.chj.chjaj.loginmode.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/***
 * 作者:chj233
 * 时间:2023/3/17 23:15
 * 描述:登录退出触发此注解的函数
 */
@Target(ElementType.METHOD)//此注解只能写在函数上
@Retention(RetentionPolicy.RUNTIME)//注解生效 运行时
public @interface LoginOut {
}

@LoginTo 作用:当校验为通过时(Manger.getInstance().getState() == 0),运行加了@LoginTo注解的函数

//注意此处的包名 后面需要用,若你之间复制到自己项目中,包名改了,注解起不了作用
package com.chj.chjaj.loginmode.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/***
 * 作者:chj233
 * 时间:2023/3/17 23:15
 * 描述:需要去登录
 */
@Target(ElementType.METHOD)//此注解只能写在函数上
@Retention(RetentionPolicy.RUNTIME)//注解生效 运行时
public @interface LoginTo {
}

AspectLogin 作用:这个类最为关键,注解是否按照我们编写好的执行,就看这个类

package com.chj.chjaj.loginmode;

import com.chj.chjaj.loginmode.annotation.LoginOut;
import com.chj.chjaj.loginmode.annotation.LoginTo;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/***
 * 作者:chj233
 * 时间:2023/3/17 23:13
 * 描述:注解控制
 */
@Aspect
public class AspectLogin {

    //切点 com.chj.chjaj.loginmode.annotation包下的CheckLogin注解为切点所有函数
    @Pointcut("execution(@com.chj.chjaj.loginmode.annotation.CheckLogin  * *(..))")
    public void loginCheck(){
    }

    //切面 loginCheck() 就是上面切点的函数名称 这个名称可以随便改 但两个地方需要保持一致
    @Around("loginCheck()")
    public void check(final ProceedingJoinPoint point) throws Throwable {
        switch (Manger.getInstance().getState()){
            case 1://已登录验证通过
                point.proceed();//往下执行,若不执行point.proceed(),那么 加了切点注解的函数都不会运行
                break;
            case -1://已退出登录
                invokeAnnotion(point.getThis(), LoginOut.class); //point.getThis() 获取注解所在的类的对象
                break;
            case 0://被强制下线
                invokeAnnotion(point.getThis(), LoginTo.class); //point.getThis() 获取注解所在的类的对象
                break;
            default:
                break;
        }
    }

    //通过对象反射
    public static void invokeAnnotion(Object object, Class annotionClass) {
        Class<?> objectClass = object.getClass(); // 获取class对象
        // 遍历所有函数
        Method[] methods = objectClass.getDeclaredMethods(); // 得到所有的 对象中所有 公开 私有 函数
        for (Method method : methods) {//循环这些函数
            method.setAccessible(true); // 让虚拟机不要去检测 private的函数

            // 判断是否被 annotionClass 注解过的函数
            boolean annotationPresent = method.isAnnotationPresent(annotionClass); //若是被注解的函数
            if (annotationPresent) { //那么之间执行
                // 当前函数 annotionClass 注解过的函数
                try {
                    method.invoke(object);//执行函数
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

MainActivity 作用:就是用来测试的

package com.chj.chjaj;

import android.os.Bundle;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import com.chj.chjaj.loginmode.Manger;
import com.chj.chjaj.loginmode.annotation.CheckLogin;
import com.chj.chjaj.loginmode.annotation.LoginOut;
import com.chj.chjaj.loginmode.annotation.LoginTo;

//简化业务层的代码
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.denglu).setOnClickListener((view) ->{
            Manger.getInstance().setState(1);
            toast("已登录");
        });
        findViewById(R.id.tuichu).setOnClickListener((view) ->{
            Manger.getInstance().setState(-1);
            toast("已退出");
        });
        findViewById(R.id.xiaxian).setOnClickListener((view) ->{
            Manger.getInstance().setState(0);
            toast("已下线");
        });
        findViewById(R.id.fasong).setOnClickListener((view) ->{
            send();
        });
        findViewById(R.id.getinfo).setOnClickListener((view )-> {
            getInfo();
        });
    }

    @CheckLogin//只需要增加@CheckLogin注解 即可去检查当前的登录状态
    protected void send(){
        toast("发送消息");
    }

    @CheckLogin//只需要增加@CheckLogin注解 即可去检查当前的登录状态
    protected void getInfo(){
        toast("获取信息");
    }

    @LoginOut//用户已退出 执行此函数
    public void logout(){
        toast("登录已退出");
    }

    @LoginTo//需要用户登录 执行此函数
    public void loginTo(){
        toast("跳转登录.....");
    }

    protected void toast(String msg){
        if (msg == null) return;
        Toast.makeText(this,msg,Toast.LENGTH_LONG).show();
    }

}
总结一下:

此方案使用Aspect 通过反射的方式来执行注解标记的函数,所以在性能上会略低,所以对性能要求非常高的函数并不太适用。文章来源地址https://www.toymoban.com/news/detail-416888.html

到了这里,关于Android AOP拯救混乱的代码架构的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 架构篇03-为什么要做架构设计?

    谈到架构设计,相信每个技术人员都是耳熟能详,但如果深入探讨一下,“为何要做架构设计?”或者“架构设计目的是什么?”类似的问题,大部分人可能从来没有思考过,或者即使有思考,也没有太明确可信的答案。 关于架构设计的目的,常见的误区有: 因为架构很重

    2024年01月21日
    浏览(61)
  • DDD架构为什么应该首选六边形架构?

    分层架构的一个重要原则是:每层只能与位于其下方的层发生耦合。 分层架构分两种:一种是严格分层架构,规定某层只能与直接位于其下方的层发生耦合;另一种是松散分层架构,允许任意上方层与任意下方层发生耦合。 下图是一个典型的DDD传统分层架构。 以上分层架构

    2024年02月16日
    浏览(51)
  • 现代软件为什么要采用微服架构

    现代软件采用微服务架构是为了解决传统单体架构在开发、部署和维护大型应用时面临的一系列问题。以下是采用微服务架构的主要优势: 1. **模块化和组件化**:微服务通过将应用拆分为一系列小型、松耦合的服务来提高模块化水平。每个服务都是围绕特定的业务功能构建

    2024年04月26日
    浏览(45)
  • 为什么王者荣耀不使用微服务架构?

    来源:zhihu.com/question/359630395/answer/954452799 今天,在知乎上看到这样一个问题:\\\"为什么游戏公司的server不愿意微服务化?\\\" 最近面试了一家游戏公司(满大间的,有上市) 我问他,公司有没有做微服务架构的打算及考量? 他很惊讶的说,我没听说过微服务耶,你可以解释一下

    2024年02月05日
    浏览(43)
  • 云架构的思考1--云计算有什么不同,为什么上云

    云计算这个词提出来已经很久(大概在2006年),在10年前听到这个东西,都说是一个未来趋势。现在这个东西已经不是一个未来趋势,而是已经渗入到方方面面的一个服务,现在一个中小型的创业公司信息化都会优先选择云;任何一个架构师都需要熟悉云上架构的一些内容。

    2024年02月04日
    浏览(53)
  • Doris架构包含哪些技术?为什么进行技术整合?

    Doris主要整合了Google Mesa(数据模型),Apache Impala(MPP Query Engine)和Apache ORCFile (存储格式,编码和压缩)的技术。 为什么要将这三种技术整合? Mesa可以满足我们许多存储需求的需求,但是Mesa本身不提供SQL查询引擎。 Impala是一个非常好的MPP SQL查询引擎,但是缺少完美的分布式存储引

    2024年02月11日
    浏览(78)
  • 【云原生-白皮书】简章1:为什么我们需要云原生架构?

    声明:本文为《阿里云云原生架构核心技术白皮书》的一些读书笔记与感想。 一文大致了解云原生架构模式特点传送门:五分钟了解云原生的架构模式 声明:本文是阅读阿里云云原生架构核心技术白皮书的一些读书笔记与感想。 云原生架构是一种创新的软件开发方法,专为

    2023年04月26日
    浏览(56)
  • Android ViewGroup onDraw为什么没调用

    ViewGroup,它本身并没有任何可画的东西,它是一个透明的控件,因些并不会触发onDraw,但是你现在给LinearLayout设置一个背景色,其实这个背景色不管你设置成什么颜色,系统会认为,这个LinearLayout上面有东西可画了,因此会调用onDraw方法。 android代码一直在优化,我看了几个

    2024年02月16日
    浏览(35)
  • 低代码是什么意思?企业为什么要用低代码平台?

    低代码是什么意思?企业为什么要用低代码平台? 这两个问题似乎困扰了很多人,总有粉丝跟小简抱怨, 一天到晚念叨低代码,倒是来个人解释清楚啊! 来了,这次一文让你全明白。 在此之前,先了解什么是云计算。 “云” :指的就是互联网,因为之前互联网(Internet)

    2024年02月07日
    浏览(54)
  • 【容器架构】你知道有 Docker 为什么还要 K8s 吗?

    👉 博主介绍 : 博主从事应用安全和大数据领域,有8年研发经验,5年面试官经验,Java技术专家,WEB架构师,阿里云专家博主,华为云云享专家,51CTO TOP红人 Java知识图谱点击链接: 体系化学习Java(Java面试专题) 💕💕 感兴趣的同学可以收藏关注下 , 不然下次找不到哟

    2024年02月16日
    浏览(71)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包