请ç¹èµå ³æ³¨ï¼ä½ çæ¯æå¯¹ææä¹é大ã
ð¥ Hiï¼ææ¯å°å½ãæ¬æå·²æ¶å½å° GitHub · AndroidFamily ä¸ãè¿éæ Android è¿é¶æé¿ç¥è¯ä½ç³»ï¼æå¿åéåçæåï¼å ³æ³¨å ¬ä¼å· [彿é] å¸¦ä½ å»ºç«æ ¸å¿ç«äºåã
åè¨
- å¨
Androidå¼åä¸ï¼éå¶æé®å¿«éç¹å»ï¼æé®é²æï¼æ¯ä¸ä¸ªå¸¸è§çéæ±ï¼ - å¨è¿ç¯æç« éï¼æå°ä»ç»ä¸ç§ä½¿ç¨
AspectJçæ¹æ³ï¼åºäºæ³¨è§£å¤çå¨ & è¿è¡æ¶æ³¨è§£åå°çåçã妿è½å¸®ä¸å¿ï¼è¯·å¡å¿ ç¹èµå å ³æ³¨ï¼è¿çç对æé常éè¦ã
ç³»åæç«
- ãAndroid | ä¸æå¸¦ä½ å ¨é¢äºè§£ AspectJ æ¡æ¶ã
- ãAndroid | ä½¿ç¨ AspectJ éå¶æé®å¿«éç¹å»ã
延伸æç«
- å ³äº åå°ï¼è¯·é 读ï¼ãJava | åå°ï¼å¨è¿è¡æ¶è®¿é®ç±»åä¿¡æ¯ï¼å« Kotlinï¼ã
- å ³äº æ³¨è§£ï¼è¯·é 读ï¼ãJava | è¿æ¯ä¸ç¯å ¨é¢çæ³¨è§£ä½¿ç¨æ»ç¥ï¼å« Kotlinï¼ã
- å ³äº æ³¨è§£å¤çå¨ï¼APTï¼ï¼è¯·é 读ï¼ãJava | 注解å¤çå¨ï¼APTï¼åçè§£æ & å®è·µã
ç®å½
1. å®ä¹éæ±
å¨å¼å§è®²è§£ä¹åï¼æä»¬å å®ä¹éæ±ï¼å ·ä½æè¿°å¦ä¸ï¼
- éå¶å¿«éç¹å»éæ± ç¤ºæå¾ï¼
2. 常è§å¤çæ¹æ³
ç®åæ¯è¾å¸¸è§çéå¶å¿«éç¹å»çå¤çæ¹æ³æä»¥ä¸ä¸¤ç§ï¼å ·ä½å¦ä¸ï¼
2.1 å°è£ 代çç±»
å°è£
ä¸ä¸ªä»£çç±»å¤çç¹å»äºä»¶ï¼ä»£çç±»éè¿å¤æç¹å»é´éå³å®æ¯å¦æ¦æªç¹å»äºä»¶ï¼å
·ä½ä»£ç å¦ä¸ï¼
// 代çç±»
public abstract class FastClickListener implements View.OnClickListener {
private long mLastClickTime;
private long interval = 1000L;
public FastClickListener() {
}
public FastClickListener(long interval) {
this.interval = interval;
}
@Override
public void onClick(View v) {
long currentTime = System.currentTimeMillis();
if (currentTime - mLastClickTime > interval) {
// ç»è¿äºè¶³å¤é¿çæ¶é´ï¼å
许ç¹å»
onClick();
mLastClickTime = nowTime;
}
}
protected abstract void onClick();
}
å¨éè¦éå¶å¿«éç¹å»çå°æ¹ä½¿ç¨è¯¥ä»£çç±»ï¼å ·ä½å¦ä¸ï¼
tv.setOnClickListener(new FastClickListener() {
@Override
protected void onClick() {
// å¤çç¹å»é»è¾
}
});
2.2 RxAndroid è¿æ»¤è¡¨è¾¾å¼
使ç¨RxJavaçè¿æ»¤è¡¨è¾¾å¼throttleFirstä¹å¯ä»¥éå¶å¿«éç¹å»ï¼å
·ä½å¦ä¸ï¼
RxView.clicks(view)
.throttleFirst(1, TimeUnit.SECONDS)
.subscribe(new Consumer<Object>() {
@Override
public void accept(Object o) throws Exception {
// å¤çç¹å»é»è¾
}
});
2.3 å°ç»
代çç±»åRxAndroidè¿æ»¤è¡¨è¾¾å¼è¿ä¸¤ç§å¤çæ¹æ³é½åå¨ä¸¤ä¸ªç¼ºç¹ï¼
- 1. ä¾µå ¥æ ¸å¿ä¸å¡é»è¾ï¼éè¦å°ä»£ç æ¿æ¢å°éè¦éå¶ç¹å»çå°æ¹ï¼
- 2. ä¿®æ¹å·¥ä½éå¤§ï¼æ¯ä¸ä¸ªå¢å éå¶ç¹å»çå°æ¹é½è¦ä¿®æ¹ä»£ç ã
æä»¬éè¦ä¸ç§æ¹æ¡è½å¤è§é¿è¿ä¸¤ä¸ªç¼ºç¹ ââ AspectJã AspectJæ¯ä¸ä¸ªæµè¡çJava AOPï¼aspect-oriented programmingï¼ç¼ç¨æ©å±æ¡æ¶ï¼è¥è¿ä¸äºè§£ï¼è¯·å¡å¿
æ¥çæç« ï¼ãAndroid | ä¸æå¸¦ä½ å
¨é¢äºè§£ AspectJ æ¡æ¶ã
3. è¯¦ç»æ¥éª¤
å¨ä¸é¢çå
容éï¼æä»¬å°ä½¿ç¨AspectJæ¡æ¶ï¼æéå¶å¿«éç¹å»çé»è¾ä½ä¸ºæ ¸å¿å
³æ³¨ç¹ä»ä¸å¡é»è¾ä¸æ½ç¦»åºæ¥ï¼åç¬ç»´æ¤ãå
·ä½æ¥éª¤å¦ä¸ï¼
æ¥éª¤1ï¼æ·»å AspectJä¾èµ
-
- ä¾èµæ²ªæ±ç
AspectJXGradleæä»¶ ââ å¨é¡¹ç®build.gradle䏿·»å æä»¶ä¾èµï¼
- ä¾èµæ²ªæ±ç
// 项ç®çº§build.gradle
dependencies {
classpath 'com.android.tools.build:gradle:3.5.3'
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.8'
}
妿æä»¶ä¸è½½éåº¦è¿æ
¢ï¼å¯ä»¥ç´æ¥ä¾èµæä»¶ jaræä»¶ï¼å°æä»¶ä¸è½½å°é¡¹ç®æ ¹ç®å½ï¼å¦/pluginsï¼,ç¶åå¨é¡¹ç®build.gradle䏿·»å æä»¶ä¾èµï¼
// 项ç®çº§build.gradle
dependencies {
classpath 'com.android.tools.build:gradle:3.5.3'
classpath fileTree(dir:'plugins', include:['*.jar'])
}
-
- åºç¨æä»¶ ââ å¨
App Moduleçbuild.gradleä¸åºç¨æä»¶ï¼
- åºç¨æä»¶ ââ å¨
// App Moduleçbuild.gradle
apply plugin: 'android-aspectjx'
...
-
- ä¾èµAspectJæ¡æ¶ ââ å¨å
å«
AspectJ代ç çModuleçbuild.gradleæä»¶ä¸æ·»å ä¾èµï¼
- ä¾èµAspectJæ¡æ¶ ââ å¨å
å«
// Module级build.gradle
dependencies {
...
api 'org.aspectj:aspectjrt:1.8.9'
...
}
æ¥éª¤2ï¼å®ç°å¤æå¿«éç¹å»çå·¥å ·ç±»
- æä»¬å
å®ç°ä¸ä¸ªå¤æ
Viewæ¯å¦å¿«éç¹å»çå·¥å ·ç±»ï¼ - å®ç°åçæ¯ä½¿ç¨
Viewçtag屿§å卿è¿ä¸æ¬¡çç¹å»æ¶é´ï¼æ¯æ¬¡ç¹å»æ¶å¤æå½åæ¶é´è·ç¦»åå¨çæ¶é´æ¯å¦å·²ç»ç»è¿äºè¶³å¤é¿çæ¶é´ï¼ - 为äºé¿å
è°ç¨
View#setTag(int key,Object tag)æ¶ä¼ å ¥çkeyä¸å ¶ä»å°æ¹ä¼ å ¥çkeyå²çªèé æè¦çï¼å¡å¿ 使ç¨å¨èµæºæä»¶ä¸å®ä¹ç idï¼èµæºæä»¶ä¸ç id è½å¤ææä¿è¯å ¨å±å¯ä¸æ§ï¼å ·ä½å¦ä¸ï¼
// ids.xml
<resources>
<item type="id" name="view_click_time" />
</resources>
public class FastClickCheckUtil {
/**
* 夿æ¯å¦å±äºå¿«éç¹å»
*
* @param view ç¹å»çView
* @param interval å¿«éç¹å»çéå¼
* @return trueï¼å¿«éç¹å»
*/
public static boolean isFastClick(@NonNull View view, long interval) {
int key = R.id.view_click_time;
// æè¿çç¹å»æ¶é´
long currentClickTime = System.currentTimeMillis();
if(null == view.getTag(key)){
// 1. ç¬¬ä¸æ¬¡ç¹å»
// ä¿åæè¿ç¹å»æ¶é´
view.setTag(key, currentClickTime);
return false;
}
// 2. éç¬¬ä¸æ¬¡ç¹å»
// 䏿¬¡ç¹å»æ¶é´
long lastClickTime = (long) view.getTag(key);
if(currentClickTime - lastClickTime < interval){
// æªè¶
è¿æ¶é´é´éï¼è§ä¸ºå¿«éç¹å»
return true;
}else{
// ä¿åæè¿ç¹å»æ¶é´
view.setTag(key, currentClickTime);
return false;
}
}
}
æ¥éª¤3ï¼å®ä¹Aspectåé¢
使ç¨@Aspect注解å®ä¹ä¸ä¸ªåé¢ï¼ä½¿ç¨è¯¥æ³¨è§£ä¿®é¥°çç±»ä¼è¢«AspectJç¼è¯å¨è¯å«ä¸ºåé¢ç±»ï¼
@Aspect
public class FastClickCheckerAspect {
// éåå¡«å
}
æ¥éª¤4ï¼å®ä¹PointCutåå
¥ç¹
使ç¨@Pointcut注解å®ä¹ä¸ä¸ªåå
¥ç¹ï¼ç¼è¯æAspectJç¼è¯å¨å°æç´¢ææå¹é
çJoinPointï¼æ§è¡ç»å
¥ï¼
@Aspect
public class FastClickAspect {
// å®ä¹ä¸ä¸ªåå
¥ç¹ï¼View.OnClickListener#onClick()æ¹æ³
@Pointcut("execution(void android.view.View.OnClickListener.onClick(..))")
public void methodViewOnClick() {
}
// éåå¡«å
Advice
}
æ¥éª¤5ï¼å®ä¹Adviceå¢å¼º
å¢å¼ºçæ¹å¼æå¾å¤ç§ï¼å¨è¿éæä»¬ä½¿ç¨@Around注解å®ä¹ç¯ç»å¢å¼ºï¼å®å°å
è£
PointCutï¼å¨PointCutååå¢å 横åé»è¾ï¼å
·ä½å¦ä¸ï¼
@Aspect
public class FastClickAspect {
// å®ä¹åå
¥ç¹ï¼View.OnClickListener#onClick()æ¹æ³
@Pointcut("execution(void android.view.View.OnClickListener.onClick(..))")
public void methodViewOnClick() {}
// å®ä¹ç¯ç»å¢å¼ºï¼å
è£
methodViewOnClick()åå
¥ç¹
@Around("methodViewOnClick()")
public void aroundViewOnClick(ProceedingJoinPoint joinPoint) throws Throwable {
// ååºç®æ 对象
View target = (View) joinPoint.getArgs()[0];
// æ ¹æ®ç¹å»é´éæ¯å¦è¶
è¿2000ï¼å¤ææ¯å¦ä¸ºå¿«éç¹å»
if (!FastClickCheckUtil.isFastClick(target, 2000)) {
joinPoint.proceed();
}
}
}
æ¥éª¤6ï¼å®ç°View.OnClickListener
å¨è¿ä¸æ¥æä»¬ä¸ºView设置OnClickListenerï¼å¯ä»¥çå°æä»¬å¹¶æ²¡ææ·»å éå¶å¿«éç¹å»çç¸å
³ä»£ç ï¼å¢å¼ºçé»è¾å¯¹åæé»è¾æ²¡æä¾µå
¥ï¼å
·ä½ä»£ç å¦ä¸ï¼
// æºç ï¼
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.text).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i("AspectJ","click");
}
});
}
}
ç¼è¯ä»£ç ï¼éååç¼è¯AspectJç¼è¯å¨æ§è¡ç»å
¥åç.classæä»¶ãè¿ä¸äºè§£å¦ä½æ¥æ¾ç¼è¯åç.classæä»¶ï¼è¯·å¡å¿
æ¥çæç« ï¼ãAndroid | ä¸æå¸¦ä½ å
¨é¢äºè§£ AspectJ æ¡æ¶ã
public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(2131361820);
findViewById(2131165349).setOnClickListener(new View.OnClickListener() {
private static final JoinPoint.StaticPart ajc$tjp_0;
// View.OnClickListener#onClick()
public void onClick(View v) {
View view = v;
// éæJoinPointï¼æ§è¡ç¯ç»å¢å¼ºï¼ä¹æ§è¡@Aroundä¿®é¥°çæ¹æ³
JoinPoint joinPoint = Factory.makeJP(ajc$tjp_0, this, this, view);
onClick_aroundBody1$advice(this, view, joinPoint, FastClickAspect.aspectOf(), (ProceedingJoinPoint)joinPoint);
}
static {
ajc$preClinit();
}
private static void ajc$preClinit() {
Factory factory = new Factory("MainActivity.java", null.class);
ajc$tjp_0 = factory.makeSJP("method-execution", (Signature)factory.makeMethodSig("1", "onClick", "com.have.a.good.time.aspectj.MainActivity$1", "android.view.View", "v", "", "void"), 25);
}
// 忥å¨View.OnClickListener#onClick()ä¸ç代ç ï¼ç¸å½äºæ ¸å¿ä¸å¡é»è¾
private static final void onClick_aroundBody0(null ajc$this, View v, JoinPoint param1JoinPoint) {
Log.i("AspectJ", "click");
}
// @Aroundæ¹æ³ä¸ç代ç ï¼å³æºç ä¸çaroundViewOnClick()ï¼ç¸å½äºAdvice
private static final void onClick_aroundBody1$advice(null ajc$this, View v, JoinPoint thisJoinPoint, FastClickAspect ajc$aspectInstance, ProceedingJoinPoint joinPoint) {
View target = (View)joinPoint.getArgs()[0];
if (!FastClickCheckUtil.isFastClick(target, 2000)) {
// éå¿«éç¹å»ï¼æ§è¡ç¹å»é»è¾
ProceedingJoinPoint proceedingJoinPoint = joinPoint;
onClick_aroundBody0(ajc$this, v, (JoinPoint)proceedingJoinPoint);
null;
}
}
});
}
}
å°ç»
å°è¿éï¼æä»¬å°±è®²è§£å®ä½¿ç¨AspectJæ¡æ¶éå¶æé®å¿«éç¹å»ç详ç»ï¼æ»ç»å¦ä¸ï¼
- 使ç¨
@Aspect注解æè¿°ä¸ä¸ªåé¢ï¼ä½¿ç¨è¯¥æ³¨è§£ä¿®é¥°çç±»ä¼è¢«AspectJç¼è¯å¨è¯å«ä¸ºåé¢ç±»ï¼ - 使ç¨
@Pointcut注解å®ä¹ä¸ä¸ªåå ¥ç¹ï¼ç¼è¯æAspectJç¼è¯å¨å°æç´¢ææå¹é çJoinPointï¼æ§è¡ç»å ¥ï¼ - 使ç¨
@Around注解å®ä¹ä¸ä¸ªå¢å¼ºï¼å¢å¼ºä¼è¢«ç»å ¥å¹é çJoinPoint
4. æ¼è¿
ç°å¨ï¼æä»¬åå½æç« å¼å¤´å®ä¹çéæ±ï¼æ»å ±æ4ç¹ãå ¶ä¸å两ç¹ä½¿ç¨ç®åçæ¹æ¡ä¸å·²ç»è½å¤å®ç°ï¼ç°å¨æä»¬å ³æ³¨åé¢ä¸¤ç¹ï¼å³å 许å®å¶æ¶é´é´éä¸è¦çå°½å¯è½å¤çç¹å»åºæ¯ã
- éæ±åå½ ç¤ºæå¾ï¼
4.1 å®å¶æ¶é´é´é
å¨å®é 项ç®ä¸ååºæ¯ä¸çæé®ï¼å¾å¾éè¦éå¶ä¸åçç¹å»æ¶é´é´éï¼å æ¤æä»¬éè¦æä¸ç§ç®ä¾¿çæ¹å¼ç¨äºå®å¶ä¸ååºæ¯çæ¶é´é´éï¼æè 对äºä¸äºä¸éè¦éå¶å¿«éç¹å»çå°æ¹ï¼æåæ³è·³è¿å¿«éç¹å»å¤æï¼å ·ä½æ¹æ³å¦ä¸ï¼
- å®ä¹æ³¨è§£
/**
* å¨éè¦å®å¶æ¶é´é´éå°æ¹æ·»å @FastClick注解
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface FastClick {
long interval() default FastClickAspect.FAST_CLICK_INTERVAL_GLOBAL;
}
- ä¿®æ¹åé¢ç±»ç
Advice
@Aspect
public class SingleClickAspect {
public static final long FAST_CLICK_INTERVAL_GLOBAL = 1000L;
@Pointcut("execution(void android.view.View.OnClickListener.onClick(..))")
public void methodViewOnClick() {}
@Around("methodViewOnClick()")
public void aroundViewOnClick(ProceedingJoinPoint joinPoint) throws Throwable {
// ååºJoinPointçç¾å
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
// ååºJoinPointçæ¹æ³
Method method = methodSignature.getMethod();
// 1. å
¨å±ç»ä¸çæ¶é´é´é
long interval = FAST_CLICK_INTERVAL_GLOBAL;
if (method.isAnnotationPresent(FastClick.class)) {
// 2. å¦ææ¹æ³ä½¿ç¨äº@FastClick修饰ï¼ååºå®å¶çæ¶é´é´é
FastClick singleClick = method.getAnnotation(FastClick.class);
interval = singleClick.interval();
}
// ååºç®æ 对象
View target = (View) joinPoint.getArgs()[0];
// 3. æ ¹æ®ç¹å»é´éæ¯å¦è¶
è¿intervalï¼å¤ææ¯å¦ä¸ºå¿«éç¹å»
if (!FastClickCheckUtil.isFastClick(target, interval)) {
joinPoint.proceed();
}
}
}
- ä½¿ç¨æ³¨è§£
findViewById(R.id.text).setOnClickListener(new View.OnClickListener() {
@FastClick(interval = 5000L)
@Override
public void onClick(View v) {
Log.i("AspectJ","click");
}
});
4.2 宿´åºæ¯è¦ç
ButterKnife @OnClick android:onClick OK RecyclerView / ListView Java Lambda NO Kotlin Lambda OK DataBinding OK
Editting...
ææ¯å°å½ï¼å¸¦ä½ æå»º Android ç¥è¯ä½ç³»ãææ¯åèåºé®é¢ï¼è¯·å ³æ³¨å ¬ä¼å· [彿é]ç§ä¿¡ææé®ã