IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 某种使用Kotlin的Android运行时AOP -> 正文阅读

[移动开发]某种使用Kotlin的Android运行时AOP

KAop

??没太用过AOP框架,了解了一些在Java界比较有名的AOP框架比如AspectJ,发现大部分都是在编译时处理源代码实现代码的织入。很好奇能不能在运行时实现AOP操作,找了一下确实是有。比如适用于安卓的epic等。
??自己也想整一个类似的,实现用注解标注一个函数就可以Hook这个函数简易运行时框架。所以整了这么一个感觉不太实用的玩具KAop(好歹有那么点用?)。使用Kotlin编写,主要面向Android。改改纯Java/Kotlin程序也可以用~(具体用法随更新会有所改动)
下文:基于KAop使用注解封装XXPermissions的安卓权限请求框架

原理

??用一个接口包装真正的方法的逻辑,再通过创建匿名类获取当前执行的方法(enclosingMethod)并代理执行前面包装的逻辑。在中间通过自定义的切面控制方法该如何调用。缺点是对每一个使用KAop的方法创建两个类,牺牲空间和性能换取AOP。

编写AOP切面

1.继承Aspect,如TimeCostAspect,实现了对方法执行时间的计算。

class TimeCostAspect : Aspect() {
    private var time: Long = 0
    override fun before(point: AbsJoinPoint) {
        time = System.currentTimeMillis()
    }

    override fun after(point: AbsJoinPoint) {
        Log.d("TimeCost", (System.currentTimeMillis() - time).toString() + "ms")
    }
}

2.对于Kotlin调用: 定义一个注解,使用@AspectAnnotation注解该注解,并且指定plugin使用TimeCostAspect
其中order代表切面的执行顺序,越小越先执行。order为一个约定的名称,可以不定义该参数。默认为Int.MAX_VALUE
对于Java调用: 必须要标记注解@Retention(RetentionPolicy.RUNTIME),kotlin默认是RUNTIME。

@AspectAnnotation(plugin = TimeCostAspect::class)
annotation class TimeCost(val order: Int = 0)

使用AOP切面

@NeedToken是定义的某个切面,除了标记注解,在Kotlin只需要额外添加两行代码就可以实现AOP。对在Kotlin中普通函数以及object内的函数,Java的普通方法以及静态方法都可以实现Hook
Kotlin
在需要切面的类初始化,在需要切面的函数标记定义的注解,函数用pointcut{...}包裹一层,其中pointcut是初始化返回的对象,这里override了他的invoke操作。

class KtClassCase {
    private val pointcut = KAop(this)

    @NeedToken
    fun test():String =  pointcut{
        return@pointcut "操作成功"
    }

}

object KtObjectCase {

    private val pointcut = KAop(this)

    @NeedToken
    fun testStatic():String =  pointcut{
        return@pointcut "操作成功"
    }
    
    @NeedToken
    @JvmStatic
    fun testJvmStatic():String = pointcut{
        return@pointcut "操作成功"
    }
}

Java
在Java中使用略显繁琐,这里只是做了个对Java的兼容,建议使用Kotlin。
注意,创建MethodGetter对象时后面的{}
正确:new MethodGetter<…>(…, …){}.proxy();
错误:new MethodGetter<…>(…, …).proxy();

public class JavaCase {
    private final Pointcut pointcut = KAop.inject(this);

    @NeedToken
    public String test(){
        return new MethodGetter<String>(pointcut, ()-> "操作成功"){}.proxy();
    }

    private static final Pointcut pointcutStatic = KAop.inject(JavaCase.class);

    @NeedToken
    public static String testStatic(){
        return new MethodGetter<String>(pointcutStatic, ()-> "操作成功"){}.proxy();
    }
}

Demo

这里模拟了一个需要登录的场景。
切面实现:

public class AuthAspect extends Aspect {
    //模拟token信息
    public static String token = null;
    @Override
    public void before(@NonNull AbsJoinPoint point) {
        Log.d("AuthAspect", "AuthAspect before");
    }

    @Override
    public Object around(@NonNull AbsJoinPoint point) {
        if(token == null){
            Log.d("AuthAspect", "用户权限不足!!");
            return "权限不足";
        } else {
            if("admin".equals(token)){
                Log.d("AuthAspect", "管理员权限");
                return super.around(point) + "@管理员";
            } else {
                Log.d("AuthAspect", "普通权限");
                return super.around(point) + "@用户";
            }
        }
    }

    @Override
    public void after(@NonNull AbsJoinPoint point) {
        Log.d("AuthAspect", "AuthAspect after");
    }
}

点击事件,ktClassCase在上面提到过

val ktClassCase = KtClassCase()
findViewById<Button>(R.id.action).setOnClickListener {
	val result = ktClassCase.test()
	Toast.makeText(this, result, Toast.LENGTH_SHORT).show()

Demo

总结

初步可以满足个人的一些日常乐子开发。

更新

  • 2022/12/19
  1. 简化Java的调用方式
    @NeedToken
    public String test(){
    	return new MethodGetter<String>(pointcut, ()-> "操作成功"){}.proxy();
    }
    
    @NeedToken
    public String test(){
    	return new MethodGetter<String>(){
    		@Override
    		public String proxy() {
    			return pointcut.pointcut(this, ()-> "操作成功");
    		}
    	}.proxy();
    }
    
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-12-25 11:21:31  更:2022-12-25 11:21:40 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 23:29:28-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码