Android AOP框架AspectJ使用詳解
前言
之前了解過(guò)android的AOP框架,用法主要用來(lái)打日志;現(xiàn)在有一個(gè)需求需要函數(shù)在新線程中執(zhí)行,并且函數(shù)主體執(zhí)行完之后,在UI線程返回結(jié)果。想到手寫的話,每次都要new Thread的操作,比較麻煩;因此就嘗試用注解的方法解決這個(gè)問(wèn)題。
AspectJ的使用核心就是它的編譯器,它就做了一件事,將AspectJ的代碼在編譯期插入目標(biāo)程序當(dāng)中,運(yùn)行時(shí)跟在其它地方?jīng)]什么兩樣,因此要使用它最關(guān)鍵的就是使用它的編譯器去編譯代碼ajc。ajc會(huì)構(gòu)建目標(biāo)程序與AspectJ代碼的聯(lián)系,在編譯期將AspectJ代碼插入被切出的PointCut中,已達(dá)到AOP的目的。
因此,無(wú)論在什么IDE上(如果使用命令行就可以直接使用ajc編譯了),問(wèn)題就是讓IDE使用ajc作為編譯器編譯代碼。
代碼實(shí)現(xiàn)
注解使用
代碼主要通過(guò)TraceLog、RunOnNewThread、RunOnNewThreadWithUICallback這三個(gè)注解與AOP容器關(guān)聯(lián)。使用方法如下:
@TraceLog
@RunOnNewThread
public void checkAndRestartDownloadTask(final boolean isAutoCache) {
DownloadManager.getInstance().startService(isAutoCache);
}
@TraceLog
@RunOnNewThreadWithUICallback
public Boolean isShowTipsForFirstVideoCache(DBQueryCallback<Boolean> callback) {
if (!PreferenceClient.is_first_video_cache_done.getBoolean() &&
(DownloadManager.getInstance().getFinishedTaskSize(true, false) > 0 ||
DownloadManager.getInstance().getFinishedTaskSize(true, true) > 0)) {
PreferenceClient.is_first_video_cache_done.setBoolean(true);
return true;
}
return false;
}
checkAndRestartDownloadTask方法,希望方法體在一個(gè)新的線程執(zhí)行并打印方法執(zhí)行的Log;isShowTipsForFirstVideoCache方法,希望方法體在一個(gè)新的線程執(zhí)行,并將函數(shù)的結(jié)果通過(guò)DBQueryCallback這個(gè)回調(diào)回傳給UI線程,同時(shí)打印方法執(zhí)行的Log。
AOP容器識(shí)別這三個(gè)注解,并實(shí)現(xiàn)注解解釋器。
@Aspect
public class TudouDownloadAspect {
public static final String TAG = TudouDownloadAspect.class.getSimpleName();
private static final String THREAD_CALLBACK_POINT_METHOD =
"execution(@com.download.common.aspect.RunOnNewThreadWithUICallback * *(.., com.download.common.callback.DBQueryCallback))";
private static final String THREAD_CALLBACK_POINT_CONSTRUCTOR =
"execution(@com.download.common.aspect.RunOnNewThreadWithUICallback *.new(.., com.download.common.callback.DBQueryCallback))";
private static final String THREAD_POINT_METHOD =
"execution(@com.download.common.aspect.RunOnNewThread * *(..))";
private static final String THREAD_POINT_CONSTRUCTOR =
"execution(@com.download.common.aspect.RunOnNewThread *.new(..))";
private static final String LOG_POINT_METHOD =
"execution(@com.download.common.aspect.TraceLog * *(..))";
private static final String LOG_POINT_CONSTRUCTOR =
"execution(@com.download.common.aspect.TraceLog *.new(..))";
@Pointcut(THREAD_CALLBACK_POINT_METHOD)
public void methodAnnotatedWithThread(){}
@Pointcut(THREAD_CALLBACK_POINT_CONSTRUCTOR)
public void constructorAnnotatedWithThread(){}
@Pointcut(THREAD_POINT_METHOD)
public void methodAnnotatedWithNewThread(){}
@Pointcut(THREAD_POINT_CONSTRUCTOR)
public void constructorAnnotatedWithNewThread(){}
@Pointcut(LOG_POINT_METHOD)
public void methodAnnotatedWithLog(){}
@Pointcut(LOG_POINT_CONSTRUCTOR)
public void constructorAnnotatedWithLog(){}
/**
* @RunOnNewThreadWithUICallback 的注解解釋器
* */
@Around("methodAnnotatedWithThread() || constructorAnnotatedWithThread()")
public Object wrapNewThreadWithCallback(final ProceedingJoinPoint joinPoint) throws Throwable {
Log.v(TAG, "in wrapNewThreadWithCallback");
Object[] objs = joinPoint.getArgs();
final DBQueryCallback callback = (DBQueryCallback) objs[objs.length-1];
new Thread(new Runnable() {
@Override
public void run() {
try {
final Object obj = joinPoint.proceed();
DownloadClient.getInstance().mainHandler.post(new Runnable() {
@Override
public void run() {
if (obj != null)
callback.querySuccess(obj);
else
callback.queryFail();
}
});
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}).start();
return null;
}
/**
* @RunOnNewThread 的注解解釋器
* */
@Around("methodAnnotatedWithNewThread() || constructorAnnotatedWithNewThread()")
public void wrapNewThread(final ProceedingJoinPoint joinPoint) throws Throwable {
Log.v(TAG, "in wrapNewThread");
new Thread(new Runnable() {
@Override
public void run() {
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}).start();
}
/**
* @TraceLog 的注解解釋器
* */
@Before("methodAnnotatedWithLog() || constructorAnnotatedWithLog()")
public void wrapWithLog(JoinPoint joinPoint) throws Throwable {
Log.v(TAG, "before->" + joinPoint.getTarget().toString() + "---" + joinPoint.getSignature().getName());
}
}
- @Aspect:聲明一個(gè)AOP容器
- @Pointcut:聲明一個(gè)切入點(diǎn)
- @Around:將函數(shù)主體包裹起來(lái),在函數(shù)主體前、后插入代碼
- @Before:在函數(shù)主體執(zhí)行之前插入代碼
使用Gradle腳本加載AOP容器
buildscript {
repositories {
mavenLocal()
maven { url "https://jitpack.io" }
}
dependencies {
classpath 'org.aspectj:aspectjtools:1.8.+' //AspectJ腳本依賴
}
}
dependencies {
compile 'org.aspectj:aspectjrt:1.8.+' //AspectJ 代碼依賴
}
//AspectJ AOP容器加載腳本
final def log = project.logger
final def variants = project.android.libraryVariants
variants.all { variant ->
JavaCompile javaCompile = variant.javaCompile
javaCompile.doLast {
String[] args = ["-showWeaveInfo",
"-1.5",
"-inpath", javaCompile.destinationDir.toString(),
"-aspectpath", javaCompile.classpath.asPath,
"-d", javaCompile.destinationDir.toString(),
"-classpath", javaCompile.classpath.asPath,
"-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]
log.debug "ajc args: " + Arrays.toString(args)
MessageHandler handler = new MessageHandler(true);
new Main().run(args, handler);
for (IMessage message : handler.getMessages(null, true)) {
switch (message.getKind()) {
case IMessage.ABORT:
case IMessage.ERROR:
case IMessage.FAIL:
log.error message.message, message.thrown
break;
case IMessage.WARNING:
log.warn message.message, message.thrown
break;
case IMessage.INFO:
log.info message.message, message.thrown
break;
case IMessage.DEBUG:
log.debug message.message, message.thrown
break;
}
}
}
}
備注
@RunOnNewThreadWithUICallback這個(gè)注解的匹配規(guī)則需要函數(shù)的最后一個(gè)參數(shù)為DBQueryCallback(必須要有一個(gè)回調(diào)參數(shù),不然怎么回傳給UI線程~)。函數(shù)的返回值必須和DBQueryCallback的泛型類型一致,因?yàn)樾枰獙⒎祷刂祩魅牖卣{(diào)當(dāng)中;
new Thread(new Runnable() {
@Override
public void run() {
try {
final Object obj = joinPoint.proceed();
DownloadClient.getInstance().mainHandler.post(new Runnable() {
@Override
public void run() {
if (obj != null)
callback.querySuccess(obj);
else
callback.queryFail();
}
});
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}).start();
注意final Object obj = joinPoint.proceed();,執(zhí)行了函數(shù)體以后,我們默認(rèn)取到的是一個(gè)Object類型的返回值,所以不能用基本數(shù)據(jù)類型(bool用Boolean,int用Interger)。還有一點(diǎn),Java中的null是可以轉(zhuǎn)化為任意類型的,所以就算在函數(shù)體直接返回null,執(zhí)行final Object obj = joinPoint.proceed();,這個(gè)類型轉(zhuǎn)化也是不會(huì)有問(wèn)題。親測(cè)有效,可以放心使用
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android實(shí)現(xiàn)用代碼簡(jiǎn)單安裝和卸載APK的方法
這篇文章主要介紹了Android實(shí)現(xiàn)用代碼簡(jiǎn)單安裝和卸載APK的方法,涉及Android針對(duì)APK文件及package的相關(guān)操作技巧,需要的朋友可以參考下2016-08-08
Android?Studio實(shí)現(xiàn)購(gòu)買售賣系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了Android?Studio實(shí)現(xiàn)購(gòu)買售賣系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-02-02
Android實(shí)現(xiàn)圖片自動(dòng)輪播并且支持手勢(shì)左右無(wú)限滑動(dòng)
這篇文章給大家介紹android實(shí)現(xiàn)圖片自動(dòng)輪播并且支持手勢(shì)左右無(wú)限滑動(dòng),代碼簡(jiǎn)單易懂,非常不錯(cuò),具有參考借鑒價(jià)值,感興趣的朋友一起看看吧2016-10-10
Android Broadcast 和 BroadcastReceiver的權(quán)限限制方式
這篇文章主要介紹了Android Broadcast 和 BroadcastReceiver的權(quán)限限制方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03
Android實(shí)現(xiàn)listview滑動(dòng)時(shí)漸隱漸現(xiàn)頂部欄實(shí)例代碼
android中實(shí)現(xiàn)listview滑動(dòng)時(shí)漸隱漸現(xiàn)頂部欄只是在獲取listview的滑動(dòng)距離上可能沒(méi)法直接獲取,需要?jiǎng)討B(tài)的去計(jì)算。感興趣的朋友一起看看吧2016-10-10
Android實(shí)現(xiàn)隱藏狀態(tài)欄和標(biāo)題欄
這篇文章主要介紹了Android實(shí)現(xiàn)隱藏狀態(tài)欄和標(biāo)題欄的相關(guān)資料,需要的朋友可以參考下2015-06-06
Android基礎(chǔ)知識(shí)之tween動(dòng)畫效果
Android基礎(chǔ)知識(shí)之tween動(dòng)畫效果,Android一共提供了兩種動(dòng)畫,這篇文章主要介紹了Android動(dòng)畫效果之tween動(dòng)畫,感興趣的小伙伴們可以參考一下2016-06-06
Android自定義DataGridView數(shù)據(jù)表格控件
這篇文章主要介紹了Android自定義DataGridView數(shù)據(jù)表格控件的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11

