赞
踩
什么是面向切面编程?首先我们来了解下两个概念:
OOP(面向对象编程):
针对业务处理过程的实体及其属性和行为进行抽象封装
,以获得更加清晰高效的逻辑单元划分。
AOP(面向切面编程):
则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。
由此可知,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
这里,我们就使用AspectJ
进行切面编程,其相对应的github地址:hujiang.aspectjx
配置步骤如下:
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.10'
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
进行了切面编程,那咱一定很好奇它是如何做到切面处理的呢?我们使用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
方法中,由此实现了切面编程!
如果以上文章对您有一点点帮助,希望您不要吝啬的点个赞加个关注,您每一次小小的举动都是我坚持写作的不懈动力!ღ( ´・ᴗ・` )
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。