一种横向业务的解决方案 -- AOP

2023-11-09

AOP(Aspect Oriented Programming)即面向切片编程。所谓面向切片编程,就是可以按照时间,将程序分成无数个时间节点,利用AOP的思想,可以在任何一个时间节点插入其他的代码,来实现自己的业务需求。换句话说,对于那些非用户需求,如:性能监控、日志记录、埋点统计、动态权限、安全控制、异常处理等,可以用AOP完美地实现。
以一个方法的执行为例子:

方法的执行顺序是A->B->C,通过AOP的方式,完全可以在B方法执行的前后插入其他的代码,以解决某种业务需求,例如方法执行时间的检测。
动态代理也是可以说是一种AOP思想的体现,只是它只能在接口方法执行的时候进行一些操作,而AOP可以在任何方法执行前,执行时和执行后做一些操作。

1. AOP涉及到的基本术语

横切关注点

AOP把软件分成两部分:核心关注点和横切关注点。核心关注点一般指的是一些涉及到用户业务的主线业务,而横切关注点就是指非用户业务(横向需求),但又需要穿插到主线业务中执行的代码,如日志记录等。

连接点

在核心关注点的地方可能存在横切关注点的地方,例如方法的入口、点击事件发生的地方等,这些就叫做连接点。

通知

在连接点处所执行的动作,也就是AOP织入的代码,就叫做通知,一般用三种:
before: 在目标方法执行之前的动作。
around: 替换目标代码的动作。
after: 在目标方法执行之后的动作。

切入点

连接点的集合,这些连接点可以确定什么时机会触发一个通知。也就是将代码切入到的目的类,目的方法就叫切入点。
切入点通常使用正则表达式或者通配符语法表示,可以指定执行某个方法,也可以制定执行多个方法,例如指定执行了标记某个注解的所有方法

切面

切入点和通知可以组合成一个切面,简单地说,切面就是切入到指定类指定方法的代码片段。

织入

将通知注入到连接点的过程。

其实开发人员主要关心的就是切入点和切面这两个东西。举个简单的例子,当程序写好了之后,忽然来了一了收集错误日志的功能,也就是需要在所有调用了Log.e(TAG,Msg)的地方将Msg和类信息保存到本地或则服务器。这时候总不可能挨个去改代码吧,利用AOP的思想就可以直接将“收集错误信息的代码段”做成一个切片,插入到Log类的e方法中,就像这样(使用Lancet实现AOP):

 @Proxy("e")
    @TargetClass("android.util.Log")
    public static int rocordLogInfo(String tag, String msg){

        RecordSDK.recordLog(This.get().getClass().getSimpleName(),
                msg);
        return (int) Origin.call();
    }

只需要添加极少的代码就可以解决这个需求了。一个字,爽!

2. AOP代码织入的时机

代码的织入过程一般有三个时机:
编译时织入:例如在Java类文件编译的时候插入完成特定业务需求的代码,需要通过特定的编译器插件来完成。
类加载时织入:通过自定义的类加载器ClassLoader的方式在目标咧的贝加载到虚拟机之前进行类的字节码的增强(插入字节码)。
运行时时织入:切面在运行中的某个时刻插入,例如动态代理。

3. AOP的应用 : 结合注解、APT实现Android动态权限的申请

众所周知,在Android 6.0以后,系统对权限的控制更加严格了,某系涉及到用户隐私的危险权限需要在用户使用的时候动态申请,对用户来说,这是一件好事儿,但是对开发者来说,这却是一个非用户需求(横向业务),在用户需求代码中融入这种横向业务无疑会增加代码的复杂性,所以,使用AOP的思想来实现这个需求对开发者来说是非常合适的,因为这并不会让代码变得复杂。
当然,基于不同的国产ROM,申请权限和对权限的处理可能有许多差异,这里只提供一种解决思路和基于官方处理权限的流程的一个库:PermissionManager,算是对这部分内容学习的一个总结。

3.1 官方的动态权限申请流程

一般会使用到v4包中的三个方法:

ContextCompat.checkSelfPermission()
ActivityCompat.requestPermissions()
ActivityCompat.shouldShowRequestPermissionRationale()

1.检查是否有某个权限:

if (ContextCompat.checkSelfPermission(context, Manifest.permission. SEND_SMS)
        != PackageManager.PERMISSION_GRANTED) {
    // 没有权限,需要申请。
}else{
    // 有权限
}

2.如果没有权限就去申请:

ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.SEND_SMS},10001 );

3.处理权限结果的回调。

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    switch (requestCode) {
        case 10001: {
            if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 用户同意授权
            } else {
                // 用户拒绝授权
            }
            return;
        }
    }
} 

4.如果用户点击了不再询问,shouldShowRequestPermissionRationale()会返回flase。只点击了拒绝授权,这个方法会返回true,所以也可以根据这个方法的返回值弹出一些说明,来提示用户为什么需要这个权限。

3.2 注解 + APT + AOP 优化申请权限的逻辑

1.注解 + APT的作用
根据注解,找到发起权限请求的类,并在编译时生成特定的回调Listener,用于将权限请求的结果回调到Activity或者Fragment中,如:

发起权限请求的是MainActivity

@NeedPermission
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        PermissionManager.requestPermissions(this,new String[]{Manifest.permission.CAMERA,Manifest.permission.WRITE_EXTERNAL_STORAGE});

    }
    @OnGranted
    void granted(){
        Log.d(TAG, "YES!用户同意了授权!");
    }
    @OnDenied
    void denied(){

        Log.d(TAG, "oh NO!用户拒绝了授权!");
    }

    @OnShowRationale
    void showRationale(){
        Log.d(TAG, "用户选择了了不再询问");
    }
}

那么则会在编译时生成如下代码:

public class MainActivity_PermissionListener implements PermissionListener<MainActivity> {
  @Override
  public void onGranted(MainActivity target) {
    target.granted();;
  }

  @Override
  public void onDenied(MainActivity target) {
    target.denied();;
  }

  @Override
  public void onShowRationale(MainActivity target) {
    target.showRationale();;
  }
}

2. AOP的使用时机

使用AOP可以将处理权限结果的相关代码织入onRequestPermissionsResult()方法中,这样就不必再重写这个方法了,所有对权限结果处理的逻辑都在使用注解(@OnGranted,@OnDenied, @OnShowRationale)标记的方法中处理了。

就比如像这样:

public class PremissionResultAspect {
    @TargetClass(value = "android.support.v7.app.AppCompatActivity", scope = Scope.LEAF)
    @Insert(value = "onRequestPermissionsResult", mayCreateSuper = true)
    public void onRequestPermissionsResultAspect(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        Log.d("xsz", "onRequestPermissionsResultAspect -> 权限的数量是 " + permissions.length);
        Origin.callVoid();
        PermissionListener listener = PermissionManager.getPermissionListener(This.get());
        if (grantResults.length > 0 && listener != null) {
            for (int i = 0; i < grantResults.length; i++) {
                if (PermissionHelper.hasGrantedPermission(grantResults[i])) {
                    //已经获得用户权限
                    listener.onGranted(this);
                } else {
                    // 用户拒绝授权
                    if (ActivityCompat.shouldShowRequestPermissionRationale((Activity) This.get(), permissions[i])) {
                        //用户拒绝了授权,但是没有点击不再询问
                        //提示用户为什么APP需要这个权限
                        listener.onDenied(this);
                    } else {
                        //返回false -> 用户点击了不再询问
                        listener.onShowRationale(this);
                    }
                }
            }
        }
    }

}

这就完成了对动态权限的请求的过程的简单封装,其主要目的还是学习这种AOP思想,代码细节以后再来慢慢完善。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

一种横向业务的解决方案 -- AOP 的相关文章

随机推荐

  • 用CSS画一个三角形

    用边框border去画 让左 下透明transparent 效果如下图所示 div width 0px height 0px border left 100px solid transparent border bottom 100px s
  • Android 高级面试题及答案

    一 性能优化 1 如何对 Android 应用进行性能分析 android 性能主要之响应速度 和UI刷新速度 可以参考博客 Android系统性能调优工具介绍 首先从函数的耗时来说 有一个工具TraceView 这是androidsdk自
  • docker--知识点提炼

    1 docker命令 docker服务 info version 容器 ps run exec top stats logs port rm stop start kill inspect cp ctrl p ctrl q 镜像 login
  • Docker容器内Superset汉化

    一 进入容器将config py文件中的BABEL DEFAULT LOCALE en 改为BABEL DEFAULT LOCALE zh 进行简单汉化 Setup default language BABEL DEFAULT LOCALE
  • Ubuntu安装idea

    下载 进入https www jetbrains com idea download section linux 选择Ultimate版本 点击下载 我下载的是这个版本 https download jetbrains com cn ide
  • 通过配置NFS使Ubuntu和海思3559A板子共享目录

    之前在Ubuntu和海思3559A板子之间来回拷贝文件都是用的scp命令 不是很方便 这里通过配置NFS来实现它们之间共享目录 操作步骤如下 1 在Ubuntu上安装NFS 执行以下命令 执行结果如下 sudo apt get instal
  • An attempt was made to call a method that does not existThe attempt was made from following location

    Dubbo和zookeeper整合springboot报错 An attempt was made to call a method that does not exist The attempt was made from the fol
  • 利用XMLHttpRequest同步和异步下载二进制文件的解决方案。

    在XMLHttpRequest2里支持二进制数据的下载了 现分别以同步和异步两种方式分别介绍 异步的方式下载 xmlRequest open GET 0 jpg true xmlRequest responseType blob 这里是关键
  • Android Studio的APP目录下的build.gradle的配置说明

    Build gradle属性说明 声明是Android程序 apply plugin com android application android 程序在编译的时候会检查lint 有任何错误提示都会停止build lintOptions
  • 集合框架知识总汇之(list集合)

    目录 编辑 1 UML 统一建模语 3 List集合 3 1特点 3 2遍历方式 3 3List优化 初始容量10 负载因子1 5 3 4LinkedList 队列 堆栈 3 5如何对Arraylist进行去重处理 面试常问题 1 Coll
  • Django4.0+使用rest_framework_jwt的问题

    问题描述 python版本 3 10 Django版本 4 1 djangorestframework jwt版本 1 11 0 在写jwt认证功能时 发现run的时候会报以下错误 from django utils translation
  • VUE 自身页面跳转自身页面

    先说一下要实现的功能 点击原案件 要回到原案件 但是原案件页面和现在的页面一样 也就是自身跳转自身页面 路由地址不变 使用vue祖传的push 方法来挑转的话 你会发现可以跳转过去 但是页面会刷新 不会触发vue生命周期函数 方法一 thi
  • [转]No response for the toolbars in BEx Analyzer 2004s

    Summary Symptom After installing the frontend either from the CD or through applying the frontend support package or the
  • 2022年蓝桥杯省赛 C/C++ A组B题灭鼠先锋题解

    问题描述 本题为填空题 只需要算出结果后 在代码中使用输出语句将所填结果输出即可 灭鼠先锋是一个老少咸宜的棋盘小游戏 由两人参与 轮流操作 灭鼠先锋的棋盘有各种规格 本题中游戏在两行四列的棋盘上进行 游戏的规则为 两人轮流操作 每次可选择在
  • 《UNIX网络编程》卷一第四章学习笔记

    UNIX网络编程 卷一第四章学习笔记 4 2 socket函数 include
  • 2023华为OD机试真题【计算快递业务主站点/回溯法/深度优先搜索】

    题目描述 快递覆盖的范围有N的站 如果A和B都可以用来中转 我们就称A B站可达 如果A B可达 B C可达 则A C达 我们现在有N个编号 如果s i j 1 表示i j可达 如果s i j 0 表示i j不可达 现用二维数组给定N个站点
  • 使用python爬取微信公众号文章

    一 背景 有时候看到某一个微信公众号中的文章 觉得写的非常不错 有种当时就想把该公众号所有的文章都看完的冲动 但是使用手机看不是特别方便 就想把文章全部下载下来到电脑上面看 二 爬虫实现步骤 使用python爬取微信公众号文章 总共分为如下
  • 图片加载防闪动的CSS方法

    图片闪动 在移动端设置图片布局时 图片使用自适应的方式 其父元素的高度是被图片高度撑开的 在图片加载前 父元素高度为0 加载后 父元素高度为图片高度 这样的过程会造成视觉上的闪烁 影响用户体验 因此 在用图片撑开父元素高度之前 就需要给父元
  • 安装sql server时提示缺少.NET 3.5 sp1

    这几天遇到了一个问题 在安装sql server的时候总是提示我没有安装 NET framework 3 5 sp1 但是我电脑上已经安装了它 多次尝试之后我百思不得其解 今天终于解决了 我的系统是win8升级上来的win10 在升级的时候
  • 一种横向业务的解决方案 -- AOP

    AOP Aspect Oriented Programming 即面向切片编程 所谓面向切片编程 就是可以按照时间 将程序分成无数个时间节点 利用AOP的思想 可以在任何一个时间节点插入其他的代码 来实现自己的业务需求 换句话说 对于那些非