Git Product home page Git Product logo

android-kvo's Introduction

Android KVO

简介

  • KVO, 即 Key-Value Observing 的缩写,当指定的实例的属性被修改后,该实例该属性的指定观察者可以收到通知。简单的说就是每次指定的被观察的实例的属性被修改后,KVO 就会自动通知相应的观察者。 可以使用 KVO 来检测对象属性的变化、快速做出响应,这能够为开发强交互、响应式应用以及实现视图和模型的双向绑定时提供大量的帮助。

  • 相信 iOS 开发的同学会很熟悉,KVO 的**本身是 Object-C 语言的一种特性,但是据说在 iOS 开发过程中比较麻烦并且还容易出现内存泄漏隐患而不被经常使用。

  • Android KVO 参考了 iOS 中的**使用在 Android 系统上实现一套属性改变自动通知的框架,结合预编译手段实现了简单使用注解就可以很方便的使用 KVO。 Java 中内存自动回收机制,使用 KVO 不用担心 iOS 中出现循环引用之类的内存泄漏。

使用

KVO 使用了 gradle 插件实现预编译,依赖了另一个 easy-gradle-plugin gradle 插件,实现更简单的使用自定义 gradle 插件,如需有兴趣移步了解。如果只想了解 KVO 的使用,不关心实现原理可忽略 easy-gradle-plugin 这部分。

1. build.gradle 构建脚本配置

  • 在项目根目录下的 build.gradle 中对应的块中添加以下下配置信息
buildscript {
    repositories {
	    maven{ url uri('https://oss.sonatype.org/content/groups/staging')}
    }
    dependencies {
        classpath "com.github.drumge:easy-plugin:0.2.4"
        classpath "com.github.drumge:kvo-plugin:0.2.18"
    }
}
allprojects {
    repositories {
        maven{ url uri('https://oss.sonatype.org/content/groups/staging')} 
    }
}
  • 在 application module(as默认叫app) 下的 build.gradle 中添加以下配置信息
dependencies {
	implementation "com.github.drumge:kvo-api:0.2.9"
}

import com.drumge.kvo.plugin.KvoPlugin
import com.drumge.kvo.plugin.KvoTransform
apply plugin: 'com.drumge.easy.plugin'
easy_plugin {
    enable = true
    plugins{
        kvo {
            plugin = new KvoPlugin(project)
            transform = new KvoTransform(project)
        }
    }
}
  • 在使用 KVO 的 module 的 build.gradle 中添加
dependencies {
    annotationProcessor "com.github.drumge:kvo-compiler:0.2.5"
}

2. KVO 初始化

出于轻量化的考虑,并不实现自己的日志库和线程池,而是提供了接口提供使用中设置自己的日志实现和线程池。这种方式对使用者来说,可以有效的控制线程池的使用和线程总数的控制。

  • 初始化线程池(必须) 实现 com.drumge.kvo.api.thread.IKvoThread 接口,并通过调用 Kvo.getInstance().setThread(new KvoThread()) 初始化。

  • 初始化日志(非必须) 实现 com.drumge.kvo.api.log.IKvoLog 接口,并通过调用 Kvo.getInstance().setLog(new KvoLog()) 初始化。

  • 最后给一个简单的初始化例子, 然后在使用前调用 KvoInit.initKvo() 即可,初始化不会耗时,建议在 Application onCreate 中初始化

public class KvoInit {

    public static void initKvo() {
        Kvo.getInstance().setLog(new KvoLog());
        Kvo.getInstance().setThread(new KvoThread());
    }

    private static class KvoThread implements IKvoThread {
        Handler mMainHandler = new Handler(Looper.getMainLooper());
        // 线程池可换成项目中统一的线程池,应用中只应存在一个全局统一的线程池,不建议创建多个线程池;
        private ScheduledExecutorService mThreadPool = Executors.newSingleThreadScheduledExecutor();

        @Override
        public void mainThread(@NonNull Runnable runnable) {
            mMainHandler.post(runnable);
        }

        @Override
        public void mainThread(@NonNull Runnable runnable, long l) {
            mMainHandler.postDelayed(runnable, l);
        }

        @Override
        public void workThread(@NonNull Runnable runnable) {
            mThreadPool.schedule(runnable, 0, TimeUnit.MILLISECONDS);
        }

        @Override
        public void workThread(@NonNull Runnable runnable, long l) {
            mThreadPool.schedule(runnable, l, TimeUnit.MILLISECONDS);
        }
    }

    private static class KvoLog implements IKvoLog {

        @Override
        public void debug(Object o, String s, Object... objects) {
            Log.d(String.valueOf(o), String.format(s, objects));
        }

        @Override
        public void info(Object o, String s, Object... objects) {
            Log.i(String.valueOf(o), String.format(s, objects));
        }

        @Override
        public void warn(Object o, String s, Object... objects) {
            Log.w(String.valueOf(o), String.format(s, objects));
        }

        @Override
        public void error(Object o, String s, Object... objects) {
            Log.e(String.valueOf(o), String.format(s, objects));
        }

        @Override
        public void error(Object o, Throwable throwable) {
            Log.e(String.valueOf(o), Log.getStackTraceString(throwable));
        }
    }
}

3. 接口说明

前提:使用 KVO 被观察的属性,必须通过 set 相关的方法类改变属性的值才会被 KVO 自动通知到观察者。其中改变属性的方法可以使用注解指定属性。
注意:KVO 的使用有一些限制,不符合的规范的会在编译期间检查报错,在编译报错时可仔细阅读相关的报错信息,然后检查使用是否规范。

  • @KvoSource(check = true) 修饰需要被观察的属性所在的类,其中参数 check 默认值为 true, 表示会检查 @KvoSource 修饰的类中只允许存在 private 私有的属性, 否则将在编译期间报错,不能编译通过。如果需要非 private 的属性存在,可以设置 check = false, 这样子会跳过非 private 属性的检查。
    另外com.github.drumge:kvo-compiler 会为每一个 @KvoSource 修饰的类的 private 类型的属性生成一个对应的 key, 方便 @KvoBind 或者 @KvoWatch 时指定绑定的属性。生成规则,@KvoSource 类对应生成一个 K_classname 的 interface 并包含对应的 private 属性,比如 @KvoSource public class Example{ private String name;} 会生成 public interface K_Example { String name = "name"; }

  • @KvoIgnore 修饰属性,当使用了 @KvoSource(check=true) 时,类中不能存在非 private 类型的属性 ,除上边设置 check = false 之外,还可以使用 @KvoIgnore 忽略指定的属性非 private 检查。

  • @KvoBind(name = K_Example.name) 修饰方法,绑定改变属性的方法和指定的属性,其中 ‘name = K_Example.name’ name 的值指定绑定的属性。@KvoBind 绑定并不是必须的,如果不使用 @KvoBind, 默认会关联属性的set方法, 其中方法名为 set+属性名(首字母大写), 例如 属性 private String name; 对应默认的绑定的修改属性的方法是 public void setName(String name){}

  • @KvoWatch 修饰方法,被修饰的方法不能为 private 类型。指定观察的属性,当指定的属性的值修改时,KVO 会自动调用 @KvoWatch 的方法达到通知观察者的目的。 @KvoWatch 中有三个属性,name(必须),tag(可选), thread(可选)。 name 为指定需要观察的属性; thread 指定观察者被调用的线程,比如需要 UI 更新时可指定 thread = KvoWatch.Thread.MAINtag 是为了解决需要观察同一个对象但是不同的实例的相同属性,这个时候就需要使用 tag 来区分不同的实例,比如一个页面中需要观察两个用户的信息中的nickname属性变化并更新 UI,用户信息对象 UserInfo,两个用户信息实例分别是 myUserInfo, otherUserInfo, 可以使用 @KvoWatch(name = K_UserInfo.nickname, tag = "my-info", thread = KvoWatch.Thread.MAIN) 来修饰更新 myUserInfo 的昵称的方法, 使用@KvoWatch(name = K_UserInfo.nickname, tag = "other-info", thread = KvoWatch.Thread.MAIN) 来修饰更新 otherUserInfo 的昵称的方法。另外使用 Kvo#bind(Object, S, String) 绑定观察的实例时需要指定 tag。

  • KvoEvent<S, V> @KvoWatch 修饰的方法的参数必须是 KvoEvent<S, V> , 其中 S 是观察的对象类(@KvoSource)的类型, V 为属性的类型,例如

@KvoWatch(name = K_UseInfo.name, tag = "my-info", thread = KvoWatch.Thread.MAIN)
public void onMyInfo(KvoEvent<UseInfo, String> event) { }
  • Kvo
    最后再了解 Kvo 类中的绑定和解绑方法就可以使用 KVO了。 注意:Kvo 必须是使用者自己调用 bind 并 unbind, 不然可能存在内存泄漏。
/**
 * 绑定观察者
 * @param target @KvoWatch 修饰观察者所在实例
 * @param source @KvoSource 修饰的被观察对象的实例
 * @param tag 标识指定观察对象的实例
 * @param notifyWhenBind  绑定时是否通知观察者
 */
public <S> void bind(@NonNull Object target, @NonNull S source, String tag, boolean notifyWhenBind);
/**
  * 解绑观察者
  * @param target @KvoWatch 修饰观察者所在实例
  * @param tag 标识指定观察对象的实例
  */
 public <S> void unbind(@NonNull Object target, @NonNull S source, String tag);
 /**
  * 解绑 target 下的所有观察者
  * @param target @KvoWatch 修饰观察者所在实例
  */
 public void unbindAll(@NonNull Object target)

4. example

可以到 github KVO 项目中的 example module 上看详细 example 使用。 被观察的属性:

@KvoSource(check = true)
public class ExampleSource {
    @KvoIgnore
    public int aa;
    private String example;
    private Integer index;
    private long time;
    private short mShort;
    private byte mByte;
    private int mInt;
    private float mFloat;
    private double mDouble;
    private boolean mBoolean;
    private char sChar;

    public void setExample(String example) {
        this.example = example;
    }

    public void setIndex(Integer index) {
        this.index = index;
    }

    public void setTime(long time) {
        this.time = time;
    }

    public void setMShort(short mShort) {
        this.mShort = mShort;
    }

    public void setMByte(byte mByte) {
        this.mByte = mByte;
    }

    public void setMInt(int mInt) {
        this.mInt = mInt;
    }

    public void setMFloat(float mFloat) {
        this.mFloat = mFloat;
    }

    public void setMDouble(double mDouble) {
        this.mDouble = mDouble;
    }

    @KvoBind(name = K_ExampleSource.mBoolean)
    public void setmBooleanHH(boolean mBoolean) {
        this.mBoolean = mBoolean;
    }

    @KvoBind(name = K_ExampleSource.sChar)
    public void setsCharDif(char sChar) {
        this.sChar = sChar;
    }
}

观察属性

public class ExampleTarget {
    private static String TAG = "ExampleTarget";

    ExampleSource tag1;
    ExampleSource tag2;
    ExampleSource tag3;

    public ExampleTarget() {
        tag1 = new ExampleSource();
        tag2 = new ExampleSource();
        tag3 = new ExampleSource();
    }

    public ExampleSource getTag1() {
        return tag1;
    }

    public ExampleSource getTag2() {
        return tag2;
    }

    public ExampleSource getTag3() {
        return tag3;
    }

    public void bindKvo() {
        Kvo.getInstance().bind(this, tag1, "tag1", false);
        Kvo.getInstance().bind(this, tag2, "tag2");
        Kvo.getInstance().bind(this, tag3);
    }

    public void unbindKvo() {
        Kvo.getInstance().unbind(this, tag1);
        Kvo.getInstance().unbind(this, tag2);
        Kvo.getInstance().unbind(this, tag3);
    }

    public void unbindAll() {
        Kvo.getInstance().unbindAll(this);
    }

    @KvoWatch(name = K_ExampleSource.example, tag = "tag1", thread = KvoWatch.Thread.MAIN)
    public void onUpdateExampleTag1(KvoEvent<ExampleSource, String> event) {
        Log.d(TAG, "onUpdateExampleTag1 oldValue: " + event.getOldValue() + ", newValue: " + event.getNewValue());
    }

    @KvoWatch(name = K_ExampleSource.example, tag = "tag2", thread = KvoWatch.Thread.WORK)
    public void onUpdateExampleTag2(KvoEvent<ExampleSource, String> event) {
        Log.d(TAG, "onUpdateExampleTag2 oldValue: " + event.getOldValue() + ", newValue: " + event.getNewValue());

    }

    @KvoWatch(name = K_ExampleSource.index)
    public void onUpdateIndex(KvoEvent<ExampleSource, Integer> event) {
        Log.d(TAG, "onUpdateIndex oldValue: " + event.getOldValue() + ", newValue: " + event.getNewValue());

    }

    @KvoWatch(name = K_ExampleSource.sChar)
    public void onUpdateChat(KvoEvent<ExampleSource, Character> event) {
        Log.d(TAG, "onUpdateChat oldValue: " + event.getOldValue() + ", newValue: " + event.getNewValue());

    }
}

todo list

  • Kvo 引用改用弱引用,避免使用者绑定之后忘记解绑导致内存泄漏;使用弱引用之后不能使用匿名类,同时检查匿名类的使用,编译期间报错。
  • 绑定指定属性,解绑指定属性
  • @KvoWatch 绑定时通知的顺序
  • 继承关系,支持子类接收父类的@KvoWatch通知
  • 拦截器
  • 泛型,见 AItemViewHolder
  • kotlin中不能直接使用注解器生成的代码,因为 kaptGenerateStubsKotlin 要比 kaptKotlin 早,stub 引用的注解器代码还没生成会导致报错

android-kvo's People

Contributors

drumge avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

android-kvo's Issues

can not found set method bind with field #$jacocoData

add jacoco for android Coverage!

android {
  buildTypes {
        debug {
            testCoverageEnabled  true
        }
  }
}
:app:transformClassesWithJacocoForDebug
:app:transformClassesWithEasyTransformForDebug

But get Exception

java.lang.RuntimeException: com.hezi.demo.SingnalBean can not found set method bind with field #$jacocoData
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at org.codehaus.groovy.reflection.CachedConstructor.invoke(CachedConstructor.java:83)
	at org.codehaus.groovy.runtime.callsite.ConstructorSite$ConstructorSiteNoUnwrapNoCoerce.callConstructor(ConstructorSite.java:105)
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:60)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:235)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:247)
	at com.drumge.kvo.plugin.KvoTransform.checkDefaultMethod(KvoTransform.groovy:353)
	at sun.reflect.GeneratedMethodAccessor962.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
	at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
	at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:384)
	at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1022)
	at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:69)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:182)
	at com.drumge.kvo.plugin.KvoTransform$_parseBindMethod_closure15.doCall(KvoTransform.groovy:303)
	at sun.reflect.GeneratedMethodAccessor960.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
	at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
	at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
	at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1022)
	at groovy.lang.Closure.call(Closure.java:414)
	at groovy.lang.Closure.call(Closure.java:430)
	at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2040)
	at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2025)
	at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2066)
	at org.codehaus.groovy.runtime.dgm$162.invoke(Unknown Source)
	at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:274)
	at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:56)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
	at com.drumge.kvo.plugin.KvoTransform.parseBindMethod(KvoTransform.groovy:294)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite$PogoCachedMethodSiteNoUnwrapNoCoerce.invoke(PogoMetaMethodSite.java:210)
	at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.callCurrent(PogoMetaMethodSite.java:59)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:166)
	at com.drumge.kvo.plugin.KvoTransform.processSource(KvoTransform.groovy:169)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
	at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
	at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:384)
	at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1022)
	at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:69)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:174)
	at com.drumge.kvo.plugin.KvoTransform$_handleSource_closure6.doCall(KvoTransform.groovy:131)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
	at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
	at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
	at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1022)
	at groovy.lang.Closure.call(Closure.java:414)
	at groovy.lang.Closure.call(Closure.java:430)
	at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2040)
	at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2025)
	at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2066)
	at org.codehaus.groovy.runtime.dgm$162.invoke(Unknown Source)
	at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:274)
	at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:56)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
	at com.drumge.kvo.plugin.KvoTransform.handleSource(KvoTransform.groovy:123)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
	at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
	at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:384)
	at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1022)
	at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.callCurrent(PogoMetaClassSite.java:69)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:166)
	at com.drumge.kvo.plugin.KvoTransform$_onAfterJar_closure1.doCall(KvoTransform.groovy:82)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
	at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
	at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
	at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1022)
	at org.codehaus.groovy.runtime.callsite.PogoMetaClassSite.call(PogoMetaClassSite.java:42)
	at org.codehaus.groovy.runtime.callsite.BooleanReturningMethodInvoker.invoke(BooleanReturningMethodInvoker.java:51)
	at org.codehaus.groovy.runtime.callsite.BooleanClosureWrapper.call(BooleanClosureWrapper.java:53)
	at org.codehaus.groovy.runtime.DefaultGroovyMethods.findAll(DefaultGroovyMethods.java:4331)
	at org.codehaus.groovy.runtime.DefaultGroovyMethods.findAll(DefaultGroovyMethods.java:4203)
	at org.codehaus.groovy.runtime.DefaultGroovyMethods.findAll(DefaultGroovyMethods.java:4188)
	at org.codehaus.groovy.runtime.dgm$202.invoke(Unknown Source)
	at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:274)
	at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:56)
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
	at com.drumge.kvo.plugin.KvoTransform.onAfterJar(KvoTransform.groovy:81)
	at com.drumge.easy.plugin.api.IEasyTransform$onAfterJar$5.call(Unknown Source)
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
	at com.drumge.easy.plugin.api.IEasyTransform$onAfterJar$5.call(Unknown Source)
	at com.drumge.easy.plugin.EasyTransform$_doAfterJar_closure10.doCall(EasyTransform.groovy:242)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:93)
	at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:325)
	at org.codehaus.groovy.runtime.metaclass.ClosureMetaClass.invokeMethod(ClosureMetaClass.java:294)
	at groovy.lang.MetaClassImpl.invokeMethod(MetaClassImpl.java:1022)
	at groovy.lang.Closure.call(Closure.java:414)
	at groovy.lang.Closure.call(Closure.java:430)
	at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2040)
	at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2025)
	at org.codehaus.groovy.runtime.DefaultGroovyMethods.each(DefaultGroovyMethods.java:2066)
	at org.codehaus.groovy.runtime.dgm$162.invoke(Unknown Source)
	at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite$PojoMetaMethodSiteNoUnwrapNoCoerce.invoke(PojoMetaMethodSite.java:274)
	at org.codehaus.groovy.runtime.callsite.PojoMetaMethodSite.call(PojoMetaMethodSite.java:56)
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:48)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:113)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
	at com.drumge.easy.plugin.EasyTransform.doAfterJar(EasyTransform.groovy:241)
	at com.drumge.easy.plugin.EasyTransform.doTransform(EasyTransform.groovy:136)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite$PogoCachedMethodSiteNoUnwrapNoCoerce.invoke(PogoMetaMethodSite.java:210)
	at org.codehaus.groovy.runtime.callsite.PogoMetaMethodSite.callCurrent(PogoMetaMethodSite.java:59)
	at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:52)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:154)
	at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:166)
	at com.drumge.easy.plugin.EasyTransform.transform(EasyTransform.groovy:81)
	at com.android.build.gradle.internal.pipeline.TransformTask$2.call(TransformTask.java:222)
	at com.android.build.gradle.internal.pipeline.TransformTask$2.call(TransformTask.java:218)
	at com.android.builder.profile.ThreadRecorder.record(ThreadRecorder.java:102)
	at com.android.build.gradle.internal.pipeline.TransformTask.transform(TransformTask.java:213)
	at sun.reflect.GeneratedMethodAccessor856.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
	at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$IncrementalTaskAction.doExecute(DefaultTaskClassInfoStore.java:173)
	at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:134)
	at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:121)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$1.run(ExecuteActionsTaskExecuter.java:122)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:336)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:328)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:197)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:107)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:111)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:92)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:70)
	at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:63)
	at org.gradle.api.internal.tasks.execution.ResolveTaskOutputCachingStateExecuter.execute(ResolveTaskOutputCachingStateExecuter.java:54)
	at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
	at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:88)
	at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:52)
	at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:52)
	at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54)
	at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
	at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34)
	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker$1.run(DefaultTaskGraphExecuter.java:248)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:336)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:328)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:197)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:107)
	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:241)
	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:230)
	at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.processTask(DefaultTaskPlanExecutor.java:124)
	at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.access$200(DefaultTaskPlanExecutor.java:80)
	at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:105)
	at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker$1.execute(DefaultTaskPlanExecutor.java:99)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.execute(DefaultTaskExecutionPlan.java:625)
	at org.gradle.execution.taskgraph.DefaultTaskExecutionPlan.executeWithTask(DefaultTaskExecutionPlan.java:580)
	at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor$TaskExecutorWorker.run(DefaultTaskPlanExecutor.java:99)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
	at java.lang.Thread.run(Thread.java:745)
:main:transformClassesWithEasyTransformForDebug FAILED

FAILURE: Build failed with an exception.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.