在公司重构的过程中,希望用KVO的方式传递数据变化的事件。然而,在传统Android的写法中,Bean是没有setter的,就没有时机来notifyObservers了。
值得庆幸的是AspectJ能够切入Field access事件,用AOP就是一个非常好的解决方法了。项目,使用方法见README.md,这里只说原理。跪谢大神的Hugo插件
切入
非常基础的AspectJ,就是我这种半吊子写的PointCut和Advice。主要思路就是切入所有:
- Observable子类中
- 没有被标记为IgnoreField的field
- 在被没有标记IgnoreInovker的类或方法中
- 被修改的代码
分发
上面的切入有个大bug,对同一个对象的连续修改会触发多次无用的notifyObservers。为了解决这个问题,采用两个方法:
- 对于有Looper的线程,所有行为都是以代码段(Message)的方式放置到queue中串行执行的,那么可以将一个代码段(Message)中的所有修改集中去重,并在queue的下一个Message统一分发。这个方法无感的解决掉了大部分(因为主线程是LooperThread)修改的去重
- 对于非Looper线程,需要给修改的方法标记为IgnoreInvoker,并在修改完成后手动调用Dispatchers#notifyDataChanged。这个并不优雅,但是我并没有找到AspectJ这么复杂的PointCut声明方式。目测可以用反射和JointPoint中的内容解决问题,但是对于尽可能轻量化的切面会过重