background

After the previous two articles Android AOP scheme (a) — AspectJ Android AOP scheme (b) — ASM we have a preliminary understanding of Android AOP, but its high threshold and learning cost is still let many people back. Today, here is a fool AOP framework for you, which is probably the most fool AOP framework for Android.

Butcherknife introduction

If it looks familiar at first glance, I’m just warming up to JakeWharton’s butterknife frame, haha. Here is the address of github github.com/LitterSun/b…

Butcherknife use

Butcherknife defines pointcuts in the form of annotations and then code them, similar to Aspectj, but with only five simple annotations as follows:

@Aspect

Indicates that a Class is an Aspect Class, and the Class must be public, as in

@Aspect
public class FragmentInjector {}Copy the code

@BeforeCall

Method call before weaving code, as in

@Aspect
public class FragmentInjector {
private static final String TAG = "FragmentInjector";

@BeforeCall(clazz = FragmentTransaction.class, method = "replace")
public static void beforeCallFragmentReplace(FragmentTransaction transaction, int containerViewId, Fragment fragment) {
    Log.e(TAG, "beforeCallFragmentReplace: transaction = " + transaction + ", containerViewId = " + containerViewId + " ,fragment = "+ fragment); }}Copy the code

The code before weaving

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getFragmentManager().beginTransaction().replace(R.id.fragment, newBlankFragment()).commit(); }}Copy the code

The woven code

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.setContentView(R.layout.activity_main);
        FragmentTransaction transaction = this.getFragmentManager().beginTransaction();
        BlankFragment fragment = new BlankFragment();
        intcontainerViewId = R.id.fragment; FragmentInjector.beforeCallFragmentReplace(transaction, containerViewId, fragment); transaction.replace(containerViewId, fragment).commit(); }}Copy the code

@AfterCall

After the method is called, weave the forecall into the code, as above, @beforecall is not described here

@BeforeSuperExecute

The parent class method before the internal implementation of the weave code, if the child does not rewrite the parent class method, will force the implementation of the method, and the method will only be in the direct child class will only weave once, the child class of the child class is not woven, to prevent multiple calls.

@Aspect
public class FragmentInjector {
    private static final String TAG = "FragmentInjector";

    @BeforeSuperExecute(clazz = Fragment.class, method = "onCreate")
    public static void beforeFragmentCreate(Fragment fragment, Bundle savedInstanceState) {
        Log.e(TAG, "beforeFragmentCreate: fragment = " + fragment + ", savedInstanceState = " + savedInstanceState);
    }

    @AfterSuperExecute(clazz = Fragment.class, method = "onResume")
    @AfterSuperExecute(clazz = DialogFragment.class, method = "onResume")
    @AfterSuperExecute(clazz = ListFragment.class, method = "onResume")
    public static void afterFragmentResume(Fragment fragment) {
        Log.e(TAG, "afterFragmentResume: fragment = "+ fragment); }}Copy the code

The code before weaving

public class BlankFragment extends Fragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState); }}Copy the code

The woven code

public class BlankFragment extends Fragment {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        FragmentInjector.beforeFragmentCreate(this, savedInstanceState);
        super.onCreate(savedInstanceState);
    }
    
    @Override
    public void onResume(a) {
        super.onResume();
        FragmentInjector.afterFragmentResume(this); }}Copy the code

@beforesuperExecute also works with interfaces, as follows, and works with Lambda expressions

@Aspect
public class ClickListenerInjector {
    private static final String TAG = "ClickListenerInjector";

    @BeforeSuperExecute(clazz = View.OnClickListener.class, method = "onClick")
    public static void beforeViewOnClick(View.OnClickListener listener, View view) {
        Log.e(TAG, "beforeViewOnClick: listener = " + listener + ", view = "+ view); }}Copy the code

@AfterSuperExecute

The parent method is woven into code after execution internally, as above @beforeSuperExecute, which is not described here

Annotation method definition

Here are some friends who may have observed the rule defined by the method below the annotation

  1. The method has to bepublic static
  2. The first argument is the pointcut’s this object, followed by arguments to the pointcut method, respectively
  3. Except for this, the argument types are strictly matched, and the order and type must be the same as the pointcut method

Butcherknife integration

Add the plugin to build.gradle in the project root directory

buildscript {
    repositories {
        mavenLocal()
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:x.x.x'
        classpath "Com. Littersun. Butcherknife: butcherknife - gradle - plugin: 1.0.0"}}Copy the code

Then apply the build.gradle plugin in the APP Module

apply plugin: 'com.littersun.butcherknife'
Copy the code

Add annotation dependencies to required Modules

dependencies {
    implementation "Com. Littersun. Butcherknife: butcherknife - annotations: 1.0.0"
}
Copy the code

communication

Butcherknife is positioned as a lightweight AOP framework, perhaps less powerful than AspectJ, that can modify the process of pointcut code execution. But having satisfied most usage scenarios, ButcherKnife, in contrast to AspectJ, handles Lambda expressions perfectly, as well as forcing overrides on superclass methods and weaving them into code. Butcherknife is still at a preliminary stage, do you have any suggestions to communicate and develop together?