AOP: Aspect-oriented Programming. While OOP is about dividing problems into individual modules, AOP is about unifying the management of a class of problems involving many modules.
Here through a few small examples, explain in Android development, how to use AOP way, global slice management, to achieve simple and elegant, once and for all effect.
1. SingleClickAspect prevents View from being clicked consecutively to start multiple events
Before using AOP, you could write a separate Click class (not elegant) or RxBinding (not concise) :
RxView.clicks(mButton)
.throttleFirst(1, TimeUnit.SECONDS)
.subscribe(new Action1<Void>() {
@Override
public void call(Voidv) { dosomething(); }});Copy the code
Now, everything can be easily solved with a single annotation:
@Aspect
public class SingleClickAspect {
static int TIME_TAG = R.id.click_time;
public static final int MIN_CLICK_DELAY_TIME = 600;// The interval is 600ms
@Pointcut("execution(@com.app.annotation.aspect.SingleClick * *(..) )")// Use SingleClick annotations to find method pointcuts
public void methodAnnotated(a) {}@Around("methodAnnotated()")// Perform method substitution at join points
public void aroundJoinPoint(ProceedingJoinPoint joinPoint) 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);
longlastClickTime = ((tag ! =null)? (long) tag : 0);
LogUtils.showLog("SingleClickAspect"."lastClickTime:" + lastClickTime);
long currentTime = Calendar.getInstance().getTimeInMillis();
if (currentTime - lastClickTime > MIN_CLICK_DELAY_TIME) {// Filter out consecutive clicks within 600 milliseconds
view.setTag(TIME_TAG, currentTime);
LogUtils.showLog("SingleClickAspect"."currentTime:" + currentTime);
joinPoint.proceed(a);// Execute the original method}}}}Copy the code
Usage: annotated on onClick
@SingleClick
public void onClick(View view) {
String comment = mViewBinding.btComment.getText().toString(a);if (TextUtils.isEmpty(comment))
Snackbar.make(mViewBinding.fab, "Comments can't be blank!", Snackbar.LENGTH_LONG).show();
else mPresenter.createComment(comment, mArticle, SpUtil.getUser());
}Copy the code
Or any method that has a view that can be used as a reference (the view can not be onClick view, but only as a reference for the time tag attached object), such as a TRouter page jump, to prevent repeated page jumps:
public class RouterHelper {
@SingleClick // Prevent consecutive clicks
public static void go(String actionName, HashMap data, View view) { TRouter.go(actionName, data, view); }}Copy the code
CheckLoginAspect intercepts permissions for users who are not logged in
The case without AOP, where the user login status needs to be determined within each method body and processed, is now easily resolved with a single annotation:
/** * Created by baixiaokang * check whether the user is logged in to the annotations by using the CheckLogin annotation. Aop slice method is used to create the source code */
@Aspect
public class CheckLoginAspect {
@Pointcut("execution(@com.app.annotation.aspect.CheckLogin * *(..) )")// Method pointcut
public void methodAnnotated(a) {}@Around("methodAnnotated()")// Perform method substitution at join points
public void aroundJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
if (null == SpUtil.getUser()) {
Snackbar.make(App.getAppContext().getCurActivity().getWindow().getDecorView(), "Please log in first!", Snackbar.LENGTH_LONG)
.setAction("Login".new View.OnClickListener() {
@Override
public void onClick(View view) {
TRouter.go(C.LOGIN);
}
}).show();
return;
}
joinPoint.proceed(a);// Execute the original method}}Copy the code
Usage:
public class AdvisePresenter extends AdviseContract.Presenter {
@CheckLogin
public void createMessage(String msg) {
_User user = SpUtil.getUser();
ApiFactory.createMessage(
new Message(ApiUtil.getPointer(
new _User(C.ADMIN_ID)), msg,
ApiUtil.getPointer(user),
user.objectId))
.subscribe(
res -> mView.sendSuc(),
e -> mView.showMsg("Message sending failed!"));
}
@CheckLogin
public void initAdapterPresenter(AdapterPresenter mAdapterPresenter) { mAdapterPresenter .setRepository(ApiFactory::getMessageList) .setParam(C.INCLUDE, C.CREATER) .setParam(C.UID, SpUtil.getUser().objectId) .fetch(); }}Copy the code
From there on, just focus on the main logic.
MemoryCacheAspect memory cache slice
The key cache method returns the same singleton reuse effect as the global cache for our clean Presenter(no argument constructor and no internal state). It also applies to other methods that need to cache results:
/** * Created by baixiaokang on 16/10/24. ** * Created by Baixiaokang on 16/10/24. The return value of a method is cached and fetched directly from the cache the next time the method is executed. * /
@Aspect
public class MemoryCacheAspect {
@Pointcut("execution(@com.app.annotation.aspect.MemoryCache * *(..) )")// Method pointcut
public void methodAnnotated() {
}
@Around("methodAnnotated()")// Perform method substitution at join points
public Object aroundJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
String methodName = methodSignature.getName();
MemoryCacheManager mMemoryCacheManager = MemoryCacheManager.getInstance();
StringBuilder keyBuilder = new StringBuilder();
keyBuilder.append(methodName);
for (Object obj : joinPoint.getArgs()) {
if (obj instanceof String) keyBuilder.append((String) obj);
else if (obj instanceof Class) keyBuilder.append(((Class) obj).getSimpleName());
}
String key = keyBuilder.toString();
Object result = mMemoryCacheManager.get(key);Key rule: method name + parameter 1+ parameter 2+...
LogUtils.showLog("MemoryCache"."The key." + key + "- >"+ (result ! =null ? "not null" : "null"));
if(result ! =null) return result;// Cache already exists, return directly
result = joinPoint.proceed();// Execute the original method
if (result instanceofList && result ! =null && ((List) result).size(a) >0 // The list is not empty
|| result instanceof String && !TextUtils.isEmpty((String) result)// The character is not empty
|| result instanceof Object&& result ! =null)// The object is not empty
mMemoryCacheManager.add(key, result);// Cache
LogUtils.showLog("MemoryCache"."The key." + key + "- >" + "save");
returnresult; }}Copy the code
Look at the Factory generated by Apt:
This class is automatically generated by APT */
public final class InstanceFactory {
/** * @ This method is automatically generated by APT */
@MemoryCache
public static Object create(Class mClass) throws IllegalAccessException, InstantiationException {
switch (mClass.getSimpleName()) {
case "AdvisePresenter": return new AdvisePresenter();
case "ArticlePresenter": return new ArticlePresenter();
case "HomePresenter": return new HomePresenter();
case "LoginPresenter": return new LoginPresenter();
case "UserPresenter": return new UserPresenter();
default: returnmClass.newInstance(); }}}Copy the code
Presenter is the reusable state of the global singleton.
4. Time consuming of TimeLogAspect automatic printing method
It is common to log how long a time-consuming operation took. Without AOP, you would need to add code to the body of each method. Now, a single annotation is all that is needed:
/** * Automatically adds the print method consumption code according to the annotation TimeLog, which is woven into the source code during compilation by aop slicing
@Aspect
public class TimeLogAspect {
@Pointcut("execution(@com.app.annotation.aspect.TimeLog * *(..) )")// Method pointcut
public void methodAnnotated() {
}
@Pointcut("execution(@com.app.annotation.aspect.TimeLog *.new(..) )")Constructor pointcuts
public void constructorAnnotated() {
}
@Around("methodAnnotated() || constructorAnnotated()")// Perform method substitution at join points
public Object aroundJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
LogUtils.showLog("TimeLog getDeclaringClass", methodSignature.getMethod().getDeclaringClass().getCanonicalName());
String className = methodSignature.getDeclaringType().getSimpleName();
String methodName = methodSignature.getName();
long startTime = System.nanoTime();
Object result = joinPoint.proceed();// Execute the original method
StringBuilder keyBuilder = new StringBuilder();
keyBuilder.append(methodName + ":");
for (Object obj : joinPoint.getArgs()) {
if (obj instanceof String) keyBuilder.append((String) obj);
else if (obj instanceof Class) keyBuilder.append(((Class) obj).getSimpleName());
}
String key = keyBuilder.toString();
LogUtils.showLog("TimeLog", (className + "." + key + joinPoint.getArgs().toString() + "-- - >." + "[" + (TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime)) + "ms]"));// Print time difference
returnresult; }}Copy the code
Usage:
@TimeLog
public void onCreate(a) {
super.onCreate();
mApp = this;
SpUtil.init(this);
store = new Stack<>();
registerActivityLifecycleCallbacks(new SwitchBackgroundCallbacks());
}Copy the code
This method takes time to print an annotation and you’re done!
In addition to these simple examples, AOP can also implement dynamic permission requests and other user permission management, including functional and logical slicing, making daily development simpler and more elegant by focusing on the key business logic and leaving the rest to slicing to automate.
For more practical use of AOP, check out the project T-MVP
Or group together to be gay:
QQ group: AndroidMVP 555343041
Update log:
2017/1/8: Use Apt package Retrofit to generate ApiFactory to replace all Repository and delete code
2017/1/7: Use DataBinding to replace all ButterKnife and delete code
2017/1/6: Replace all viewholders with DataBinding, delete code, and move on to a new era
2016/12/30: Use Apt to generate global route TRouter, more elegant page jump, support for passing parameters and shared view transition animation
2016/12/29: Removed BaseMultiVH and added VHClassSelector to support more perfect multi-viewholder
2016/12/28: Use Apt to generate global ApiFactory instead of all models
2016/12/27: Added BaseMultiVH extension to support multiple ViewHolder types
2016/12/26: Removed CoreAdapterPresenter optimized TRecyclerView
Android practical tip: Use generics, write less code
Android AOP combat :APT to create minimalist routing
Global route TRouter, more elegant page jump
Android AOP tutorial :Javassist EventBus
Add OkBus to implement annotation passing events
Android AOP three musketeers :APT,AspectJ,Javassist
>2. Added APT initial chemical plant and replaced dagger2. > < span style = “max-width: 100%; clear: both; min-height: 1em