前言
什么是面向切面编程?首先我们来了解下两个概念:
OOP(面向对象编程):
针对业务处理过程的实体及其属性和行为进行抽象封装
,以获得更加清晰高效的逻辑单元划分。
AOP(面向切面编程):
则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。
由此可知,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
配置AspectJ以及报错处理
这里,我们就使用AspectJ
进行切面编程,其相对应的github地址:hujiang.aspectjx
配置步骤如下:
- 根目录build.gradle中添加
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.10'
- 对应app模块build.gradle 添加:
apply plugin: 'android-aspectjx'
implementation 'org.aspectj:aspectjrt:1.8.9'
当然如果按照以上配置就可以编译通过,那要先祝贺你了,这里我记录下我配置后编译报错的一些问题:
> Failed to apply plugin 'android-aspectjx'.
> Build was configured to prefer settings repositories over project repositories but repository 'MavenLocal' was added by plugin 'android-aspectjx'
解决方案:
替换setting.gradle文件dependencyResolutionManagement
配置中 repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
为repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
Cause: zip file is empty
解决方案:
对应app模块添加配置
aspectjx{
exclude 'org.jetbrains.kotlin', "module-info", 'versions.9'
}
经过上述的配置,我们就可以开始使用AspectJ
进行切面编程了!!!
一个小栗子
这里,我们举个简单又实际开发中会碰到的案例:
比如在开发中,碰到很多地方需要先判断当前是否有网络,如果没有网络就弹Toast,有网络的时候才进行对应界面跳转!
那怎么去处理这类问题呢?我们常用的做法是一个个地方去进行判断是否有网络,这样如果需求进行变动,就会很麻烦,同时这种做法也不太优雅。
下面我们就使用AspectJ
来解决这个问题;
- 首先定义
CheckNet
注解
package com.crystal.essayjoker.aop
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FUNCTION)
annotation class CheckNet()
- 对
CheckNet
注解进行界面处理
package com.crystal.essayjoker.aop
import android.app.Activity
import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkInfo
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.fragment.app.Fragment
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Pointcut
@Aspect
class CheckNetAspect {
/**
* 找到要处理的切点
* * *(..)表示可以处理所有的方法 比如有参无参的
*/
@Pointcut("execution(@com.crystal.essayjoker.aop.CheckNet * *(..))")
fun checkNetBehavior() {
}
/**
* 处理切面
*/
@Around("checkNetBehavior()")
fun checkNetRound(joinPoint: ProceedingJoinPoint): Any? {
Log.e("CheckNetAspect", "checkNetRound")
val target = joinPoint.`this` //获取当前切点所在的类
val context = getContext(target)
if (context != null) {
if (!isNetworkAvailable(context)) {
Toast.makeText(context, "请检查网络是否正常", Toast.LENGTH_SHORT).show()
return null
}
}
return joinPoint.proceed()
}
private fun getContext(target: Any): Context? {
return when (target) {
is Activity -> {
target
}
is Fragment -> {
target.context
}
is View -> {
target.context
}
else -> null
}
}
/**
* 判断网络是否可用
*/
private fun isNetworkAvailable(context: Context): Boolean {
// 获取手机所有连接管理对象(包括对wi-fi,net等连接的管理)
val connectivityManager =
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
// 获取NetworkInfo对象
val networkInfo = connectivityManager.allNetworkInfo
if (networkInfo.isNotEmpty()) {
for (i in networkInfo.indices) {
// 判断当前网络状态是否为连接状态
if (networkInfo[i].state == NetworkInfo.State.CONNECTED) {
return true
}
}
}
return false
}
}
- 测试验证
class CheckNetActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_check_net)
findViewById<Button>(R.id.btn).setOnClickListener {
onCheckNet()
}
}
@CheckNet
fun onCheckNet() {
Toast.makeText(this@CheckNetActivity, "跳转到其他界面", Toast.LENGTH_SHORT).show()
}
}
测试结果:
点击按钮时,会去检查网络,没有会提示请检查网络是否正常
,有网则会提示跳转到其他界面
!
AspectJ实现原理
上面我们简单实用AspectJ
进行了切面编程,那咱一定很好奇它是如何做到切面处理的呢?我们使用jadx
反编译生成的APK,找到CheckNetActivity中的onCheckNet方法
:
private static final /* synthetic */ void onCheckNet_aroundBody0(CheckNetActivity ajc$this, JoinPoint joinPoint) {
Toast.makeText(ajc$this, "跳转到其他界面", 0).show();
}
private static final /* synthetic */ Object onCheckNet_aroundBody1$advice(CheckNetActivity ajc$this, JoinPoint thisJoinPoint, CheckNetAspect ajc$aspectInstance, ProceedingJoinPoint joinPoint) {
Intrinsics.checkNotNullParameter(joinPoint, "joinPoint");
Log.e("CheckNetAspect", "checkNetRound");
Object target = joinPoint.getThis();
Intrinsics.checkNotNullExpressionValue(target, TypedValues.AttributesType.S_TARGET);
Context context = ajc$aspectInstance.getContext(target);
if (context == null || ajc$aspectInstance.isNetworkAvailable(context)) {
onCheckNet_aroundBody0(ajc$this, joinPoint);
return null;
}
Toast.makeText(context, "请检查网络是否正常", 0).show();
return null;
}
可以看出在编译时AspectJ
将代码插入到onCheckNet
方法中,由此实现了切面编程!
小结
如果以上文章对您有一点点帮助,希望您不要吝啬的点个赞加个关注,您每一次小小的举动都是我坚持写作的不懈动力!ღ( ´・ᴗ・` )