真流弊、实战最真实的Android的切面编程 AOP

概念


Java中的编程思想是OOP(Object-Oriented Progreming)面向对象的编程,这样编程会使得问题或功能划分到一个个模块里,每个模块之间通过设计好的接口进行交互,就会使得代码单一职责原则,但是在有些时候,我们有很多不同类型的模块都需要同一个功能,这个时候还用OOP就会很麻烦。所以这时AOP就诞生了,它不仅能在Java中用,还能在我们的Android中应用。

举个栗子:我目前希望知道我们工程中所有Activity的每个方法一被执行,就反映出它的执行效率,如果以OOP的方式编写代码,我们需要在每个Activity中的每个方法前后都调用获取时间的方法,然后计算出执行时间,通过Log日志打印出来,或者可以通过接口、抽象类等等方式来实现,但是这样修改都涉及到修改原工程的代码以及修改的地方比较多,Boss这时候肯定就不会同意了,因为你这修改量这么大,万一出错,我的App不是就不稳定了,当然最后你实在不行而这个功能也必须存在,那你就自己慢慢做吧…………

AOP(Aspect-Oriented Progreming)面向方面的编程,当你需要某些模块都有一个相同功能时,那你就可以使用AOP编程模式

首先——环境搭建

1.插件:插件的方式有很多,我目前看到的有两种

  • 第一种
    gradle_plugin_android_aspectjx

  • 第二种
    首先,需要在项目根目录的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>

显示结果:

最后还是推荐一些深入学习的文章(也给自己以后公司里遇到这样的问题回来看看):

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值