Project introduction

A lightweight AOP(Android) application framework that covers the most practical AOP applications. Project address: github.com/xuexiangjys… , if you like, welcome star support!

The design was originally

In our normal development process, we will certainly encounter permission application, thread switch, data cache, exception capture, buried point and method execution time statistics and other problems. These are very common problems and not very difficult to implement, but they are just too cumbersome and can lead to a lot of repetitive, patterned code.

Design ideas

It was Hugo of JakeWharton who first introduced me to the idea of AOP, and reading its source code got me hooked on the dynamic code weaving of AspectJ. I then took a closer look at AspectJ-related technologies, collecting typical AOP application scenarios on Android and implementing them one by one through AspectJ. The result is XAOP, the library.

To solve the pain points

  • Fix quick clicks
  • Solve Android6.0 above dynamic permission application problem
  • Thread free switching issues
  • The log burying point is abnormal
  • Cache issues (disk cache and memory cache)
  • Exception capture processing
  • Business interception (login verification, validity verification, etc.)

The integration guide

Add Gradle dependencies

1. Add build.gradle repositories in the project root directory:

allprojects {
    repositories {
        ...
        maven { url "https://jitpack.io" }
    }
}
Copy the code

Add xAOP to build. Gradle dependencies in the root directory of your project.

Buildscript {... dependencies {... the classpath 'com. Making. Xuexiangjys. XAOP: XAOP - plugin: 1.1.0'}}Copy the code

Add dependencies and references to xAOP plug-ins in your project’s build.gradle

apply plugin: 'com.xuexiang. Xaop '; Use 1.1.0 version and above implementation 'com. Making. Xuexiangjys. XAOP: XAOP - runtime: 1.1.0' / / if it is a support project, Com. Dead simple. Please use the version 1.0.5 implementation 'xuexiangjys. XAOP: XAOP - runtime: 1.0.5'}Copy the code

4. Perform initialization in Application

XAOP.init(this); // Initialize the plug-in xaop.debug (true); // Enable xaop.setpriority (log.info); / / set the level of log print, the default is 0 / / transferring dynamic application permissions section of the application was refused incident response to monitor XAOP. SetOnPermissionDeniedListener (new PermissionUtils.OnPermissionDeniedListener() { @Override public void onDenied(List<String> permissionsDenied) { // Processing when permission application is rejected}}); Xaop.setinterceptor (new Interceptor() {@override public Boolean Intercept (int type, int Interceptor); JoinPoint JoinPoint) throws Throwable {xLogger. d(" Intercepting, intercepting type :" + type); Switch (type) {case 1: // Do what you want to intercept break; case 2: return true; //return true, directly intercepts the slice execution default: break; } return false; }}); / / set up automatic capture exception handler XAOP. SetIThrowableHandler (new IThrowableHandler () {@ Override public Object handleThrowable (String flag, Throwable) {xlogger. d(" exception caught, exception flag:" + flag); if (flag.equals(TRY_CATCH_KEY)) { return 100; } return null; }});Copy the code

Compatible with Kotlin syntax configuration

Add aspectJx to build. Gradle dependencies in the root directory of the project.

Buildscript {... dependencies {... the classpath 'com. Hujiang. Aspectjx: gradle - android - the plugin - aspectjx: 2.0.10'}}Copy the code

2. Add dependencies and references to the AspectJx plug-in in your project’s build.gradle

Aspectjx {include 'project applicationId'}Copy the code

For details, see the Kotlin-test project.

Confuse configuration

-keep @com.xuexiang.xaop.annotation.* class * {*; } -keep @org.aspectj.lang.annotation.* class * {*; } -keep class * { @com.xuexiang.xaop.annotation.* <fields>; @org.aspectj.lang.annotation.* <fields>; } -keepclassmembers class * { @com.xuexiang.xaop.annotation.* <methods>; @org.aspectj.lang.annotation.* <methods>; }Copy the code

Based on using

Quick click slice

  • SingleClickProperty sheet
The property name type The default value note
value long 1000 Quick click interval (ms)

1. Use @Singleclick to mark click methods. Note that the click method must have the click control View as the method parameter, otherwise it will not work.

2. You can set the time interval for quick clicking (unit :ms). The default value is 1000ms.

@singleclick (5000) public void handleOnClick(View v) {xlogger. e(" Click response!" ); Toastutil.get ().toast(" Click response!" ); hello("xuexiangjys", "666666"); }Copy the code

Dynamically apply for permission slices

  • PermissionProperty sheet
The property name type The default value note
value String[] / Set of permissions to apply for

1. Use @permission to mark the method that requires the Permission to execute. You can apply for one or more permissions.

2. When using the @permission annotation, the system automatically determines whether to apply for Permission.

@SingleClick
@Permission({PermissionConsts.CALENDAR, PermissionConsts.CAMERA, PermissionConsts.LOCATION})
private void handleRequestPermission(View v) {

}
Copy the code

Main thread section

1. Use @mainthread to mark the methods that need to be executed in the MainThread.

2. If the @mainthread annotation is used, the MainThread is automatically switched to the MainThread.

@mainThread private void doInMainThread(View v) {mtvHello.settext (" MainThread "); }Copy the code

IO thread slice

  • IOThreadProperty sheet
The property name type The default value note
value ThreadType ThreadType.Fixed The type of the child thread

1. Annotate methods that need to be executed in THE I/O thread with @ioThread. The type of the thread pool can be set to ThreadType. Otherwise, the default type is Fixed.

The types of thread pools are as follows:

  • Single: Single thread pool
  • Fixed: Multi-thread pool
  • Disk: Disk read-write thread pool (essentially single thread pool)
  • Network: Network request thread pool (essentially multithreaded pool)

2. The @ioThread annotation is used to automatically switch to the SPECIFIED TYPE of I/O threads.

@ioThread (threadType.single) private String doInIOThread(View v) {return "thread.currentThread ().getName(); }Copy the code

Log print section

  • DebugLogProperty sheet
The property name type The default value note
priority int 0 Priority of logs

1. Use @debuglog to mark the methods and classes to be printed. You can set the printing priority. If you do not set the printing priority, the default priority is 0. Note: If the printed priority is lower than the priority set by xaop.setPriority, it will not be printed.

2. During the execution of classes and methods using the @debuglog annotation, the method name, parameters, execution time, and results will be printed.

3. Call Xaop. setISerializer to set the serializer for the print serialization parameter object.

4. Xaop.setlogger can be called to set up the implementation interface for printing. By default, logCAT log printing above the 4000 limit is provided.

@DebugLog(priority = Log.ERROR)
private String hello(String name, String cardId) {
    return "hello, " + name + "! Your CardId is " + cardId + ".";
}
Copy the code

Memory cache slice

  • MemoryCacheProperty sheet
The property name type The default value note
value String “” Memory cache key
enableEmpty boolean true Whether the cache is allowed to be null for strings, arrays, collections, etc

1. Annotate methods that require a MemoryCache using @memorycache. Can set the cache key, do not set the default key to the method name (parameters 1 = 1 value | | parameters 2 = 2 value…). Of course, you can also change the auto-generation rules for keys by calling Xaop.seticacheKeyCreator.

2. Annotation methods must return values, otherwise memory cache slicing will not work.

3. The @memoryCache annotation is used to automatically implement the cache policy. The default memory cache used is LruCache.

4. Xaop.initmemorycache can be called to set the maximum number of memory caches. The default is Runtime.geTruntime ().maxMemory() / 1024) / 8

@MemoryCache
private String hello(String name, String cardId) {
    return "hello, " + name + "! Your CardId is " + cardId + ".";
}

Copy the code

Disk cache slice

  • DiskCacheProperty sheet
The property name type The default value note
value String “” Memory cache key
cacheTime long – 1 Cache duration [unit: s]. The default value is permanent
enableEmpty boolean true Whether the cache is allowed to be null for strings, arrays, collections, etc

1. Use @diskcache to mark the method that requires DiskCache. Can set the cache key, do not set the default key to the method name (parameters 1 = 1 value | | parameters 2 = 2 value…). Of course, you can also change the auto-generation rules for keys by calling Xaop.seticacheKeyCreator.

2. You can set the disk cache validity period (unit :s). If this parameter is not set, it takes effect permanently.

3. Annotated methods must return values, otherwise disk cache slicing will not work.

4. The @diskCache annotation is used to automatically implement the cache policy. The default disk cache used is JakeWharton’s DiskLruCache.

5. Xaop.initdiskcache can be called to set the properties of disk cache, including disk serializer IDiskConverter, root directory of disk cache, maximum space of disk cache, etc.

@DiskCache
private String hello(String name, String cardId) {
    return "hello, " + name + "! Your CardId is " + cardId + ".";
}

Copy the code

Automatic capture of abnormal slices

  • SafeProperty sheet
The property name type The default value note
value String “” Flag to catch an exception

1. Annotate the methods that need to be caught with @safe. You can set a Flag to catch exceptions. The default Flag is the name of the current class. The method name.

2. Call XAOP. SetIThrowableHandler set an exception of custom processing, can realize to offset processing of the exception. If not set, only stack information for exceptions is printed.

3. The @safe method is used to automatically capture exceptions and handle exceptions in a unified manner to ensure smooth operation.

@Safe(TRY_CATCH_KEY)
private int getNumber() {
    return 100 / 0;
}
Copy the code

Custom intercept slice

  • InterceptProperty sheet
The property name type The default value note
value int[] / Intercept type

1. Annotate methods and classes that need to be intercepted using @Intercept. You can set to apply for one or more interception types.

2. Custom intercepting slicing does not work if you do not call xaop.setInterceptor to set the interceptor for slicing.

3. Using classes and methods annotated by @Intercept, interceptors set by XAOP will be automatically called at execution time for interception processing. If the interceptor processing returns true, execution of the class or method is blocked and not executed.

4. Use @Intercept to flexibly Intercept slices. For example, the user login permission.

@singleclick (5000) @debuglog (priority = log.error) @Intercept(3) public void handleOnClick(View v) {xlogger. e(" click response!" ); Toastutil.get ().toast(" Click response!" ); hello("xuexiangjys", "666666"); } @debuglog (priority = log.error) @intercept ({1,2,3}) private String hello(String name, String cardId) {return "hello, " + name + "! Your CardId is " + cardId + "."; }Copy the code

[Note] : When there are multiple slice annotations, they are generally executed from top to bottom.


Use the advanced

Login authentication

In the application, for some functions, such as personal center, wallet, collection and other functions that require us to verify login, we can achieve it through @Intercept business Intercept section.

  1. Define the business interception type
Public static final int INTERCEPT_LOGIN = 10;Copy the code
  1. Define interception processing logic
XAOP.setInterceptor(new Interceptor() { @Override public boolean intercept(int type, JoinPoint joinPoint) throws Throwable { switch(type) { case INTERCEPT_LOGIN: if (! Loginactivity.sislogined) {// No loginactivity.sislogined) {ToastUtils. Toast (" Please log in first!" ); ActivityUtils.startActivity(LoginActivity.class); return true; } break; //return true; default: break; } return false; }});Copy the code
  1. Increase where interception is required@Interceptmark
@intercept (INTERCEPT_LOGIN) public void doSomeThing() {ToastUtils. }Copy the code

Q&A

Access problems

Please be sure to read the integration guide carefully before use, as long as you refer to the documentation for each step to access, there will be no problems!

1. Q: My project is the Kotlin project. How do I use it?

A: To configure the Kotlin project, you only need to add aspectJX plug-in on the basis of the original project. For details, please refer to compatible Kotlin Syntax Configuration.

2. Q: Why do I get an error every time I run a buildInvalid byte tag in constant poolAnd it automatically generates oneajcore.xxxxxxxxx.txtFile?

A: there is a good chance that your project is currently running androidx, but you are using the support version of XAOP, causing compilation failures. It is important to note that if your project is support, use version 1.0.5; If your project is running androidx, please use version 1.1.0 or above.

3. Q: Why do I compile everything well, but using either slice doesn’t do anything?

A: There are two possible reasons.

  • 1. The version of XAOP you are using does not match your project version. For example, if your project is on androidx but you are using the support version of XAOP, slicing will not work.
  • 2. You forgot in the projectbuild.gradleAdded a reference to the XAOP plug-in in.
Apply plugin: 'com.xuexiang.xaop' // reference the Xaop pluginCopy the code

Problems with use

Q: Why do I use it@SingleClickAnnotation click method doesn’t work?

A: Any method marked by @singleclick must have a click control View as an argument, otherwise it will not work.

Q: Why do I use it@PermissionAnnotation method, return value invalid?

A: Since the dynamic Permission request is an asynchronous operation, methods annotated by @Permission have no return value.


Supporting facilities

  • Simplified version of Android shell template project
  • Android shell template project

Wechat official account

More information, welcome wechat search public number: “My Android open source journey”