概念
Java中的编程思想是OOP(Object-Oriented Progreming)面向对象的编程,这样编程会使得问题或功能划分到一个个模块里,每个模块之间通过设计好的接口进行交互,就会使得代码单一职责原则,但是在有些时候,我们有很多不同类型的模块都需要同一个功能,这个时候还用OOP就会很麻烦。所以这时AOP就诞生了,它不仅能在Java中用,还能在我们的Android中应用。
举个栗子:我目前希望知道我们工程中所有Activity的每个方法一被执行,就反映出它的执行效率,如果以OOP的方式编写代码,我们需要在每个Activity中的每个方法前后都调用获取时间的方法,然后计算出执行时间,通过Log日志打印出来,或者可以通过接口、抽象类等等方式来实现,但是这样修改都涉及到修改原工程的代码以及修改的地方比较多,Boss这时候肯定就不会同意了,因为你这修改量这么大,万一出错,我的App不是就不稳定了,当然最后你实在不行而这个功能也必须存在,那你就自己慢慢做吧…………
AOP(Aspect-Oriented Progreming)面向方面的编程,当你需要某些模块都有一个相同功能时,那你就可以使用AOP编程模式
首先——环境搭建
1.插件:插件的方式有很多,我目前看到的有两种
第二种
首先,需要在项目根目录的build.gradle中 dependencies 的增加依赖:
classpath ‘com.jakewharton.hugo:hugo-plugin:1.2.1’
然后再主项目或者库的build.gradle中增加AspectJ的依赖:
compile ‘org.aspectj:aspectjrt:1.8.9’
同时加入AspectJX插件:
apply plugin: ‘com.jakewharton.hugo’
(第一种对于我来说是个传说,第二种用过)但是他们到底有没有什么缺点我也不清楚,只知道第一个的问题在项目当中,无法兼容databinding,目前不确定还存不存在…………
2.Gradle配置:通过在Gradle构建脚本中进行相关配置,这儿具体讲解第二种方式
首先,先安装AspectJ, 这一步我不知道到底要不要安装,因为我先开始自己运行工程没成功,后面安装了还是没成功,最后发现我写的文件有个地方有问题,所以改了就成功了,希望你们操作的时候操作完了能告诉我一声,谢谢,理论上他是jar文件,所以在AndroidStudio中添加依赖就行了,应该可以不需要安装 那我们先去下载,在这儿你可以根据自己的喜好下载对应的版本,建议自己学习用最新的,公司项目用稳定的,具体安装流程
在AndroidStudio中新建项目,配置app中的build.gradle文件,我的配置如下:
apply plugin: 'com.android.application'
import org.aspectj.bridge.IMessage
import org.aspectj.bridge.MessageHandler
import org.aspectj.tools.ajc.Main
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'org.aspectj:aspectjtools:1.8.10'
classpath 'org.aspectj:aspectjweaver:1.8.10'
}
}
repositories {
mavenCentral()
}
final def log = project.logger
final def variants = project.android.applicationVariants
variants.all { variant ->
if (!variant.buildType.isDebuggable()) {
log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")
return;
}
JavaCompile javaCompile = 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)
MessageHandler handler = new MessageHandler(true);
new Main().run(args, handler);
for (IMessage message : handler.getMessages(null, true)) {
switch (message.getKind()) {
case IMessage.ABORT:
case IMessage.ERROR:
case IMessage.FAIL:
log.error message.message, message.thrown
break;
case IMessage.WARNING:
log.warn message.message, message.thrown
break;
case IMessage.INFO:
log.info message.message, message.thrown
break;
case IMessage.DEBUG:
log.debug message.message, message.thrown
break;
}
}
}
}
android {
compileSdkVersion 25
buildToolsVersion "26.0.0"
defaultConfig {
applicationId "com.leezp.xingyun.aspect_demo"
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support.constraint:constraint-layout:1.0.0-beta4'
testCompile 'junit:junit:4.12'
compile 'org.aspectj:aspectjrt:1.8.10'
}
然后——代码实现
在代码中我们需要多建一个类,这个类就是一个监测类,它会在代码编译的时候,将监测类所需要执行的方法植入到被检测的方法中,当执行到被检测的方法,所需执行的方法也跟着一起被执行了
package com.leezp.xingyun.aspect_demo;
import android.app.Activity;
import android.util.Log;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
/**
* Created by xingyun on 2017/8/18.
*
* 监听所有Activity中的**可见**函数并将其时间通过Log日志打出
*
* 下方的@Aspect标示就代表这个类是监测类
*/
@Aspect
public class TraceAspect {
public static final String TAG = "TraceAspect";
//这儿是切入点的字符串,也就是让其监测和(* *..Activity+.*(..))一样的方法
// 这个匹配规则我们这次先不讲,等第二版的时候我们对其讲解
// private static final String POINTCUT_METHOD = "execution(* *..Activity+.*(..))";
//这儿是切入点,我们可以根据切入点的方法名做些小动作
// @Pointcut(POINTCUT_METHOD)
@Pointcut("execution(* *..Activity+.*(..))")
public void methodLogger() {}
// @Before("methodLogger()")
// public void onMethodBefore(JoinPoint joinPoint) {
// //在调用切入点方法之前可以做一些操作,就在这儿做
// //比如获取这个方法的类名或者调用方法前弹个提示信息,修改一些数据等等
// //我这儿就只输出一下方法的类名
// Log.e(TAG, "onMethodBefore: 类名:"+joinPoint.toString());
// //获取方法的类的对象,对其进行操作
// Log.e(TAG, "onMethodBefore: 是否是Activity? "+(joinPoint.getTarget() instanceof Activity));
// //还可以操作更多,自己慢慢探索
// }
@After("methodLogger()")
public void onMethodAfter(JoinPoint joinPoint) {
//在调用切点方法之后可以做些操作,就在这儿做
//比如获取这个方法的类名或者调用方法前弹个提示信息,修改一些数据等等
//我这儿就只输出一下方法的类名
Log.e(TAG, "onMethodBefore: 类名:"+joinPoint.toString());
//获取方法的类的对象,对其进行操作
Log.e(TAG, "onMethodBefore: 是否是Activity? "+(joinPoint.getTarget() instanceof Activity));
//还可以操作更多,自己慢慢探索
}
@Around("methodLogger()")
public Object onMethodAround(ProceedingJoinPoint joinPoint) throws Throwable {
//方法执行时,对其进行操作
//方法执行开始,计时
long startTime = System.nanoTime();
//方法执行
Object proceed = joinPoint.proceed();
//方法执行后,计时
long endTime = System.nanoTime();
//这个Object的proceed对象就是我们监听的方法的返回值
//我们可以通过改变这个值中的一些属性,达到改变这个返回值的作用
Log.e(TAG, "onMethodAround: 方法执行的效率:"+(endTime-startTime));
return proceed;
}
}
在此友谊提醒:after、before、around这三个方法的传入属性一样时,不能同时存在,目前after与around能共存,是因为after的传入属性是JoinPoint ,而around的传入属性是ProceedingJoinPoint ,其实ProceedingJoinPoint是JoinPoint的子类,也就说他们两个方法的属性是可以一样的!
其他代码:
MainActivity.java
public class MainActivity extends AppCompatActivity {
private Button mChange;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mChange = (Button) findViewById(R.id.activity_main_change);
mChange.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setClass(MainActivity.this, TestActivity.class);
startActivity(intent);
}
});
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.leezp.xingyun.aspect_demo.MainActivity">
<TextView
android:id="@+id/activity_main_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="30dp"
android:text="Hello World!"
android:textSize="20sp"
android:textColor="#FF0000"/>
<Button
android:id="@+id/activity_main_change"
android:layout_marginTop="50dp"
android:layout_below="@id/activity_main_info"
android:layout_alignLeft="@id/activity_main_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="切换到第二个页面" />
</RelativeLayout>
TestActivity.java
public class TestActivity extends Activity {
private Button mBack;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
mBack = (Button) findViewById(R.id.activity_test_back);
mBack.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
@Override
protected void onStart() {
super.onStart();
}
@Override
protected void onRestart() {
super.onRestart();
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onPause() {
super.onPause();
}
@Override
protected void onStop() {
super.onStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}
activity_test.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/activity_test_back"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="返回" />
</LinearLayout>
显示结果:
最后还是推荐一些深入学习的文章(也给自己以后公司里遇到这样的问题回来看看):