Skip to content

Latest commit

 

History

History
340 lines (237 loc) · 10.2 KB

README.md

File metadata and controls

340 lines (237 loc) · 10.2 KB

CrepeCake

A compile-time aop engine like AspectJ but easier to use in android application development.

GitHub license Android Weekly Recommend

Wiki


First At A Glance : ]

Here is am example that injected the onCreate() Method in MainActivity:

@Aspect(MainActivity.class)
public class MainActivityAspect {

    protected void onCreate(InvocationHandler invocationHandler, Bundle savedInstanceState) {
      System.out.println("⇢ onCreate");
      long startTime = System.currentTimeMillis();
      invocationHandler.invoke(savedInstanceState);
      System.out.println(String.format("⇠ onCreate [%dms]", System.currentTimeMillis() - startTime));    
    }

}

That's all,these code will be executed during the onCreate method in MainActivity with printing the running time of it:

I/System.out: ⇢ onCreate
I/System.out: ⇠ onCreate [33ms]

Installation

  1. Add the buildscript dependencies in the root project:
buildscript {

  repositories {
      google()
      jcenter()
  }
  dependencies {
      classpath 'com.android.tools.build:gradle:3.0.1'
      classpath 'net.idik.crepecake:plugin:0.0.4' // Add Here
  }

}
  1. Add crepecake plugin in the target module

The crepecake plugin MUST AFTER(VERY IMPORTANT) the application plugin

// Apply application or library plugin here...
apply plugin: 'net.idik.crepecake'

Usage

  1. Programme the injection processor on the basis of target class, with the annotation @Aspect
@Aspect(MainActivity.class)
public class MainActivityAspect {
  // injection methods...
}
  1. Programme the injection method

    Step 1. Declare the injection method with the target method infos, including the access level, static or not, name of method, parameters.

    Step 2. Insert InvocationHandler invocationHandler at the 1st position of the parameter list

    Done.

    @Aspect(MainActivity.class)
    public class MainActivityAspect {
    
        protected void onCreate(InvocationHandler invocationHandler, Bundle savedInstanceState) {
          System.out.println("⇢ onCreate");
          long startTime = System.currentTimeMillis();
          invocationHandler.invoke(savedInstanceState);
          System.out.println(String.format("⇠ onCreate [%dms]", System.currentTimeMillis() - startTime)); 
        }
    
        public android.support.v7.app.ActionBar getSupportActionBar(InvocationHandler invocationHandler) {
            ActionBar bar = (ActionBar) invocationHandler.invoke();
            //do stuff...
            return bar;
        }
    
        //other injection methods...
    }

So far, we have completed the job that injects the onCreate(Bundle savedInstanceState)getSupportActionBar() and other methods in the MainActivity.

InvocationHandler is a flag indicating this is an injection method. Beside, we could invoke the original method through it and obtain the result(if return type is not void).

AspectConfig

We could customize the injection points by inheriting the AspectConfig class

public class OnClickAspectConfig extends AspectConfig {
    @Override
    protected boolean isEnable() {
        return super.isEnable();
    }

    @Override
    public boolean isHook(Class clazz) {
        return View.OnLongClickListener.class.isAssignableFrom(clazz) || View.OnClickListener.class.isAssignableFrom(clazz);
    }
}

The above codes means that we will inject ALL the subClass of OnLongClickListener and OnClickListener.

⚠️ Attention: The AspectConfig class will be executed during the compile-time, so do not make any running-time logic within it.

Then, use it to the injection processor with the annotation @Aspect

@Aspect(OnClickAspectConfig.class)
public class OnClickListenerAspect {
  public void onClick(InvocationHandler invocationHandler, View view) {
      System.out.println("OnClick: " + view);
      invocationHandler.invoke(view);
  }

  public boolean onLongClick(InvocationHandler invocationHandler, View view) {
      boolean isConsume = (boolean) invocationHandler.invoke(view);
      if (isConsume) {
          System.out.println("OnLongClick: " + view);
      }
      return isConsume;
  }
}

See more...

Great Thanks to...


先撇一眼 : ]

举个例子注入MainActivity的onCreate方法:

@Aspect(MainActivity.class)
public class MainActivityAspect {

    protected void onCreate(InvocationHandler invocationHandler, Bundle savedInstanceState) {
      System.out.println("⇢ onCreate");
      long startTime = System.currentTimeMillis();
      invocationHandler.invoke(savedInstanceState);
      System.out.println(String.format("⇠ onCreate [%dms]", System.currentTimeMillis() - startTime));    
    }

}

这就完成了对MainActivity的注入,以上代码会输出onCreate的运行时间:

I/System.out: ⇢ onCreate
I/System.out: ⇠ onCreate [33ms]

安装

  1. 在root project的buildscript中添加dependencies如下:
buildscript {

  repositories {
      google()
      jcenter()
  }
  dependencies {
      classpath 'com.android.tools.build:gradle:3.0.1'
      classpath 'net.idik.crepecake:plugin:0.0.4' //添加在此
  }

}
  1. 在目标模块的build.gradle文件中添加插件(请务必添加于Application插件后)如下:
// apply application or library plugin here
apply plugin: 'net.idik.crepecake'

用法

  1. 根据目标类,编写注入处理器,通过@Aspect指定目标类
@Aspect(MainActivity.class)
public class MainActivityAspect {
  // 注入方法们...
}
  1. 编写注入方法,步骤如下

    Step 1. 声明注入方法与目标方法声明一致,包括访问属性、静态、方法命名、参数列表等
    Step 2. 添加InvocationHandler invocationHandler至参数列表第一位
    Done.

    @Aspect(MainActivity.class)
    public class MainActivityAspect {
    
        protected void onCreate(InvocationHandler invocationHandler, Bundle savedInstanceState) {
          System.out.println("⇢ onCreate");
          long startTime = System.currentTimeMillis();
          invocationHandler.invoke(savedInstanceState);
          System.out.println(String.format("⇠ onCreate [%dms]", System.currentTimeMillis() - startTime));
        }
    
        public android.support.v7.app.ActionBar getSupportActionBar(InvocationHandler invocationHandler) {
            ActionBar bar = (ActionBar) invocationHandler.invoke();
            //do stuff...
            return bar;
        }
    
        //other inject methods...
    }

至此,我们已经完成了对MainActivity的onCreate(Bundle savedInstanceState)getSupportActionBar()以及其他方法的切面注入。

InvocationHandler这个参数很重要,这个参数标志着这是一个注入方法,并且通过这个参数,我们可以调用目标方法,并获取返回值(如果非void

AspectConfig

通过继承AspectConfig对象可以对注入点进行个性化的定制,如下:

public class OnClickAspectConfig extends AspectConfig {
    @Override
    protected boolean isEnable() {
        return super.isEnable();
    }

    @Override
    public boolean isHook(Class clazz) {
        return View.OnLongClickListener.class.isAssignableFrom(clazz) || View.OnClickListener.class.isAssignableFrom(clazz);
    }
}

其中,isHook方法判断该类是否为目标类,在上述代码中指定了OnLongClickListenerOnClickListener所有子类都为注入目标,我们可以通过该方法进行定制。

⚠️ 注意:这个类将会在编译时被调用,请不要在此类中做运行时动态逻辑。

写完配置类后,在注入类上通过@Aspect注解应用即可

@Aspect(OnClickAspectConfig.class)
public class OnClickListenerAspect {
  public void onClick(InvocationHandler invocationHandler, View view) {
      System.out.println("OnClick: " + view);
      invocationHandler.invoke(view);
  }

  public boolean onLongClick(InvocationHandler invocationHandler, View view) {
      boolean isConsume = (boolean) invocationHandler.invoke(view);
      if (isConsume) {
          System.out.println("OnLongClick: " + view);
      }
      return isConsume;
  }
}

查看更多...

万分感谢


License

Copyright 2017 认真的帅斌

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.