<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/aspect/EnableSystraceAspect.java<|end_filename|>
package cn.com.superLei.aoparms.aspect;

import android.util.Log;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

import cn.com.superLei.aoparms.annotation.EnableSystrace;

/**
 * description $desc$
 * created by jerry on 2019/5/30.
 */
@Aspect
public class EnableSystraceAspect {
//    private static final String POINTCUT_METHOD = "execution(@cn.com.superLei.aoparms.annotation.EnableSystrace * *(..))";
    private static final String POINTCUT_METHOD = "execution(* android.app.Application.onCreate(..))";

    @Pointcut(POINTCUT_METHOD)
    public void onSystraceMethod() {

    }

    @Around("onSystraceMethod() && @annotation(enableSystrace)")
    public Object doSystraceMethod(ProceedingJoinPoint joinPoint, EnableSystrace enableSystrace) throws Throwable {
        long filter = enableSystrace.filter();
        boolean containNative = enableSystrace.containNative();
        Class clazz = joinPoint.getClass();
        if (clazz.isAnnotationPresent(EnableSystrace.class)){

        }
        return joinPoint.proceed();
    }
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/annotation/Async.java<|end_filename|>
package cn.com.superLei.aoparms.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface Async {
//    int priority() default 5;//一旦改变优先级,则会重新new Thread
}


<|start_filename|>app/src/main/java/com/heaton/baselibsample/TestComponent.java<|end_filename|>
package com.heaton.baselibsample;

import android.util.Log;

import cn.com.superLei.aoparms.annotation.Component;

/**
 * description $desc$
 * created by jerry on 2019/6/4.
 */
@Component
public class TestComponent {


    private static final String TAG = "TestComponent";

    public static void main(String[] args) {
        byte[] data = new byte[4];
        data[0] = (byte) 255;
        data[1] = (byte) 0xff;

        Log.e(TAG, "main: ");
    }
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/annotation/PermissionDenied.java<|end_filename|>
package cn.com.superLei.aoparms.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * description $desc$
 * created by jerry on 2019/8/5.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PermissionDenied {

//    int requestCode() default 0;
}

<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/aspect/DelayAwayAspect.java<|end_filename|>
package cn.com.superLei.aoparms.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

import cn.com.superLei.aoparms.annotation.DelayAway;
import cn.com.superLei.aoparms.common.collection.NoEmptyHashMap;
import io.reactivex.disposables.Disposable;

/**
 * description $desc$
 * created by jerry on 2019/5/30.
 */
@Aspect
public class DelayAwayAspect {
    private static final String TAG = "DelayAwayAspect";
    private static final String POINTCUT_METHOD = "execution(@cn.com.superLei.aoparms.annotation.DelayAway * *(..))";

    @Pointcut(POINTCUT_METHOD)
    public void onDelayAwayMethod() {
    }

    @Around("onDelayAwayMethod() && @annotation(delayAway)")
    public Object doDelayAwayMethod(final ProceedingJoinPoint joinPoint, DelayAway delayAway) throws Throwable {
        String key = delayAway.key();
        Disposable subscribe = (Disposable) NoEmptyHashMap.getInstance().get(key);
        if (subscribe != null){
            subscribe.dispose();
            NoEmptyHashMap.getInstance().remove(key);
        }
        return joinPoint.proceed();
    }
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/common/PkgScanner.java<|end_filename|>
package cn.com.superLei.aoparms.common;

import android.annotation.TargetApi;
import android.os.Build;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * description $desc$
 * created by jerry on 2019/6/4.
 */
public class PkgScanner {

    /**
     * 包名
     */
    private String pkgName;

    /**
     * 包对应的路径名
     */
    private String pkgPath;

    /**
     * 注解的class对象
     */
    private Class anClazz;

    private ClassLoader cl;


    public PkgScanner(String pkgName) {
        this.pkgName = pkgName;
        this.pkgPath = PathUtils.packageToPath(pkgName);

        cl = Thread.currentThread().getContextClassLoader();
    }

    public PkgScanner(String pkgName, Class anClazz) {
        this(pkgName);

        this.anClazz = anClazz;
    }

    /**
     * 执行扫描操作.
     *
     * @return
     * @throws IOException
     */
    public List<String> scan() throws IOException {
        List<String> list = loadResource();
        if (null != this.anClazz) {
            list = filterComponents(list);
        }

        return list;
    }

    public void setPkgName(String pkgName) {
        this.pkgName = pkgName;
        this.pkgPath = PathUtils.packageToPath(pkgName);
    }

    public void setAnnocation(Class an) {
        this.anClazz = an;
    }

    private List<String> loadResource() throws IOException {
        List<String> list = null;

        Enumeration<URL> urls = cl.getResources(pkgPath);
        while (urls.hasMoreElements()) {
            URL u = urls.nextElement();

            ResourceType type = determineType(u);

            switch (type) {
                case JAR:
                    String path = PathUtils.distillPathFromJarURL(u.getPath());
                    list = scanJar(path);
                    break;

                case FILE:
                    list = scanFile(u.getPath(), pkgName);
                    break;
            }
        }

        return list;
    }

    /**
     * 根据URL判断是JAR包还是文件目录
     * @param url
     * @return
     */
    private ResourceType determineType(URL url) {
        if (url.getProtocol().equals(ResourceType.FILE.getTypeString())) {
            return ResourceType.FILE;
        }

        if (url.getProtocol().equals(ResourceType.JAR.getTypeString())) {
            return ResourceType.JAR;
        }

        throw new IllegalArgumentException("不支持该类型:" + url.getProtocol());
    }

    /**
     * 扫描JAR文件
     * @param path
     * @return
     * @throws IOException
     */
    private List<String> scanJar(String path) throws IOException {
        JarFile jar = new JarFile(path);

        List<String> classNameList = new ArrayList<>(20);

        Enumeration<JarEntry> entries = jar.entries();
        while (entries.hasMoreElements()) {
            JarEntry entry = entries.nextElement();
            String name = entry.getName();

            if( (name.startsWith(pkgPath)) && (name.endsWith(ResourceType.CLASS_FILE.getTypeString())) ) {
                name = PathUtils.trimSuffix(name);
                name = PathUtils.pathToPackage(name);

                classNameList.add(name);
            }
        }

        return classNameList;
    }

    /**
     * 扫描文件目录下的类
     * @param path
     * @return
     */
    private List<String> scanFile(String path, String basePkg) {
        File f = new File(path);

        List<String> classNameList = new ArrayList<>(10);

        // 得到目录下所有文件(目录)
        File[] files = f.listFiles();
        if (null != files) {
            int LEN = files.length;

            for (int ix = 0 ; ix < LEN ; ++ix) {
                File file = files[ix];

                // 判断是否还是一个目录
                if (file.isDirectory()) {
                    // 递归遍历目录
                    List<String> list = scanFile(file.getAbsolutePath(), PathUtils.concat(basePkg, ".", file.getName()));
                    classNameList.addAll(list);

                } else if (file.getName().endsWith(ResourceType.CLASS_FILE.getTypeString())) {
                    // 如果是以.class结尾
                    String className = PathUtils.trimSuffix(file.getName());
                    // 如果类名中有"$"不计算在内
                    if (-1 != className.lastIndexOf("$")) {
                        continue;
                    }

                    // 命中
                    String result = PathUtils.concat(basePkg, ".", className);
                    classNameList.add(result);
                }
            }
        }

        return classNameList;
    }

    /**
     * 过虑掉没有指定注解的类
     * @param classList
     * @return
     */
    @TargetApi(Build.VERSION_CODES.N)
    private List<String> filterComponents(List<String> classList) {
        List<String> newList = new ArrayList<>(20);

        classList.forEach(name -> {
            try {
                Class clazz = Class.forName(name);
                Annotation an = clazz.getAnnotation(this.anClazz);
                if (null != an) {
                    newList.add(name);
                }

            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        });

        return newList;
    }
}


<|start_filename|>app/src/main/java/com/heaton/baselibsample/Constant.java<|end_filename|>
package com.heaton.baselibsample;

public class Constant {
    public static final int MAIN_ACTIVITY_STATISTIC_KEY = 1;
    public static final int TEST_CLICK_STATISTIC_KEY = 2;
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/common/statistic/StatisticInfo.java<|end_filename|>
package cn.com.superLei.aoparms.common.statistic;

public class StatisticInfo {
    private int key;
    private long startTime;
    private String statisticName;
    private boolean isActivity;
    private boolean isMethod;
    private Enum<ActivityLife> activityLife;

    public StatisticInfo(int key, long startTime, String statisticName, boolean isMethod) {
        this.key = key;
        this.startTime = startTime;
        this.statisticName = statisticName;
        this.isMethod = isMethod;
    }

    public StatisticInfo(int key, long startTime, String statisticName, boolean isActivity, Enum<ActivityLife> activityLife) {
        this.key = key;
        this.startTime = startTime;
        this.statisticName = statisticName;
        this.isActivity = isActivity;
        this.activityLife = activityLife;
    }

    public Enum<ActivityLife> getActivityLife() {
        return activityLife;
    }

    public void setActivityLife(Enum<ActivityLife> activityLife) {
        this.activityLife = activityLife;
    }

    public int getKey() {
        return key;
    }

    public void setKey(int key) {
        this.key = key;
    }

    public long getStartTime() {
        return startTime;
    }

    public void setStartTime(long startTime) {
        this.startTime = startTime;
    }

    public String getStatisticName() {
        return statisticName;
    }

    public void setStatisticName(String statisticName) {
        this.statisticName = statisticName;
    }

    public boolean isActivity() {
        return isActivity;
    }

    public void setActivity(boolean activity) {
        isActivity = activity;
    }

    public boolean isMethod() {
        return isMethod;
    }

    public void setMethod(boolean method) {
        isMethod = method;
    }

    @Override
    public String toString() {
        return "StatisticInfo{" +
                "key=" + key +
                ", startTime=" + startTime +
                ", statisticName='" + statisticName + '\'' +
                ", isActivity=" + isActivity +
                ", isMethod=" + isMethod +
                ", activityLife=" + activityLife +
                '}';
    }

    public enum ActivityLife {
        CREATE,
        RESTART,
        PAUSE,
        DESTROY
    }
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/common/permission/IPermission.java<|end_filename|>
package cn.com.superLei.aoparms.common.permission;

import java.util.List;

/**
 * description $desc$
 * created by jerry on 2019/8/5.
 */
public interface IPermission {
    //同意权限
    void permissionGranted();

    //拒绝权限并且选中不再提示
    void permissionNoAskDenied(int requestCode, List<String> denyNoAskList);

    //取消权限
    void permissionDenied(int requestCode, List<String> denyList);
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/callback/Interceptor.java<|end_filename|>
package cn.com.superLei.aoparms.callback;

/**
 * description $desc$
 * created by jerry on 2019/6/3.
 */
public interface Interceptor {
    boolean intercept(String key, String methodName) throws Throwable;
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/annotation/Retry.java<|end_filename|>
package cn.com.superLei.aoparms.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * description $desc$
 * created by jerry on 2019/5/30.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Retry {
    /**
     * 重试次数
     * @return
     */
    int count() default 0;


    /**
     * 重试的间隔时间
     * @return
     */
    long delay() default 0L;


    /**
     * 是否支持异步重试方式
     * @return
     */
    boolean asyn() default false;

    /**
     * 重试n次后，结果的回调
     * @return
     */
    String retryCallback() default "";
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/annotation/SingleClick.java<|end_filename|>
package cn.com.superLei.aoparms.annotation;

import android.view.View;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 防止重复点击
 * Created by jerry on 2018/6/13.
 */

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SingleClick {
    long value() default  500L;

    int[] ids() default {View.NO_ID};
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/common/assist/TestAssist.java<|end_filename|>
package cn.com.superLei.aoparms.common.assist;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;

public class TestAssist {

    public static ClassPool pool = ClassPool.getDefault();
    public static CtClass clazz = pool.makeClass("Employ");

    public static void main(String[] args) throws Exception {

        //创建属性方法一
        CtField ctFieldOne = CtField.make("private Integer empId;", clazz);
        clazz.addField(ctFieldOne);
        CtField ctFieldTwo = CtField.make("private Integer empAge;", clazz);
        clazz.addField(ctFieldTwo);

        //创建属性方法二
        CtField ctFieldThree = new CtField(pool.get("java.lang.String"),"empName", clazz);
        ctFieldThree.setModifiers(Modifier.PRIVATE);
        clazz.addField(ctFieldThree);

        //创建含参构造器
        CtConstructor constructor = new CtConstructor(new CtClass[]{pool.get("java.lang.Integer"), pool.get("java.lang.String"),pool.get("java.lang.Integer")}, clazz);
        constructor.setBody("{this.empId = $1; this.empName = $2; this.empAge = $3;}");
        clazz.addConstructor(constructor);

        //创建方法方法一
        CtMethod  ctMethodOne = CtMethod.make("public Integer getEmpId() {return empId;}", clazz);
        clazz.addMethod(ctMethodOne);
        CtMethod ctMethodTwo = CtMethod.make("public void setEmpId(Integer empId) {this.empId = empId;}", clazz);
        clazz.addMethod(ctMethodTwo);

        //创建方法方法二
        clazz.addMethod(CtNewMethod.getter("getEmpAge", ctFieldTwo));
        clazz.addMethod(CtNewMethod.setter("setEmpAge", ctFieldTwo));

        clazz.writeFile("/home/liulei/AndroidStudioProjects/Github/AopArms/aop-arms/src/main/java/cn/com/superLei/aoparms/common/assist");


        Class clazzEmploy = clazz.toClass();

        //调用含参构造器
        Object obj = clazzEmploy.getConstructor(Integer.class,String.class,Integer.class).newInstance(1,"Sandy",8);

        //调用方法
        Method method =clazzEmploy.getDeclaredMethod("getEmpAge");
        System.out.println("method.invoke(obj): " + method.invoke(obj));
    }

}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/Options.java<|end_filename|>
package cn.com.superLei.aoparms;

//配置类
public class Options {
    private boolean isLoggable = false;
    private String logTag = "AopArms";

    public Options() {
    }

    public boolean isLoggable() {
        return isLoggable;
    }

    public Options setLoggable(boolean loggable) {
        isLoggable = loggable;
        return this;
    }

    public String getLogTag() {
        return logTag;
    }

    public Options setLogTag(String logTag) {
        this.logTag = logTag;
        return this;
    }

}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/common/ResourceType.java<|end_filename|>
package cn.com.superLei.aoparms.common;

/**
 * description $desc$
 * created by jerry on 2019/6/4.
 */
public enum  ResourceType {
    JAR("jar"),
    FILE("file"),

    CLASS_FILE(".class");

    private String typeString;

    private ResourceType(String type) {
        this.typeString = type;
    }

    public String getTypeString() {
        return this.typeString;
    }
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/aspect/InterceptAspect.java<|end_filename|>
package cn.com.superLei.aoparms.aspect;

import android.text.TextUtils;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

import cn.com.superLei.aoparms.AopArms;
import cn.com.superLei.aoparms.annotation.Intercept;

/**
 * description $desc$
 * created by jerry on 2019/5/30.
 */
@Aspect
public class InterceptAspect {
    private static final String POINTCUT_METHOD = "execution(@cn.com.superLei.aoparms.annotation.Intercept * *(..))";

    @Pointcut(POINTCUT_METHOD)
    public void onInterceptMethod() {
    }

    @Around("onInterceptMethod() && @annotation(intercept)")
    public Object doInterceptMethod(ProceedingJoinPoint joinPoint, Intercept intercept) throws Throwable {
        if (AopArms.getInterceptor() == null)return joinPoint.proceed();
        String value = intercept.value();
        if (!TextUtils.isEmpty(value)){
            //拦截
            boolean result = proceedIntercept(intercept.value(), joinPoint);
            return result ? null : joinPoint.proceed();
        }
        return joinPoint.proceed();
    }

    private boolean proceedIntercept(String value, ProceedingJoinPoint joinPoint) throws Throwable {
        boolean intercept = AopArms.getInterceptor().intercept(value, joinPoint.getSignature().getName());
        if (intercept){
            return true;
        }
        return false;
    }
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/annotation/Delay.java<|end_filename|>
package cn.com.superLei.aoparms.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Delay {

    String key() default "";

    /**
     * 回调是否在异步线程
     * @return
     */
    boolean asyn() default false;

    int priority() default 5;//一旦改变优先级,则会重新new Thread

    long delay() default 0L; //延迟时间 单位是毫秒

    /**
     * 时间单位
     * @return
     */
    TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/aspect/MainThreadAspect.java<|end_filename|>
package cn.com.superLei.aoparms.aspect;

import android.os.Looper;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.FlowableEmitter;
import io.reactivex.FlowableOnSubscribe;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;

/**
 * description $desc$
 * created by jerry on 2019/5/30.
 */
@Aspect
public class MainThreadAspect {

    private static final String POINTCUT_METHOD = "execution(@cn.com.superLei.aoparms.annotation.MainThread * *(..))";

    @Around("onMainThreadMethod()")
    public void doMainThreadMethod(final ProceedingJoinPoint joinPoint) throws Throwable {
        mainThreadMethod(joinPoint);
    }

    @Pointcut(POINTCUT_METHOD)
    public void onMainThreadMethod() {
    }

    private void mainThreadMethod(final ProceedingJoinPoint joinPoint) throws Throwable {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            joinPoint.proceed();
        } else {
            Flowable.create(new FlowableOnSubscribe<Object>() {
                                @Override
                                public void subscribe(FlowableEmitter<Object> e) throws Exception {
                                    try {
                                        joinPoint.proceed();
                                    } catch (Throwable throwable) {
                                        throwable.printStackTrace();
                                    }
                                }
                            }
                    , BackpressureStrategy.BUFFER)
                    .subscribeOn(AndroidSchedulers.mainThread())
                    .subscribe();
        }
    }
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/common/systrace/SystraceMonitor.java<|end_filename|>
package cn.com.superLei.aoparms.common.systrace;

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.util.Printer;

import cn.com.superLei.aoparms.AopArms;
import cn.com.superLei.aoparms.AopLog;

public class SystraceMonitor {
    private static final String TAG = "SystraceMonitor";
    private static SystraceMonitor sInstance = new SystraceMonitor();
    private Handler mIoHandler;
    //方法耗时的卡口,60毫秒
    private static final long TIME_BLOCK = 60L;
    private long filterTime = TIME_BLOCK;
    private long startMonitorTime = 0L;
    private boolean isBlock = false;
    private boolean isContainNative = false;

    private SystraceMonitor() {
        HandlerThread logThread = new HandlerThread("log");
        logThread.start();
        mIoHandler = new Handler(logThread.getLooper());
    }

    public void start() {
        Looper.getMainLooper().setMessageLogging(new Printer() {
            //分发和处理消息开始前的log
            private static final String START = ">>>>> Dispatching";
            //分发和处理消息结束后的log
            private static final String END = "<<<<< Finished";

            @Override
            public void println(String x) {
                if (x.startsWith(START)) {
                    //开始计时
                    startMonitor();
                }
                if (x.startsWith(END)) {
                    //结束计时，并计算出方法执行时间
                    removeMonitor();
                }
            }
        });

    }

    public boolean isContainNative() {
        return isContainNative;
    }

    public void setContainNative(boolean containNative) {
        isContainNative = containNative;
    }

    public long getFilterTime() {
        return filterTime;
    }

    public void setFilterTime(long filterTime) {
        this.filterTime = filterTime;
    }

    private Runnable mLogRunnable = new Runnable() {
        @Override
        public void run() {
            isBlock = true;
            //打印出执行的耗时方法的栈消息
            StringBuilder sb = new StringBuilder();
            StackTraceElement[] stackTrace = Looper.getMainLooper().getThread().getStackTrace();
            for (StackTraceElement s : stackTrace) {
                /*if (isContainNative){
                    sb.append(s.toString());
                }else {
                    if (s.getClassName().contains(AopArms.getContext().getPackageName())){
                        sb.append(s.toString());
                    }
                }*/
                sb.append(s.toString());
                sb.append("\n");
            }
            AopLog.w(TAG, sb.toString());
        }
    };

    public static SystraceMonitor getInstance() {
        return sInstance;
    }

    /**
     * 开始计时
     */
    public void startMonitor() {
        startMonitorTime = System.currentTimeMillis();
        mIoHandler.postDelayed(mLogRunnable, filterTime);
    }

    /**
     * 停止计时
     */
    public void removeMonitor() {
        if (isBlock){
            AopLog.w(TAG, "耗时时长: ["+(System.currentTimeMillis() - startMonitorTime)+"ms]");
        }
        isBlock = false;
        startMonitorTime = 0L;
        mIoHandler.removeCallbacks(mLogRunnable);
    }
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/annotation/Component.java<|end_filename|>
package cn.com.superLei.aoparms.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * description $desc$
 * created by jerry on 2019/6/4.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface Component {
}

<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/aspect/PrefsEvictAspect.java<|end_filename|>
package cn.com.superLei.aoparms.aspect;

import android.text.TextUtils;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

import cn.com.superLei.aoparms.AopArms;
import cn.com.superLei.aoparms.annotation.PrefsEvict;
import cn.com.superLei.aoparms.common.utils.ArmsPreference;


/**
 * Created by jerry on 2018/6/13.
 */

@Aspect
public class PrefsEvictAspect {

    @Pointcut("execution(@cn.com.superLei.aoparms.annotation.PrefsEvict * *(..))")
    public void onPrefsEvictMethod() {
    }

    @Around("onPrefsEvictMethod() && @annotation(prefsEvict)")
    public Object doPrefsEvictMethod(final ProceedingJoinPoint joinPoint, PrefsEvict prefsEvict) throws Throwable {
        Object result = null;
        if (prefsEvict!=null) {
            String key = prefsEvict.key();
            boolean allEntries = prefsEvict.allEntries();
            result = joinPoint.proceed();
            if (allEntries){
                if (!TextUtils.isEmpty(key))
                    throw new IllegalArgumentException("Key cannot have value when cleaning all caches");
                ArmsPreference.clear(AopArms.getContext());
            }
            ArmsPreference.remove(AopArms.getContext(), key);
        } else {
            // 不影响原来的流程
            result = joinPoint.proceed();
        }

        return result;
    }

}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/aspect/ScheduledAwayAspect.java<|end_filename|>
package cn.com.superLei.aoparms.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

import cn.com.superLei.aoparms.annotation.ScheduledAway;
import cn.com.superLei.aoparms.common.collection.NoEmptyHashMap;
import io.reactivex.disposables.Disposable;

@Aspect
public class ScheduledAwayAspect {

    private static final String TAG = "ScheduledAspect";
    private static final String POINTCUT_METHOD = "execution(@cn.com.superLei.aoparms.annotation.ScheduledAway * *(..))";

    @Pointcut(POINTCUT_METHOD)
    public void onScheduledAwayMethod() {
    }

    @Around("onScheduledAwayMethod() && @annotation(scheduledAway)")
    public Object doScheduledAwayMethod(final ProceedingJoinPoint joinPoint, ScheduledAway scheduledAway) throws Throwable {
        Object result = null;
        String key = scheduledAway.key();
        Disposable subscribe = (Disposable) NoEmptyHashMap.getInstance().get(key);
        if (subscribe != null){
            subscribe.dispose();
            NoEmptyHashMap.getInstance().remove(key);
        }
        result = joinPoint.proceed();
        return result;
    }

}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/callback/StatisticCallback.java<|end_filename|>
package cn.com.superLei.aoparms.callback;

import cn.com.superLei.aoparms.common.statistic.StatisticInfo;

/**
 * description $desc$
 * created by jerry on 2019/6/3.
 */
public interface StatisticCallback {
    void statistics(StatisticInfo statisticInfo);
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/common/permission/AopPermissionActivity.java<|end_filename|>
package cn.com.superLei.aoparms.common.permission;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.text.TextUtils;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

import cn.com.superLei.aoparms.R;

/**
 * description $desc$
 * created by jerry on 2019/8/5.
 */
public class AopPermissionActivity extends Activity {
    private static IPermission permissionListener;
    private String[] permissions;
    private static final String PERMISSION_KEY = "permission_key";
    private static final String REQUEST_CODE = "request_code";
    private static final String REQUEST_RATIONALE = "request_rationale";
    private int requestCode;
    private String rationale;

    /**
     * 跳转到Activity申请权限
     *
     * @param context     Context
     * @param permissions Permission List
     * @param iPermission Interface
     */
    public static void PermissionRequest(Context context, String[] permissions, int requestCode, String rationale, IPermission iPermission) {
        permissionListener = iPermission;
        Intent intent = new Intent(context, AopPermissionActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
        Bundle bundle = new Bundle();
        bundle.putStringArray(PERMISSION_KEY, permissions);
        bundle.putInt(REQUEST_CODE, requestCode);
        bundle.putString(REQUEST_RATIONALE, rationale);
        intent.putExtras(bundle);
        context.startActivity(intent);
        if (context instanceof Activity) {
            ((Activity) context).overridePendingTransition(0, 0);
        }

    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_permission);
        if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        }
        Bundle bundle = getIntent().getExtras();
        if (bundle != null) {
            permissions = bundle.getStringArray(PERMISSION_KEY);
            requestCode = bundle.getInt(REQUEST_CODE, 0);
            rationale = bundle.getString(REQUEST_RATIONALE);
        }
        if (permissions == null || permissions.length <= 0) {
            finish();
            return;
        }
        requestPermission(permissions);
    }


    /**
     * 申请权限
     *
     * @param permissions permission list
     */
    private void requestPermission(String[] permissions) {

        if (AopPermissionUtils.hasSelfPermissions(this, permissions)) {
            //all permissions granted
            if (permissionListener != null) {
                permissionListener.permissionGranted();
                permissionListener = null;
            }
            finish();
            overridePendingTransition(0, 0);
        } else {
            boolean shouldShowRequestPermissionRationale = AopPermissionUtils
                    .shouldShowRequestPermissionRationale(this, permissions);
            if (TextUtils.isEmpty(rationale)){
                //request permissions
                ActivityCompat.requestPermissions(this, permissions, requestCode);
            }else {
                if (shouldShowRequestPermissionRationale){
                    showRequestPermissionRationale(this, permissions,rationale);
                }else {
                    //request permissions
                    ActivityCompat.requestPermissions(this, permissions, requestCode);
                }
            }
        }
    }

    public void showRequestPermissionRationale(Activity activity, String[] permissions, String rationale){
        new AlertDialog.Builder(activity)
                .setMessage(rationale)
                .setPositiveButton(R.string.btn_sure, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                        ActivityCompat.requestPermissions(activity, permissions, requestCode);
                    }
                })
                .setNegativeButton(R.string.btn_cancel, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.dismiss();
                        finish();
                        overridePendingTransition(0, 0);
                    }
                })
                .create().show();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

        if (AopPermissionUtils.verifyPermissions(grantResults)) {
            //所有权限都同意
            if (permissionListener != null) {
                permissionListener.permissionGranted();
            }
        } else {
            if (!AopPermissionUtils.shouldShowRequestPermissionRationale(this, permissions)) {
                //权限被拒绝并且选中不再提示
                if (permissions.length != grantResults.length) return;
                List<String> denyNoAskList = new ArrayList<>();
                for (int i = 0; i < grantResults.length; i++) {
                    if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
                        denyNoAskList.add(permissions[i]);
                    }
                }
                if (permissionListener != null) {
                    permissionListener.permissionNoAskDenied(requestCode, denyNoAskList);
                }
            } else {
                //权限被取消
                if (permissionListener != null) {
                    List<String> denyList = new ArrayList<>();
                    for (int i = 0; i < grantResults.length; i++) {
                        if (grantResults[i] == PackageManager.PERMISSION_DENIED) {
                            denyList.add(permissions[i]);
                        }
                    }
                    permissionListener.permissionDenied(requestCode, denyList);
                }
            }

        }
        permissionListener = null;
        finish();
        overridePendingTransition(0, 0);
    }
}


<|start_filename|>app/src/main/java/com/heaton/baselibsample/MainActivity.java<|end_filename|>
package com.heaton.baselibsample;

import android.Manifest;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

import butterknife.ButterKnife;
import butterknife.OnClick;
import cn.com.superLei.aoparms.annotation.Async;
import cn.com.superLei.aoparms.annotation.Cache;
import cn.com.superLei.aoparms.annotation.CacheEvict;
import cn.com.superLei.aoparms.annotation.Callback;
import cn.com.superLei.aoparms.annotation.DelayAway;
import cn.com.superLei.aoparms.annotation.Intercept;
import cn.com.superLei.aoparms.annotation.MainThread;
import cn.com.superLei.aoparms.annotation.Permission;
import cn.com.superLei.aoparms.annotation.PermissionDenied;
import cn.com.superLei.aoparms.annotation.PermissionNoAskDenied;
import cn.com.superLei.aoparms.annotation.Prefs;
import cn.com.superLei.aoparms.annotation.PrefsEvict;
import cn.com.superLei.aoparms.annotation.Retry;
import cn.com.superLei.aoparms.annotation.Safe;
import cn.com.superLei.aoparms.annotation.Scheduled;
import cn.com.superLei.aoparms.annotation.ScheduledAway;
import cn.com.superLei.aoparms.annotation.SingleClick;
import cn.com.superLei.aoparms.annotation.Statistics;
import cn.com.superLei.aoparms.annotation.TimeLog;
import cn.com.superLei.aoparms.common.permission.AopPermissionUtils;
import cn.com.superLei.aoparms.common.utils.ArmsCache;
import cn.com.superLei.aoparms.common.utils.ArmsPreference;

@Statistics(Constant.MAIN_ACTIVITY_STATISTIC_KEY)
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private String str;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);

        initData();

        initArticle();
    }

    /**
     * 开启请求权限注解
     * @ value 权限值
     * @ rationale 拒绝后的下一次提示(开启后，拒绝后，下一次会先提示该权限申请提示语)
     * @ requestCode 权限请求码标识
     */
    @Permission(value = {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE}, rationale = "为了更好的体验，请打开相关权限")
    public void permission(View view) {
        Log.e(TAG, "permission: 权限已打开");
    }

    /**
     * 请求拒绝注解回调
     * @param requestCode 权限请求码标识
     * @param denyList 被拒绝的权限集合
     */
    @PermissionDenied
    public void permissionDenied(int requestCode, List<String> denyList){
        Log.e(TAG, "permissionDenied: "+requestCode);
        Log.e(TAG, "permissionDenied>>>: "+denyList.toString());
    }

    /**
     * 请求拒绝且不在提示注解回调
     * @param requestCode 权限请求码标识
     * @param denyNoAskList 被拒绝且不再提示的权限集合
     */
    @PermissionNoAskDenied
    public void permissionNoAskDenied(int requestCode, List<String> denyNoAskList){
        Log.e(TAG, "permissionNoAskDenied: "+requestCode);
        Log.e(TAG, "permissionNoAskDenied>>>: "+denyNoAskList.toString());
        AopPermissionUtils.showGoSetting(this, "为了更好的体验，建议前往设置页面打开权限");
    }

    /**
     * key：缓存的键
     * expiry：缓存过期时间,单位s
     *
     * @return 缓存的值
     */
    @Cache(key = "userList", expiry = 60 * 60 * 24)
    private ArrayList<User> initData() {
        ArrayList<User> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            User user = new User();
            user.setName("艾神一不小心:" + i);
            user.setPassword("<PASSWORD>);
            list.add(user);
        }
        return list;
    }

    @Prefs(key = "article")
    private Article initArticle() {
        Article article = new Article();
        article.author = "jerry";
        article.title = "hello android";
        article.createDate = "2019-05-31";
        article.content = "this is a test demo";
        return article;
    }

    public void getArticle(View view) {
        Article article = ArmsPreference.get(this, "article", null);
        Log.e(TAG, "getArticle: " + article);
    }

    @TimeLog
    public void getUser(View view) {
        ArrayList<User> users = ArmsCache.get(this).getAsList("userList", User.class);
        Log.e(TAG, "getUser: " + users);
    }

    /**
     * key:缓存的键
     * beforeInvocation:缓存的清除是否在方法之前执行, 如果出现异常缓存就不会清除   默认false
     * allEntries：是否清空所有缓存(与key互斥)  默认false
     */
    @CacheEvict(key = "userList", beforeInvocation = true, allEntries = false)
    public void removeUser(View view) {
        Log.e(TAG, "removeUser: >>>>");
    }

    /**
     * key:sp的键
     * allEntries：是否清空所有存储(与key互斥)  默认false
     */
    @PrefsEvict(key = "article", allEntries = false)
    public void removeArticle(View view) {
        Log.e(TAG, "removeArticle: >>>>");
    }

    @Async
    public void asyn(View view) {
        Log.e(TAG, "useAync: " + Thread.currentThread().getName());
    }

    @Safe(callBack = "throwMethod")
    public void safe(View view) {
        str.toString();
    }

    @SingleClick(value = 2000L)
    private void onclick() {
        Log.e(TAG, "onclick: >>>>");
    }

    @Callback
    public void throwMethod(Throwable throwable) {
        Log.e(TAG, "throwMethod: >>>>>" + throwable.toString());
    }

    @Retry(count = 3, delay = 1000, asyn = true, retryCallback = "retryCallback")
    public boolean retry(View view) {
        Log.e(TAG, "retryDo: >>>>>>" + Thread.currentThread().getName());
        return false;
    }

    @Callback
    public void retryCallback(boolean result) {
        Log.e(TAG, "retryCallback: >>>>" + result);
    }

    @Scheduled(key = "scheduled", interval = 1000L, count = 10, taskExpiredCallback = "taskExpiredCallback")
    public void scheduled(View view) {
        Log.e(TAG, "scheduled: >>>>");
    }

    @ScheduledAway(key = "scheduled")
    public void cancelScheduled(View view){
        Log.e(TAG, "cancelScheduled: >>>>");
    }

    @Callback
    public void taskExpiredCallback() {
        Log.e(TAG, "taskExpiredCallback: >>>>");
    }

//    @Delay(key = "test", asyn = true, delay = 2000L)
    public void delay(View view) {
        try {
            Thread.sleep(400L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Log.e(TAG, "delay: >>>>>"+Thread.currentThread().getPriority());
    }

    @DelayAway(key = "test")
    public void cancelDelay(View view) {
        Log.e(TAG, "cancelDelay: >>>>");
    }

    @Intercept("login_intercept")
    public void intercept(View view) {
        Log.e(TAG, "intercept: 已登陆>>>>");
    }

    @Prefs(key = "userId")
    public String login(View view) {
        return "1";
    }

    @PrefsEvict(key = "userId")
    public void logout(View view) {
        Log.e(TAG, "logout: >>>>>");
    }

    @SingleClick(ids = {R.id.singleClick, R.id.singleClick2})
    @OnClick({R.id.singleClick1, R.id.singleClick, R.id.singleClick2})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.singleClick1:
                Log.e(TAG, "我不防抖");
                break;
            case R.id.singleClick:
                Log.e(TAG, "我防抖");
                break;
            case R.id.singleClick2:
                Log.e(TAG, "我防抖2");
                break;
        }
    }

    @Statistics(Constant.TEST_CLICK_STATISTIC_KEY)
    public void statistic(View view) {
        Log.e(TAG, "statistic: ");
    }

    @MainThread
    public void mainThread(View view) {
        Log.e(TAG, "mainThread: " + Thread.currentThread().getName());
    }
}


<|start_filename|>app/src/main/java/com/heaton/baselibsample/MyApplication.java<|end_filename|>
package com.heaton.baselibsample;

import android.app.Application;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;

import cn.com.superLei.aoparms.AopArms;
import cn.com.superLei.aoparms.Options;
import cn.com.superLei.aoparms.annotation.Delay;
import cn.com.superLei.aoparms.annotation.EnableSystrace;
import cn.com.superLei.aoparms.common.utils.ArmsPreference;


/**
 * 应用入口
 * Created by LiuLei on 2016/4/25.
 * 　　　　　　　　┏┓　　　┏┓
 * 　　　　　　　┏┛┻━━━┛┻┓
 * 　　　　　　　┃　　　　　　　┃
 * 　　　　　　　┃　　　━　　　┃
 * 　　　　　　　┃　＞　　　＜　┃
 * 　　　　　　　┃　　　　　　　┃
 * 　　　　　　　┃...　⌒　...　┃
 * 　　　　　　　┃　　　　　　　┃
 * 　　　　　　　┗━┓　　　┏━┛
 * 　　　　　　　　　┃　　　┃　Code is far away from bug with the animal protecting
 * 　　　　　　　　　┃　　　┃   神兽保佑,代码无bug
 * 　　　　　　　　　┃　　　┃
 * 　　　　　　　　　┃　　　┃
 * 　　　　　　　　　┃　　　┃
 * 　　　　　　　　　┃　　　┃
 * 　　　　　　　　　┃　　　┗━━━┓
 * 　　　　　　　　　┃　　　　　　　┣┓
 * 　　　　　　　　　┃　　　　　　　┏┛
 * 　　　　　　　　　┗┓┓┏━┳┓┏┛
 * 　　　　　　　　　　┃┫┫　┃┫┫
 * 　　　　　　　　　　┗┻┛　┗┻┛
 */
@EnableSystrace(filter = 60L, containNative = true)//开启耗时监控,过滤时间>60ms,包含native系统方法
public class MyApplication extends Application {

    private static final String TAG = "MyApplication";
    private static MyApplication mApplication;

    @Override
    public void onCreate() {
        super.onCreate();
        mApplication = this;
        AopArms.init(this, new Options().setLoggable(true));

        AopArms.setInterceptor((key, methodName) -> {
            Log.e(TAG, "intercept methodName:>>>>>"+methodName);
            if ("login_intercept".equals(key)){
                String userId = ArmsPreference.get(mApplication, "userId", "");
                if (TextUtils.isEmpty(userId)){
                    Toast.makeText(mApplication, "您还没有登录", Toast.LENGTH_SHORT).show();
                    return true;
                }
            }
            return false;
        });
        AopArms.setStatisticCallback(statisticInfo -> {
            Log.e(TAG, "statisticInfo: "+statisticInfo.toString());
        });

        initSDK();

    }

    /**
     * 异步初始化第三方sdk,可设置线程优先级
     */
    @Delay(delay = 100L, asyn = true, priority = 10)
    public void initSDK(){
        //...
    }

    public static MyApplication getInstance() {
        return mApplication;
    }


}
/**
 * 　                   &#######&
 * 　                   #########&
 * 　                  ###########&
 * 　                 ##&#$###$  ##&
 * 　                ;###  ####& ####
 * 　                ###;#######  ####
 * 　               &###########  #####
 * 　              ;#########o##   #####
 * 　              #########  ##   ######
 * 　            ;########### ###   ######
 * 　            ################   #######
 * 　           #################;   ######,
 * 　           #############$####   #######
 * 　          ########&;,########   &#######
 * 　        ;#########   &###       ########
 * 　        ##########    ###;      ########
 * 　       ###########     ##$      ########
 * 　       ###########     ###     #########
 * 　       ##########&$     ##    ;########
 * 　       #########, !      ##   ########
 * 　      ;########&          ##  #######
 * 　        #&#####            ##   &#&
 * 　          o# &#             #;
 * 　             ##             ##
 * 　             &#&           ;##
 * 　              ##           ###
 * <p/>
 * 　　　　　　　　　葱官赐福　　百无禁忌
 */


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/aspect/DelayAspect.java<|end_filename|>
package cn.com.superLei.aoparms.aspect;

import android.text.TextUtils;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

import java.util.concurrent.TimeUnit;

import cn.com.superLei.aoparms.annotation.Delay;
import cn.com.superLei.aoparms.common.collection.NoEmptyHashMap;
import cn.com.superLei.aoparms.common.utils.RxJavaHelper;
import io.reactivex.Observable;
import io.reactivex.Scheduler;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;

/**
 * description 延迟任务
 * created by jerry on 2019/5/30.
 */
@Aspect
public class DelayAspect {
    private static final String TAG = "DelayAspect";
    private static final String POINTCUT_METHOD = "execution(@cn.com.superLei.aoparms.annotation.Delay * *(..))";

    @Pointcut(POINTCUT_METHOD)
    public void onDelayMethod() {
    }

    @Around("onDelayMethod() && @annotation(delay)")
    public Object doDelayMethod(final ProceedingJoinPoint joinPoint, Delay delay) throws Throwable {
        String key = delay.key();
        if (TextUtils.isEmpty(key)){
            key = joinPoint.getSignature().getName();
        }
        long delayTime = delay.delay();
        boolean asyn = delay.asyn();
        int priority = delay.priority();
        TimeUnit unit = delay.timeUnit();
        Object result = null;
        if (delayTime>0) {
            final String finalKey = key;
            Scheduler scheduler = RxJavaHelper.scheduler(priority);
            Disposable subscribe = Observable.timer(delayTime, unit)
                    .subscribeOn(scheduler)
                    .observeOn(asyn ? scheduler: AndroidSchedulers.mainThread())
                    .subscribe(new Consumer<Long>() {
                        @Override
                        public void accept(Long aLong) throws Exception {
                            try {
                                joinPoint.proceed();
                                NoEmptyHashMap.getInstance().remove(finalKey);
                            } catch (Throwable throwable) {
                                throwable.printStackTrace();
                            }
                        }
                    });
            NoEmptyHashMap.getInstance().put(key, subscribe);
        } else {
            result = joinPoint.proceed();
        }
        return result;
    }
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/common/utils/ArmsPreference.java<|end_filename|>
package cn.com.superLei.aoparms.common.utils;

import android.content.Context;
import android.content.SharedPreferences;
import android.text.TextUtils;
import android.util.Base64;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.util.Map;

/**
 * Created by LiuLei on 2016/7/15.
 *
 */
public class ArmsPreference {

    /**
     * 保存在手机里面的文件名
     */
    public static final String FILE_NAME = "share_data";

    /**
     * 保存数据的方法，我们需要拿到保存数据的具体类型，然后根据类型调用不同的保存方法
     *
     * @param context
     * @param key
     * @param object
     */
    public static void put(Context context, String key, Object object) {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        if (object instanceof String) {
            editor.putString(key, (String) object);
        } else if (object instanceof Integer) {
            editor.putInt(key, (Integer) object);
        } else if (object instanceof Boolean) {
            editor.putBoolean(key, (Boolean) object);
        } else if (object instanceof Float) {
            editor.putFloat(key, (Float) object);
        } else if (object instanceof Long) {
            editor.putLong(key, (Long) object);
        } else {
            putObject(editor, key, object);
        }
        SharedPreferencesCompat.apply(editor);
    }

    /**
     * 得到保存数据的方法，我们根据默认值得到保存的数据的具体类型，然后调用相对于的方法获取值
     *
     * @param context
     * @param key
     * @param defaultObject
     * @return
     */
    public static <T> T get(Context context, String key, T defaultObject) {
	    SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
	    if (defaultObject instanceof String) {
		    return (T) sp.getString(key, (String) defaultObject);
	    } else if (defaultObject instanceof Integer) {
		    return (T) Integer.valueOf(sp.getInt(key, (Integer) defaultObject));
	    } else if (defaultObject instanceof Boolean) {
		    return (T) Boolean.valueOf(sp.getBoolean(key, (Boolean) defaultObject));
	    } else if (defaultObject instanceof Float) {
		    return (T) Float.valueOf(sp.getFloat(key, (Float) defaultObject));
	    } else if (defaultObject instanceof Long) {
		    return (T) Long.valueOf(sp.getLong(key, (Long) defaultObject));
	    } else {
		    return (T) getObject(sp, key);
	    }
    }

    private static Object getObject(SharedPreferences sp, String key) {
        try {
            String stringBase64 = sp.getString(key, "");
            if (TextUtils.isEmpty(stringBase64))
                return null;
            byte[] base64Bytes = Base64.decode(stringBase64.getBytes(), Base64.DEFAULT);
            ByteArrayInputStream bais = new ByteArrayInputStream(base64Bytes);
            ObjectInputStream ois = new ObjectInputStream(bais);
            return ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    private static void putObject(SharedPreferences.Editor editor, String key, Object obj) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(obj);

            String stringBase64 = new String(Base64.encode(baos.toByteArray(), Base64.DEFAULT));
            editor.putString(key, stringBase64);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }



    /**
     * 移除某个key值已经对应的值
     *
     * @param context
     * @param key
     */
    public static void remove(Context context, String key) {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.remove(key);
        SharedPreferencesCompat.apply(editor);
    }

    /**
     * 清除所有数据
     *
     * @param context
     */
    public static void clear(Context context) {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sp.edit();
        editor.clear();
        SharedPreferencesCompat.apply(editor);
    }

    /**
     * 查询某个key是否已经存在
     *
     * @param context
     * @param key
     * @return
     */
    public static boolean contains(Context context, String key) {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        return sp.contains(key);
    }

    /**
     * 返回所有的键值对
     *
     * @param context
     * @return
     */
    public static Map<String, ?> getAll(Context context) {
        SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE);
        return sp.getAll();
    }

    /**
     * 创建一个解决SharedPreferencesCompat.apply方法的一个兼容类
     *
     * @author zhy
     */
    private static class SharedPreferencesCompat {
        private static final Method sApplyMethod = findApplyMethod();

        /**
         * 反射查找apply的方法
         *
         * @return
         */
        @SuppressWarnings({"unchecked", "rawtypes"})
        private static Method findApplyMethod() {
            try {
                Class clz = SharedPreferences.Editor.class;
                return clz.getMethod("apply");
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
            return null;
        }

        /**
         * 如果找到则使用apply执行，否则使用commit
         *
         * @param editor
         */
        public static void apply(SharedPreferences.Editor editor) {
            try {
                if (sApplyMethod != null) {
                    sApplyMethod.invoke(editor);
                    return;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            editor.commit();
        }
    }

}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/aspect/StatisticsAspect.java<|end_filename|>
package cn.com.superLei.aoparms.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;

import cn.com.superLei.aoparms.AopArms;
import cn.com.superLei.aoparms.annotation.Statistics;
import cn.com.superLei.aoparms.callback.StatisticCallback;
import cn.com.superLei.aoparms.common.statistic.StatisticInfo;

/**
 * description $desc$
 * created by jerry on 2019/5/30.
 */
@Aspect
public class StatisticsAspect {
    private static final String POINTCUT_METHOD = "execution(@cn.com.superLei.aoparms.annotation.Statistics * *(..))";

    @Pointcut(POINTCUT_METHOD)
    public void onStatisticsMethod() {
    }

    @Around("onStatisticsMethod() && @annotation(statistics)")
    public Object doStatisticsMethod(ProceedingJoinPoint joinPoint, Statistics statistics) throws Throwable {
        int value = statistics.value();
        StatisticCallback callback = AopArms.getStatisticCallback();
        if (callback != null){
            Signature signature = joinPoint.getSignature();
            if (signature instanceof MethodSignature){
                StatisticInfo info = new StatisticInfo(value, System.currentTimeMillis(),signature.getName(),true);
                callback.statistics(info);
            }
        }
        return joinPoint.proceed();
    }
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/aspect/SingleClickAspect.java<|end_filename|>
package cn.com.superLei.aoparms.aspect;

import android.util.Log;
import android.view.View;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

import java.util.Calendar;

import cn.com.superLei.aoparms.R;
import cn.com.superLei.aoparms.annotation.SingleClick;

/**
 * Created by jerry on 2018/6/13.
 */

@Aspect
public class SingleClickAspect {
    static int TIME_TAG = R.id.click_time;

    @Pointcut("execution(@cn.com.superLei.aoparms.annotation.SingleClick * *(..))")//方法切入点
    public void onSingleClickMethod() {
    }

    @Around("onSingleClickMethod() && @annotation(singleClick)")//在连接点进行方法替换
    public void doSingleClickMethod(ProceedingJoinPoint joinPoint, SingleClick singleClick) throws Throwable {
        View view = null;
        for (Object arg : joinPoint.getArgs())
            if (arg instanceof View) view = (View) arg;
        if (view != null) {
            Object tag = view.getTag(TIME_TAG);
            long lastClickTime = ((tag != null) ? (long) tag : 0);
            Log.d("SingleClickAspect", "lastClickTime:" + lastClickTime);
            long currentTime = Calendar.getInstance().getTimeInMillis();
            long value = singleClick.value();
            int[] ids = singleClick.ids();

            if (currentTime - lastClickTime > value || !hasId(ids, view.getId())) {//过滤掉500毫秒内的连续点击
                view.setTag(TIME_TAG, currentTime);
                Log.d("SingleClickAspect", "currentTime:" + currentTime);
                joinPoint.proceed();//执行原方法
            }

        }
    }

    public static boolean hasId(int[] arr, int value) {
        for (int i : arr) {
            if (i == value)
                return true;
        }
        return false;
    }
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/common/utils/RxJavaHelper.java<|end_filename|>
package cn.com.superLei.aoparms.common.utils;

import android.os.Process;
import android.support.annotation.Size;

import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

import io.reactivex.Scheduler;
import io.reactivex.annotations.NonNull;
import io.reactivex.schedulers.Schedulers;

public class RxJavaHelper {

    public static ThreadFactory newFactory(@Size(min = 1, max = 10) int priority){
        return new ThreadFactory() {
            @Override
            public Thread newThread(@NonNull final Runnable r) {
                Thread thread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //这里的r是线程池或Sceduler的Worker，所以我们要在调用r.run()方法前提前设置优先级
                        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                        r.run();
                    }
                });
                thread.setPriority(priority);
                return thread;
            }
        };
    }

    public static Scheduler scheduler(int priority){
        if (priority!=5){
            return Schedulers.from(Executors.newScheduledThreadPool(1, newFactory(priority)));
        }
        return Schedulers.io();
    }

}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/common/reflect/NULL.java<|end_filename|>
package cn.com.superLei.aoparms.common.reflect;

/**
 * description $desc$
 * created by jerry on 2019/5/29.
 */
public class NULL {
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/common/utils/FileUtils.java<|end_filename|>
package cn.com.superLei.aoparms.common.utils;

import java.io.File;

/**
 * description $desc$
 * created by jerry on 2019/5/29.
 */
public class FileUtils {
    public static boolean exists(File file) {
        return file!=null && file.exists();
    }

    /**
     * 判断是否文件
     * @param file
     * @return
     */
    public static boolean isFile(File file) {
        return exists(file) && file.isFile();
    }

    /**
     * 判断是否目录
     * @param file
     * @return
     */
    public static boolean isDirectory(File file) {
        return exists(file) && file.isDirectory();
    }
}


<|start_filename|>app/src/main/java/com/heaton/baselibsample/Article.java<|end_filename|>
package com.heaton.baselibsample;

import java.io.Serializable;

/**
 * description $desc$
 * created by jerry on 2019/5/28.
 */
public class Article implements Serializable {

    public String author;
    public String title;
    public String createDate;
    public String content;

    @Override
    public String toString() {
        return "Article{" +
                "author='" + author + '\'' +
                ", title='" + title + '\'' +
                ", createDate='" + createDate + '\'' +
                ", content='" + content + '\'' +
                '}';
    }
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/annotation/Safe.java<|end_filename|>
package cn.com.superLei.aoparms.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * description $desc$
 * created by jerry on 2019/5/30.
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Safe {

    String callBack() default "";
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/common/utils/Preconditions.java<|end_filename|>
package cn.com.superLei.aoparms.common.utils;


import java.util.List;
import java.util.Map;
import java.util.Set;

public class Preconditions {

    /**
     *  可以判断任何一个对象是否为空,包括List Map String 复杂对象等等,
     *  只能判断对象,而不能判断基本数据类型
     *  Preconditions.isBlank("")  true
     *  Preconditions.isBlank(" ")  true
     *  Preconditions.isBlank(null)  true
     *  Preconditions.isBlank("null")  false
     * @param t
     * @param <T>
     * @return
     */
    public static <T> boolean isBlank(T t) {

        if (t==null) {
            return true;
        }


        if (t instanceof List) {
            if (((List) t).size()==0) {
                return true;
            }
        } else if (t instanceof Map) {
            if (((Map) t).size()==0) {
                return true;
            }
        } else if (t instanceof Set) {
            if (((Set) t).size()==0) {
                return true;
            }
        } else if (t instanceof Object []) {
            if (((Object[]) t).length==0) {
                return true;
            }
        } else if (t instanceof String) {

            String str = (String)t;
            if (str.length()==0) return true;

            str = str.trim();
            if (str.length()==0) return true;
        }

        return false;
    }

    public static <T> boolean isNotBlank(T t) {
        return !isBlank(t);
    }

    public static boolean isNotBlanks(Object... objects) {

        if (objects==null) {
            return false;
        }

        for (Object obj:objects) {
            if (isBlank(obj)) {
                return false;
            }
        }

        return true;
    }

    public static <T> T checkNotNull(T arg) {
        return checkNotNull(arg, "Argument must not be null");
    }

    public static <T> T checkNotNull(T arg, String message) {
        if (arg == null) {
            throw new NullPointerException(message);
        }
        return arg;
    }
}



<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/aspect/RetryAspect.java<|end_filename|>
package cn.com.superLei.aoparms.aspect;

import android.util.Log;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;

import java.util.concurrent.TimeUnit;

import cn.com.superLei.aoparms.annotation.Retry;
import cn.com.superLei.aoparms.common.reflect.Reflect;
import cn.com.superLei.aoparms.common.reflect.ReflectException;
import cn.com.superLei.aoparms.common.utils.Preconditions;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.ObservableSource;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;

@Aspect
public class RetryAspect {

    private static final String TAG = "RetryAspect";
    private static final String POINTCUT_METHOD = "execution(@cn.com.superLei.aoparms.annotation.Retry * *(..))";

    private int retryCount = 0;//当前出错重试次数

    @Pointcut(POINTCUT_METHOD)
    public void onRetryMethod() {
    }

    @Around("onRetryMethod() && @annotation(retry)")
    public Object doRetryMethod(final ProceedingJoinPoint joinPoint, Retry retry) throws Throwable {

        final int count = retry.count();
        final long delay = retry.delay();
        boolean asyn = retry.asyn();
        final String retryCallback = retry.retryCallback();
        String type = ((MethodSignature) joinPoint.getSignature()).getReturnType().toString();
        if (!"boolean".equalsIgnoreCase(type)){
            Log.e(TAG, "方法返回值必须是boolean类型");
            return joinPoint.proceed();
        }
        Disposable subscribe = Observable.create(new ObservableOnSubscribe<Boolean>() {
            @Override
            public void subscribe(ObservableEmitter<Boolean> emitter) throws Exception {
                Boolean value = false;
                try {
                    Log.e(TAG, "任务重试中,当前重试次数>>>>"+retryCount+"---"+Thread.currentThread().getName());
                    value = (Boolean) joinPoint.proceed();
                } catch (Throwable throwable) {
                    throwable.printStackTrace();
                }
                if (value){
                    Log.e(TAG, "任务请求成功,当前重试次数>>>>"+retryCount);
                    doRetryResult(joinPoint, retryCallback, true);
                    retryCount = 0;
                }else {
                    throw new Exception("任务请求失败,准备重试...");
                }
            }
        }).observeOn(Schedulers.io())
          .subscribeOn(asyn ? Schedulers.io() : AndroidSchedulers.mainThread())
          .retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {
            @Override
            public ObservableSource<?> apply(Observable<Throwable> throwableObservable) throws Exception {
                return throwableObservable
                        .flatMap(new Function<Throwable, ObservableSource<?>>() {
                            @Override
                            public ObservableSource<?> apply(Throwable throwable) throws Exception {
                                if (++retryCount <= count) {
                                    Log.e(TAG, "An error occurred, ready to try again, retries count>>>>>" + retryCount);
                                    // When this Observable calls onNext, the original Observable will be retried (i.e. re-subscribed).
                                    return Observable.timer(delay, TimeUnit.MILLISECONDS);
                                }
                                return Observable.error(throwable);
                            }
                        });
            }
        }).subscribe(new Consumer<Boolean>() {
                    @Override
                    public void accept(Boolean aBoolean) throws Exception {
                        Log.e(TAG, "accept: >>>>>" + aBoolean);
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        Log.e(TAG, "throwable: >>>>>" + throwable.getMessage());
                        doRetryResult(joinPoint, retryCallback, false);
                        retryCount = 0;
                    }
                });
        return null;
    }

    private void doRetryResult(ProceedingJoinPoint joinPoint, String retryCallback, boolean result){
        if (Preconditions.isNotBlank(retryCallback)) {
            try {
                Reflect.on(joinPoint.getTarget()).callback(retryCallback, result);
            } catch (ReflectException exception) {
                exception.printStackTrace();
                Log.e(TAG, "no method "+retryCallback);
            }
        }
    }
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/common/statistic/ActivityLifecycleImpl.java<|end_filename|>
package cn.com.superLei.aoparms.common.statistic;

import android.app.Activity;
import android.app.Application;
import android.os.Bundle;

import java.lang.annotation.Annotation;

import cn.com.superLei.aoparms.AopArms;
import cn.com.superLei.aoparms.annotation.Statistics;
import cn.com.superLei.aoparms.callback.StatisticCallback;

public class ActivityLifecycleImpl implements Application.ActivityLifecycleCallbacks {

    private void proceedStatistic(Activity activity, StatisticInfo.ActivityLife activityLife){
        Class<? extends Activity> cls = activity.getClass();
        if (cls.isAnnotationPresent(Statistics.class)){
            for(Annotation ann : cls.getDeclaredAnnotations()){
                if(ann instanceof Statistics){
                    int value = ((Statistics) ann).value();
                    StatisticInfo info = new StatisticInfo(value,System.currentTimeMillis(),cls.getSimpleName(),true, activityLife);
                    StatisticCallback callback = AopArms.getStatisticCallback();
                    if (callback != null){
                        callback.statistics(info);
                    }
                }
            }
        }
    }

    @Override
    public void onActivityCreated(Activity activity, Bundle bundle) {
        proceedStatistic(activity, StatisticInfo.ActivityLife.CREATE);
    }

    @Override
    public void onActivityStarted(Activity activity) {

    }

    @Override
    public void onActivityResumed(Activity activity) {

    }

    @Override
    public void onActivityPaused(Activity activity) {
        proceedStatistic(activity, StatisticInfo.ActivityLife.PAUSE);
    }

    @Override
    public void onActivityStopped(Activity activity) {

    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {

    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        proceedStatistic(activity, StatisticInfo.ActivityLife.DESTROY);
    }
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/annotation/Scheduled.java<|end_filename|>
package cn.com.superLei.aoparms.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.concurrent.TimeUnit;

/**
 * description 定时任务
 * created by jerry on 2019/5/30.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Scheduled {

    String key() default "";
    /**
     * 初始化延迟
     * @return
     */
    long initialDelay() default 0L;

    /**
     * 时间间隔
     * @return
     */
    long interval();

    /**
     * 时间单位
     * @return
     */
    TimeUnit timeUnit() default TimeUnit.MILLISECONDS;

    /**
     * 执行次数
     * @return
     */
    int count() default Integer.MAX_VALUE;

    /**
     * 定时任务到期回调
     * @return
     */
    String taskExpiredCallback() default "";
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/aspect/PrefsAspect.java<|end_filename|>
package cn.com.superLei.aoparms.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;

import cn.com.superLei.aoparms.AopArms;
import cn.com.superLei.aoparms.annotation.Prefs;
import cn.com.superLei.aoparms.common.utils.ArmsPreference;


/**
 * Created by jerry on 2018/6/13.
 */

@Aspect
public class PrefsAspect {

    @Pointcut("execution(@cn.com.superLei.aoparms.annotation.Prefs * *(..))")
    public void onPrefsMethod() {
    }

    @Around("onPrefsMethod() && @annotation(prefs)")
    public Object doPrefsMethod(final ProceedingJoinPoint joinPoint, Prefs prefs) throws Throwable {
        Object result = null;
        if (prefs!=null) {
            String key = prefs.key();

            result = joinPoint.proceed();
            String type = ((MethodSignature) joinPoint.getSignature()).getReturnType().toString();

            if (!"void".equalsIgnoreCase(type)) {
                ArmsPreference.put(AopArms.getContext(), key, result);
            }
        } else {
            // 不影响原来的流程
            result = joinPoint.proceed();
        }

        return result;
    }

}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/common/statistic/StatisticsLife.java<|end_filename|>
package cn.com.superLei.aoparms.common.statistic;

import android.app.Application;
import android.util.Log;

public class StatisticsLife {
    private static final String TAG = StatisticsLife.class.getSimpleName();

    private static ActivityLifecycleImpl sLifecycleImpl;

    public static ActivityLifecycleImpl getLifecycle() {
        return sLifecycleImpl;
    }

    public static void registerStatisticsLife(Application application) {
        if (sLifecycleImpl == null) {
            synchronized (StatisticsLife.class) {
                if (sLifecycleImpl == null) {
                    sLifecycleImpl = new ActivityLifecycleImpl();
                }
            }
        }
        application.registerActivityLifecycleCallbacks(sLifecycleImpl);
    }

    public static void unregisterStatisticsLife(Application application) {
        if (sLifecycleImpl != null) {
            application.unregisterActivityLifecycleCallbacks(sLifecycleImpl);
        } else {
            Log.i(TAG, "ActivityLifecycleImpl不能为空");
        }
    }

}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/aspect/PermissionAspect.java<|end_filename|>
package cn.com.superLei.aoparms.aspect;

import android.app.Fragment;
import android.content.Context;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

import cn.com.superLei.aoparms.AopArms;
import cn.com.superLei.aoparms.annotation.Permission;
import cn.com.superLei.aoparms.annotation.PermissionDenied;
import cn.com.superLei.aoparms.annotation.PermissionNoAskDenied;
import cn.com.superLei.aoparms.common.permission.IPermission;
import cn.com.superLei.aoparms.common.permission.AopPermissionActivity;


/**
 * Created by jerry on 2018/6/13.
 */

@Aspect
public class PermissionAspect {

    @Around("execution(@cn.com.superLei.aoparms.annotation.Permission * *(..)) && @annotation(permission)")
    public void aroundJoinPoint(final ProceedingJoinPoint joinPoint, final Permission permission) throws Throwable {
        Context context = null;
        final Object object = joinPoint.getThis();
        if (object instanceof Context) {
            context = (Context) object;
        } else if (object instanceof Fragment) {
            context = ((Fragment) object).getActivity();
        } else if (object instanceof android.support.v4.app.Fragment) {
            context = ((android.support.v4.app.Fragment) object).getActivity();
        } else {
            //获取切入点方法上的参数列表
            Object[] objects = joinPoint.getArgs();
            if (objects.length > 0) {
                //非静态方法且第一个参数为context
                if (objects[0] instanceof Context) {
                    context = (Context) objects[0];
                } else {
                    //没有传入context 默认使用application
                    context = AopArms.getContext();
                }
            } else {
                context = AopArms.getContext();
            }
        }
        if (context == null || permission == null) {
            joinPoint.proceed();
            return;
        }
        AopPermissionActivity.PermissionRequest(context, permission.value(),
                permission.requestCode(), permission.rationale(), new IPermission() {
                    @Override
                    public void permissionGranted() {
                        try {
                            joinPoint.proceed();
                        } catch (Throwable throwable) {
                            throwable.printStackTrace();
                        }
                    }

                    @Override
                    public void permissionNoAskDenied(int requestCode, List<String> denyNoAskList) {
                        Class<?> cls = object.getClass();
                        Method[] methods = cls.getDeclaredMethods();
                        if (methods.length == 0) return;
                        for (Method method : methods) {
                            //过滤不含自定义注解PermissionNoAskDenied的方法
                            boolean isHasAnnotation = method.isAnnotationPresent(PermissionNoAskDenied.class);
                            if (isHasAnnotation) {
                                method.setAccessible(true);
                                //获取方法类型
                                Class<?>[] types = method.getParameterTypes();
                                if (types.length != 2) return;
                                //获取方法上的注解
                                PermissionNoAskDenied aInfo = method.getAnnotation(PermissionNoAskDenied.class);
                                if (aInfo == null) return;
                                //解析注解上对应的信息
                                try {
                                    method.invoke(object, requestCode, denyNoAskList);
                                } catch (IllegalAccessException e) {
                                    e.printStackTrace();
                                } catch (InvocationTargetException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }

                    @Override
                    public void permissionDenied(int requestCode, List<String> denyList) {
                        Class<?> cls = object.getClass();
                        Method[] methods = cls.getDeclaredMethods();
                        if (methods.length == 0) return;
                        for (Method method : methods) {
                            //过滤不含自定义注解PermissionDenied的方法
                            boolean isHasAnnotation = method.isAnnotationPresent(PermissionDenied.class);
                            if (isHasAnnotation) {
                                method.setAccessible(true);
                                //获取方法类型
                                Class<?>[] types = method.getParameterTypes();
                                if (types.length != 2) return;
                                //获取方法上的注解
                                PermissionDenied aInfo = method.getAnnotation(PermissionDenied.class);
                                if (aInfo == null) return;
                                //解析注解上对应的信息
                                try {
                                    method.invoke(object, requestCode, denyList);
                                } catch (IllegalAccessException e) {
                                    e.printStackTrace();
                                } catch (InvocationTargetException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }

                });
    }
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/common/PathUtils.java<|end_filename|>
package cn.com.superLei.aoparms.common;

import java.io.File;

/**
 * description $desc$
 * created by jerry on 2019/6/4.
 */
public class PathUtils {
    private PathUtils() {}

    /**
     * 把路径字符串转换为包名.
     * a/b/c/d -> a.b.c.d
     *
     * @param path
     * @return
     */
    public static String pathToPackage(String path) {
        if (path.startsWith("/")) {
            path = path.substring(1);
        }

        return path.replaceAll("/", ".");
    }

    /**
     * 包名转换为路径名
     * @param pkg
     * @return
     */
    public static String packageToPath(String pkg) {
        return pkg.replaceAll("\\.", File.separator);
    }

    /**
     * 将多个对象转换成字符串并连接起来
     * @param objs
     * @return
     */
    public static String concat(Object... objs) {
        StringBuilder sb = new StringBuilder(30);
        for (int ix = 0 ; ix < objs.length ; ++ix) {
            sb.append(objs[ix]);
        }

        return sb.toString();
    }

    /**
     * 去掉文件的后缀名
     * @param name
     * @return
     */
    public static String trimSuffix(String name) {
        int dotIndex = name.indexOf('.');
        if (-1 == dotIndex) {
            return name;
        }

        return name.substring(0, dotIndex);
    }

    public static String distillPathFromJarURL(String url) {
        int startPos = url.indexOf(':');
        int endPos = url.lastIndexOf('!');

        return url.substring(startPos + 1, endPos);
    }

    public static void main(String[] args) {
        String s = "/a/b/c/d";
        System.out.println(pathToPackage(s));
    }
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/AopArms.java<|end_filename|>
package cn.com.superLei.aoparms;

import android.annotation.SuppressLint;
import android.app.Application;
import android.content.Context;

import cn.com.superLei.aoparms.annotation.EnableSystrace;
import cn.com.superLei.aoparms.callback.Interceptor;
import cn.com.superLei.aoparms.callback.StatisticCallback;
import cn.com.superLei.aoparms.common.statistic.StatisticsLife;
import cn.com.superLei.aoparms.common.systrace.SystraceMonitor;

/**
 * description $desc$
 * created by jerry on 2019/5/31.
 */
public class AopArms {
    @SuppressLint("StaticFieldLeak")
    private static Application application;
    private static Interceptor sInterceptor;
    private static StatisticCallback statisticCallback;

    public static void init(Application app){
        init(app, new Options());
    }

    public static void init(Application app, Options options){
        if (app == null)throw new IllegalArgumentException("application is null");
        application = app;
        AopLog.init(options);
        StatisticsLife.registerStatisticsLife(application);
        systraceEnable(application);
    }

    private static void systraceEnable(Application app) {
        Class<? extends Application> cls = app.getClass();
        if (cls.isAnnotationPresent(EnableSystrace.class)){
            EnableSystrace systrace = cls.getAnnotation(EnableSystrace.class);
            if (systrace != null){
                long filter = systrace.filter();
                boolean containNative = systrace.containNative();
                SystraceMonitor monitor = SystraceMonitor.getInstance();
                monitor.setContainNative(containNative);
                monitor.setFilterTime(filter);
                monitor.start();
            }
        }
    }

    public static Application getApplication(){
        if (application == null){
            throw new IllegalStateException("please init AopArms");
        }
        return application;
    }

    public static Context getContext(){
        return getApplication().getApplicationContext();
    }

    public static void setInterceptor(Interceptor interceptor){
        sInterceptor = interceptor;
    }

    public static Interceptor getInterceptor(){
        return sInterceptor;
    }

    public static StatisticCallback getStatisticCallback() {
        return statisticCallback;
    }

    public static void setStatisticCallback(StatisticCallback statisticCallback) {
        AopArms.statisticCallback = statisticCallback;
    }
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/common/collection/NoEmptyHashMap.java<|end_filename|>
package cn.com.superLei.aoparms.common.collection;


import java.util.HashMap;
import java.util.Map;

import cn.com.superLei.aoparms.common.utils.Preconditions;

/**
 * description $desc$
 * created by jerry on 2019/5/30.
 */
public class NoEmptyHashMap<K,V> extends HashMap<K, V> {

    private static NoEmptyHashMap noEmptyHashMap = new NoEmptyHashMap();
    public static <K,V>NoEmptyHashMap <K,V>getInstance() {
        return noEmptyHashMap;
    }

    private NoEmptyHashMap() {
        super();
    }

    @Override
    public V put(final K key, final V value) {
        if (Preconditions.isBlank(key) || Preconditions.isBlank(value)) {
            return null;
        }
        return super.put(key, value);
    }

    @Override
    public void putAll(final Map<? extends K, ? extends V> map) {
        for (Entry<? extends K, ? extends V> entry : map.entrySet()) {
            put(entry.getKey(), entry.getValue());
        }
    }
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/aspect/SafeAspect.java<|end_filename|>
package cn.com.superLei.aoparms.aspect;

import android.util.Log;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

import java.io.PrintWriter;
import java.io.StringWriter;

import cn.com.superLei.aoparms.annotation.Safe;
import cn.com.superLei.aoparms.common.reflect.Reflect;
import cn.com.superLei.aoparms.common.reflect.ReflectException;
import cn.com.superLei.aoparms.common.utils.Preconditions;

/**
 * description $desc$
 * created by jerry on 2019/5/30.
 */
@Aspect
public class SafeAspect {

    private static final String TAG = "SafeAspect";
    private static final String POINTCUT_METHOD = "execution(@cn.com.superLei.aoparms.annotation.Safe * *(..))";

    @Pointcut(POINTCUT_METHOD)
    public void onSafeMethod() {
    }

    @Around("onSafeMethod() && @annotation(safe)")
    public Object doSafeMethod(final ProceedingJoinPoint joinPoint, Safe safe) throws Throwable {

        Object result = null;
        try {
            result = joinPoint.proceed();
        } catch (Throwable e) {
            Log.w(TAG, getStringFromException(e));

            String callBack = safe.callBack();

            if (Preconditions.isNotBlank(callBack)) {

                try {
                    Reflect.on(joinPoint.getTarget()).callback(callBack, e);
                } catch (ReflectException exception) {
                    exception.printStackTrace();
                    Log.e(TAG, "no method "+callBack);
                }
            }
        }
        return result;
    }

    private static String getStringFromException(Throwable ex) {
        StringWriter errors = new StringWriter();
        ex.printStackTrace(new PrintWriter(errors));
        return errors.toString();
    }
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/annotation/EnableSystrace.java<|end_filename|>
package cn.com.superLei.aoparms.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface EnableSystrace {
    long filter() default 60L;
    boolean containNative() default false;
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/aspect/CacheAspect.java<|end_filename|>
package cn.com.superLei.aoparms.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

import java.io.Serializable;
import cn.com.superLei.aoparms.AopArms;
import cn.com.superLei.aoparms.annotation.Cache;
import cn.com.superLei.aoparms.common.utils.ArmsCache;

/**
 * description $desc$
 * created by jerry on 2019/5/30.
 */
@Aspect
public class CacheAspect {
    private static final String POINTCUT_METHOD = "execution(@cn.com.superLei.aoparms.annotation.Cache * *(..))";

    @Pointcut(POINTCUT_METHOD)
    public void onCacheMethod() {
    }

    @Around("onCacheMethod() && @annotation(cache)")
    public Object doCacheMethod(ProceedingJoinPoint joinPoint, Cache cache) throws Throwable {
        String key = cache.key();
        int expiry = cache.expiry();

        Object result = joinPoint.proceed();
        ArmsCache aCache = ArmsCache.get(AopArms.getContext());
        if (expiry>0) {
            aCache.put(key,(Serializable)result,expiry);
        } else {
            aCache.put(key,(Serializable)result);
        }
        return result;
    }
}


<|start_filename|>aop-arms/src/main/java/cn/com/superLei/aoparms/annotation/MainThread.java<|end_filename|>
package cn.com.superLei.aoparms.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface MainThread {
}
