aop编程 Android,AOP 面向切面编程在 Android 中的使用

AOP 面向切面编程在 Android 中的使用

GitHub 地址(欢迎下载完整 Demo)

https://github.com/ganchuanpu/AOPDemo

项目需求描述

ab7653affab982b574eb7acc55df2e04.gif

我想类似于这样的个人中心的界面, 大家都不会陌生吧那几个有箭头的地方都是可以点击进行页面跳转的, 但是需要先判断用户是否登录, 如果已经登录, 则正常跳转, 如果没有登录, 则跳转到登录页面先登录, 但凡是有注册, 登录的 APP, 这样的操作, 大家应该都很熟悉吧一般情况下, 我们的逻辑是这样的/**

* 跳转到我的关注页面

*/

publicvoidtoMyAttention(){

// 判断当前用户是否登录

if(LoginHelper.isLogin(this)){

// 如果登录才跳转, 进入我的关注页面

Intentintent=newIntent(this,WaitReceivingActivity.class);

startActivity(intent);

}else{

// 跳转到登录页面, 先登录

Intentintent=newIntent(this,LoginActivity.class);

startActivity(intent);

}

}

重复的体力劳动, 想想都可怕而且类似的还有网络判断, 权限管理, Log 日志的统一管理这样的问题那么, 我们也没有更优雅的方式来解决这一类的问题呢, 答案是有的

先给出我解决了上述问题之后的代码/**

* 跳转到我的关注页面

*/

@CheckLogin

publicvoidtoMyAttention(){

Intentintent=newIntent(this,WaitReceivingActivity.class);

startActivity(intent);

}

AspectJ

AspectJ 实际上是对 AOP 编程思想的一个实践, AOP 虽然是一种思想, 但就好像 OOP 中的 Java 一样, 一些先行者也开发了一套语言来支持 AOP 目前用得比较火的就是 AspectJ 了, 它是一种几乎和 Java 完全一样的语言, 而且完全兼容 Java(AspectJ 应该就是一种扩展 Java, 但它不是像 Groovy 那样的拓展)当然, 除了使用 AspectJ 特殊的语言外, AspectJ 还支持原生的 Java, 只要加上对应的 AspectJ 注解就好所以, 使用 AspectJ 有两种方法:

- 完全使用 AspectJ 的语言这语言一点也不难, 和 Java 几乎一样, 也能在 AspectJ 中调用 Java 的任何类库 AspectJ 只是多了一些关键词罢了

- 或者使用纯 Java 语言开发, 然后使用 AspectJ 注解, 简称 @AspectJ

基础概念

- Aspect 切面: 切面是切入点和通知的集合

PointCut 切入点: 切入点是指那些通过使用一些特定的表达式过滤出来的想要切入 Advice 的连接点

Advice 通知: 通知是向切点中注入的代码实现方法

Joint Point 连接点: 所有的目标方法都是连接点.

Weaving 编织: 主要是在编译期使用 AJC 将切面的代码注入到目标中, 并生成出代码混合过的. class 的过程.

实践步骤

1 在 android studio 中直接配置 AspectJ, 这个配置很重要, 如果失败, 后面就无法成功, 先贴出我的配置, 在 app 的 build.gradle 中做如下配置apply plugin:'com.android.application'

importorg.aspectj.bridge.IMessage

importorg.aspectj.bridge.MessageHandler

importorg.aspectj.tools.ajc.Main

buildscript{

repositories{

mavenCentral()

}

dependencies{

classpath'org.aspectj:aspectjtools:1.8.9'

classpath'org.aspectj:aspectjweaver:1.8.9'

}

}

repositories{

mavenCentral()

}

finaldeflog=project.logger

finaldefvariants=project.android.applicationVariants

variants.all{variant->

if(!variant.buildType.isDebuggable()){

log.debug("Skipping non-debuggable build type'${variant.buildType.name}'.")

return;

}

JavaCompilejavaCompile=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)

MessageHandlerhandler=newMessageHandler(true);

newMain().run(args,handler);

for(IMessagemessage:handler.getMessages(null,true)){

switch(message.getKind()){

caseIMessage.ABORT:

caseIMessage.ERROR:

caseIMessage.FAIL:

log.error message.message,message.thrown

break;

caseIMessage.WARNING:

log.warn message.message,message.thrown

break;

caseIMessage.INFO:

log.info message.message,message.thrown

break;

caseIMessage.DEBUG:

log.debug message.message,message.thrown

break;

}

}

}

}

android{

compileSdkVersion25

buildToolsVersion"25.0.2"

defaultConfig{

applicationId"com.zx.aopdemo"

minSdkVersion17

targetSdkVersion25

versionCode1

versionName"1.0"

testInstrumentationRunner"android.support.test.runner.AndroidJUnitRunner"

}

buildTypes{

release{

minifyEnabledfalse

proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'

}

}

}

dependencies{

compile fileTree(dir:'libs',include:['*.jar'])

androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2',{

excludegroup:'com.android.support',module:'support-annotations'

})

compile'com.android.support:appcompat-v7:25.3.1'

compile'com.android.support.constraint:constraint-layout:1.0.2'

compile'org.aspectj:aspectjrt:1.8.9'

testCompile'junit:junit:4.12'

}

为什么这么配置? 因为 AspectJ 是对 java 的扩展, 而且是完全兼容 java 的但是编译时得用 Aspect 专门的编译器, 这里的配置就是使用 Aspect 的编译器, 单独加入 aspectj 依赖是不行的到这里准备工作已完成, 可以开始看看具体实现了

2 创建切面 AspectJ

用来处理触发切面的回调@Aspect

publicclassCheckLoginAspectJ{

privatestaticfinalStringTAG="CheckLogin";

/**

* 找到处理的切点

* * *(..) 可以处理 CheckLogin 这个类所有的方法

*/

@Pointcut("execution(@com.zx.aopdemo.login.CheckLogin * *(..))")

publicvoidexecutionCheckLogin(){

}

/**

* 处理切面

*

* @param joinPoint

* @return

*/

@Around("executionCheckLogin()")

publicObjectcheckLogin(ProceedingJoinPointjoinPoint)throwsThrowable{

Log.i(TAG,"checkLogin:");

MethodSignaturesignature=(MethodSignature)joinPoint.getSignature();

CheckLogincheckLogin=signature.getMethod().getAnnotation(CheckLogin.class);

if(checkLogin!=null){

Contextcontext=(Context)joinPoint.getThis();

if(MyApplication.isLogin){

Log.i(TAG,"checkLogin: 登录成功");

returnjoinPoint.proceed();

}else{

Log.i(TAG,"checkLogin: 请登录");

Toast.makeText(context,"请登录",Toast.LENGTH_SHORT).show();

returnnull;

}

}

returnjoinPoint.proceed();

}

}

这里要使用 Aspect 的编译器编译必须给类打上标注,@Aspect

还有这里的 Pointcut 注解, 就是切点, 即触发该类的条件里面的字符串如下

ab7653affab982b574eb7acc55df2e04.gif

在 Pointcut 这里, 我使用了 execution, 也就是以方法执行时为切点, 触发 Aspect 类而 execution 里面的字符串是触发条件, 也是具体的切点我来解释一下参数的构成 execution(@com.zx.aopdemo.login.CheckLogin * *(..))这个条件是所有加了 CheckLogin 注解的方法或属性都会是切点, 范围比较广

**: 表示是任意包名

..: 表示任意类型任意多个参数

com.zx.aopdemo.login.CheckLogin 这是我的项目包名下需要指定类的绝对路径再来看看 @Around,Around 是指 JPoint 执行前或执行后被触发, 除了 Around 还有其他几种方式

ab7653affab982b574eb7acc55df2e04.gif

创建完 Aspect 类之后, 还需要一个注解类, 它的作用是: 哪里需要做切点, 那么哪里就用注解标注一下, 这样方便快捷

3 创建注解类packagecom.zx.aopdemo.login;

importjava.lang.annotation.ElementType;

importjava.lang.annotation.Retention;

importjava.lang.annotation.RetentionPolicy;

importjava.lang.annotation.Target;

@Target(ElementType.METHOD)// 可以注解在方法 上

@Retention(RetentionPolicy.RUNTIME)// 运行时 (执行时) 存在

public@interfaceCheckLogin{

}

4Activity 使用登录的注解publicclassLoginActivityextendsAppCompatActivityimplementsView.OnClickListener,RadioGroup.OnCheckedChangeListener{

privateRadioGroupradioGroup;

@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_login);

test();

}

@CheckLogin

publicvoidtest(){

Log.i("tag","判断是否登录");

}

test()方法执行时就是一个切点在执行 test()时, 会回调上面的 CheckLoginAspectJ 类的 executionCheckLogin()方法然后会执行

如下方法/**

* 处理切面

*

* @param joinPoint

* @return

*/

@Around("executionCheckLogin()")

publicObjectcheckLogin(ProceedingJoinPointjoinPoint)throwsThrowable{

Log.i(TAG,"checkLogin:");

MethodSignaturesignature=(MethodSignature)joinPoint.getSignature();

CheckLogincheckLogin=signature.getMethod().getAnnotation(CheckLogin.class);

if(checkLogin!=null){

Contextcontext=(Context)joinPoint.getThis();

if(MyApplication.isLogin){

Log.i(TAG,"checkLogin: 登录成功");

returnjoinPoint.proceed();

}else{

Log.i(TAG,"checkLogin: 请登录");

Toast.makeText(context,"请登录",Toast.LENGTH_SHORT).show();

returnnull;

}

}

returnjoinPoint.proceed();

}

如果使用的是以方法相关为切点, 那么使用 MethodSignature 来接收 joinPoint 的 Signature 如果是属性或其他的, 那么可以使用 Signature 类来接收之后可以使用 Signature 来获取注解类, 那么通过 jointPoint.getThis()获取使用该注解的的上下文对象

来源: https://www.cnblogs.com/ganchuanpu/p/8594877.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值