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
- The method has to be
public static
- The first argument is the pointcut’s this object, followed by arguments to the pointcut method, respectively
- 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?