AOP之Javassist应用于自动实现EventBus解读(三)

解读demo:https://github.com/north2016/T-MVP

一.前言

javassist是一个操作class文件即class字节码的动态类库;在打包过程中,用来检查、”动态”修改以及创建 Java类。其功能与jdk自带的反射功能类似,但比反射功能更强大。

为了方便看,我将javassist的demo剥离出来

二.原理

gradle从1.5开始,gradle插件包含了一个叫Transform的API,这个API允许第三方插件在class文件转为为dex文件前操作编译好的class文件,这个API的目标是简化自定义类操作,而不必处理Task,并且在操作上提供更大的灵活性。并且可以更加灵活地进行操作。 
官方文档:http://google.github.io/android-gradle-dsl/javadoc/ 

demo的原理也是基于此来完成的。即java代码编译后生成class文件,当要打包.class-->.dex时,Javassit库操作.class文件,读取所有的文件的注解,根据注解将EventBus的注解与反注解以及触发事件的方法生成到当前的.class文件中。

三.核心类

ClassPool:javassist的类池,使用ClassPool 类可以跟踪和控制所操作的类,它的工作方式与 JVM 类装载器非常相似, 
CtClass: 提供了检查类数据(如字段和方法)以及在类中添加新字段、方法和构造函数、以及改变类、父类和接口的方法。
CtField:用来访问域 (属性)
CtMethod :用来访问方法 

CtConstructor:用来访问构造器

不过,Javassist 并未提供删除类中字段、方法或者构造函数的任何方法。 

注意限制与局限性:当调用ctClass.toClass()时,修改后的类将被当前的ClassLoader加载并实例化。 在调用ctClass.toClass()时,会加载此类,如果此类在之前已经被加载过,则会报一个duplicate load的错误,表示不能重复加载一个类。所以,修改方法的实现必须在修改的类加载之前进行。不能访问块之外的局部变量。如果在一个方法的开始和结尾都增加了代码段,那么在方法的结尾块中无法访问方法开始中的代码段中的变量(不太完美的解决方法是将原方法改名,然后再增加与原方法同名的方法)。

CtClass.getDeclaredMethods()获取自己申明的方法,CtClass.getMethods()会把所有父类的方法都加上

四.代码详解

注意:修改插件中的代码一定要重新发布,发布之前可先将之前的插件删除,将build删除,重新运行./gradlew -p plugin clean build uploadArchives --info

1.transform插件

1.1在插件module的build.gradle中加入javassist库,并配置发布的插件

apply plugin: 'groovy'
apply plugin: 'maven'

dependencies {
    compile gradleApi()//gradle sdk
    compile localGroovy()//groovy sdk
    compile 'com.android.tools.build:gradle:2.3.1'
//    compile 'com.android.tools.build:transform-api:1.5.0'
    compile 'org.javassist:javassist:3.20.0-GA'//javassist库
}
uploadArchives {
    repositories.mavenDeployer {//重新发布之后,会在本地的repo路径生成仓库(也可发布到远程)可参照http://geek.csdn.net/news/detail/53459
        repository(url: uri('../repo'))
        pom.groupId = 'com.app.plugin'
        pom.artifactId = 'gradleplugin'
        pom.version = '1.0.0'
    }
}

repositories {
    jcenter()
}

//注意⚠️:   插件修改后运行前需要重新发布: ./gradlew -p plugin clean build uploadArchives --info

1.2继承插件Plugin并将自定义的Transform注册到android中的插件中

public class JavassistPlugin implements Plugin<Project> {

    void apply(Project project) {
        def log = project.logger
        log.error "========================";
        log.error "Javassist开始修改Class!";
        log.error "========================";
        //版本不一样获取的AppExtension方式不一样,这里一定要注意
        //project.android.registerTransform(new JavassistTransform(project)) 这里是无效的
        def android = project.extensions.getByType(AppExtension);
        android.registerTransform(new JavassistTransform(project));
        project.task('transformPath') {
            doLast {
                System.out.println('+++++++++++++++++++++transformPath task')
            }
        }
    }
}
1.3自定义Transform
public class JavassistTransform extends Transform {
    Project project

    public JavassistTransform(Project project) {    // 构造函数,我们将Project保存下来备用
        this.project = project
        project.logger.error("====start===JavassistTransform");
    }

    //transformClassesWithMyClassTransformForDebug 运行时的名字
    //transformClassesWith + getName() + For + Debug或Release
    @Override
    String getName() {// 设置我们自定义的Transform对应的Task名称
        return com.app.plugin.JavassistTransform.simpleName;
    }


    @Override
    // 指定输入的类型,通过这里的设定,可以指定我们要处理的文件类型这样确保其他类型的文件不会传入
    //需要处理的数据类型,有两种枚举类型
    //CLASSES和RESOURCES,CLASSES代表处理的java的class文件,RESOURCES代表要处理java的资源
    Set<QualifiedContent.ContentType> getInputTypes() {
        return Sets.immutableEnumSet(QualifiedContent.DefaultContentType.CLASSES)
    }


    @Override
    /****指定Transform的作用范围
     *
     *  指Transform要操作内容的范围,官方文档Scope有7种类型:
     *  EXTERNAL_LIBRARIES            只有外部库
     *  PROJECT                       只有项目内容
     *  PROJECT_LOCAL_DEPS            只有项目的本地依赖(本地jar)
     *  PROVIDED_ONLY                 只提供本地或远程依赖项
     *  SUB_PROJECTS                  只有子项目。
     *  SUB_PROJECTS_LOCAL_DEPS       只有子项目的本地依赖项(本地jar)。
     *  TESTED_CODE                   由当前变量(包括依赖项)测试的代码
     */
    Set<QualifiedContent.Scope> getScopes() {
        return Sets.immutableEnumSet(QualifiedContent.Scope.PROJECT, QualifiedContent.Scope.PROJECT_LOCAL_DEPS,
                QualifiedContent.Scope.SUB_PROJECTS, QualifiedContent.Scope.SUB_PROJECTS_LOCAL_DEPS,
                QualifiedContent.Scope.EXTERNAL_LIBRARIES)
    }

    /***当前Transform是否支持增量编译*/
    @Override
    boolean isIncremental() {
        return false
    }

    /***
     * Transform中的核心方法,
     * @param context
     * @param inputs 中是传过来的输入流,其中有两种格式,一种是jar包格式一种是目录格式。
     * @param referencedInputs
     * @param outputProvider outputProvider 获取到输出目录,最后将修改的文件复制到输出目录,这一步必须做不然编译会报错
     * @param isIncremental
     * @throws IOException
     * @throws TransformException
     * @throws InterruptedException
     */
    @Override
    void transform(Context context, Collection<TransformInput> inputs,
                   Collection<TransformInput> referencedInputs,
                   TransformOutputProvider outputProvider, boolean isIncremental)
            throws IOException, TransformException, InterruptedException {//只有打包apk时才会执行,build不会执行
        def startTime = System.currentTimeMillis();
        project.logger.error("===exe=====transform" + inputs.size());
        // Transform的inputs有两种类型,一种是目录,一种是jar包,要分开遍历
        inputs.each { TransformInput input ->
            try {
                input.jarInputs.each {
                    project.logger.error("===exe=====jarInputs" );
                    MyInject.injectDir(it.file.getAbsolutePath(), BusHelper.PKG_NAME, project)
                    String outputFileName = it.name.replace(".jar", "") + '-' + it.file.path.hashCode()
                    def output = outputProvider.getContentLocation(outputFileName, it.contentTypes, it.scopes, Format.JAR)
                    FileUtils.copyFile(it.file, output)
                }
            } catch (Exception e) {
                project.logger.error e.getMessage();
            }
            //对类型为“文件夹”的input进行遍历
            input.directoryInputs.each { DirectoryInput directoryInput ->
                project.logger.error("===exe=====directoryInputs" );
                //文件夹里面包含的是我们手写的类以及R.class、BuildConfig.class以及R$XXX.class等
                MyInject.injectDir(directoryInput.file.absolutePath, BusHelper.PKG_NAME, project)
                // 获取output目录
                def dest = outputProvider.getContentLocation(directoryInput.name,
                        directoryInput.contentTypes, directoryInput.scopes,
                        Format.DIRECTORY)

                // 将input的目录复制到output指定目录
                FileUtils.copyDirectory(directoryInput.file, dest)
            }
        }
        ClassPool.getDefault().clearImportedPackages();
        project.logger.error("JavassistTransform cast :" + (System.currentTimeMillis() - startTime) / 1000 + " secs");
    }
}

主要看transform(xxx)方法。当编译打包时,会执行t该方法,通过看代码,主要是遍历两个jar和文件夹列表。随后主要是以下这行代码。

 MyInject.injectDir(it.file.getAbsolutePath(), BusHelper.PKG_NAME, project)
将当前文件夹路径以及当前项目的包名作为参数传进去。注意包名:要有统一的包名的开头,用于自动匹配app\build\intermediates\classes\debug\统一包名\....的包名路径

如这里:BusHelper.PKG_NAME=“me\”;那么自动匹配app\build\intermediates\classes\debug\me\...

MyInject.injectDir:

/****
     *
     * @param path jar包或者文件夹路径
     * @param packageName 自己的包名,一定要有一个统一的包名
     * @param project
     */
    public static void injectDir(String path, String packageName, Project project) {
        project.logger.error( "p == "+path+" pkg "+packageName);
        pool.appendClassPath(path)
        //project.android.bootClasspath 加入android.jar,否则找不到android相关的所有类
        pool.appendClassPath(project.android.bootClasspath[0].toString());
        Utils.importBaseClass(pool);
        File dir = new File(path)
        if (dir.isDirectory()) {//遍历文件夹
            dir.eachFileRecurse { File file ->
                String filePath = file.absolutePath//确保当前文件是class文件,并且不是系统自动生成的class文件
                if (filePath.endsWith(".class") && !filePath.contains('R$') && !filePath.contains('$')//代理类
                        && !filePath.contains('R.class') && !filePath.contains("BuildConfig.class")) {
                    // 判断当前目录是否是在我们的应用包里面
                    int index = filePath.indexOf(packageName);
                    project.logger.error( index+" filePath == "+filePath);//C:\Users\Administrator\Desktop\AOP\EventBus-master-javassit\ViewFinder-master\sample\build\intermediates\classes\debug\me\brucezz\viewfinder\sample\SecondActivity.class
                    project.logger.error( index+" packageName == "+packageName);
                    boolean isMyPackage = index != -1;
                    project.logger.error( index+"isMyPackage == "+isMyPackage);
                    if (isMyPackage) {
                        //将路径转成包名+类名
                        String className = Utils.getClassName(index, filePath);
                        project.logger.error( "pool : "+pool.toString());
                        project.logger.error( "className : "+className);
                        CtClass c = pool.getCtClass(className)
                        if (c.isFrozen()) c.defrost()
                        BusInfo mBusInfo = new BusInfo()
                        mBusInfo.setProject(project)
                        mBusInfo.setClazz(c)
                        if (c.getName().endsWith("Activity") || c.getSuperclass().getName().endsWith("Activity")) mBusInfo.setIsActivity(true)
                        boolean isAnnotationByBus = false;
                        //getDeclaredMethods获取自己申明的方法,c.getMethods()会把所有父类的方法都加上
                        for (CtMethod ctmethod : c.getDeclaredMethods()) {
                            String methodName = Utils.getSimpleName(ctmethod);
                            if (BusHelper.ON_CREATE.contains(methodName)) mBusInfo.setOnCreateMethod(ctmethod)
                            if (BusHelper.ON_DESTROY.contains(methodName)) mBusInfo.setOnDestroyMethod(ctmethod)
                            for (Annotation mAnnotation : ctmethod.getAnnotations()) {
                                if (mAnnotation.annotationType().canonicalName.equals(BusHelper.OkBusRegisterAnnotation))
                                    mBusInfo.setBusRegisterMethod(ctmethod)
                                if (mAnnotation.annotationType().canonicalName.equals(BusHelper.OkBusUnRegisterAnnotation))
                                    mBusInfo.setBusUnRegisterMethod(ctmethod)
                                if (mAnnotation.annotationType().canonicalName.equals(BusHelper.OkBusAnnotation)) {
                                    project.logger.info " method:" + c.getName() + " -" + ctmethod.getName()
                                    mBusInfo.methods.add(ctmethod)
                                    mBusInfo.annotations.add(mAnnotation)
                                    if (!isAnnotationByBus) isAnnotationByBus = true
                                }
                            }
                        }
                        if (((mBusInfo.BusRegisterMethod != null && mBusInfo.BusUnRegisterMethod == null
                                || mBusInfo.BusRegisterMethod == null && mBusInfo.BusUnRegisterMethod != null)))
                            assert false: Utils.getBusErr()
                        if (mBusInfo != null && isAnnotationByBus) {
                            try {
                                project.logger.error( "intBus path == "+path);
                                BusHelper.intBus(mBusInfo, path)
                            } catch (DuplicateMemberException e) {
                            }
                        }
                        c.detach()//用完一定记得要卸载,否则pool里的永远是旧的代码
                    }
                }
            }
        }
    }

一行行代码看:

1:

private final static ClassPool pool = ClassPool.getDefault();Utils.importBaseClass(pool); 

 /**
     * 事先载入相关类
     * @param pool
     */
    static void importBaseClass(ClassPool pool) {
        pool.importPackage(LogTimeHelper.LogTimeAnnotation);
        pool.importPackage(BusHelper.OkBusAnnotation);
        pool.importPackage(BusHelper.OkBusRegisterAnnotation);
        pool.importPackage(BusHelper.OkBusUnRegisterAnnotation);
        pool.importPackage("android.os.Bundle");
        pool.importPackage("me.brucezz.viewfinder.sample.event.OkBus")
        pool.importPackage("me.brucezz.viewfinder.sample.event.Event")
        pool.importPackage("android.os.Message")
    }

将需要的包名都导入池子中。主要是新增的代码所需要的导入的包。

2:递归遍历文件夹中所有class文件,并根据文件路径和包名开头获取class的包名+类名,从而从javassist库的池子中获取CtClass类操作当前.class

                       
                       
CtClass c = pool.getCtClass(className)
if (c.isFrozen()){
    c.defrost()
}
BusInfo mBusInfo = new BusInfo()
mBusInfo.setProject(project)
mBusInfo.setClazz(c)
if (c.getName().endsWith("Activity") || c.getSuperclass().getName().endsWith("Activity")){
    mBusInfo.setIsActivity(true)
}

获取当前class实例,并将其解冻使其可用,随后生成BusInfo实例,用以记录增加EventBus注册和反注册以及触发事件方法的注解。再者当前Activity命名必须是以activity结尾或父类的命名是以Activity结尾,这个写一个BaseActivity就可以了。

BusInfo

public class BusInfo {

    Project project//保留当前工程的引用
    CtClass clazz//当前处理的class
    List<CtMethod> methods = new ArrayList<>()//带有Bus注解的方法列表
    List<Annotation> annotations = new ArrayList<>()//带有Bus注解的注解列表
    List<Integer> eventIds = new ArrayList<>()//带有Bus注解的注解id列表
    boolean isActivity = false;//是否是在Activity
    CtMethod OnCreateMethod//Activity或Fragment的初始化方法
    CtMethod OnDestroyMethod//Activity或Fragment的销毁方法
    CtMethod BusRegisterMethod//被Register注解标注的初始化方法
    CtMethod BusUnRegisterMethod//被UnRegister注解标注的销毁方法
}

接着,遍历当前类的方法是否有使用注解以及是否重写onCreate()和onDestory(),如果有,则将注解信息解析存储到busInfo实例中。

//getDeclaredMethods获取自己申明的方法,c.getMethods()会把所有父类的方法都加上
for (CtMethod ctmethod : c.getDeclaredMethods()) {
    String methodName = Utils.getSimpleName(ctmethod);
    if (BusHelper.ON_CREATE.contains(methodName)) mBusInfo.setOnCreateMethod(ctmethod)
    if (BusHelper.ON_DESTROY.contains(methodName)) mBusInfo.setOnDestroyMethod(ctmethod)
    for (Annotation mAnnotation : ctmethod.getAnnotations()) {
        if (mAnnotation.annotationType().canonicalName.equals(BusHelper.OkBusRegisterAnnotation))
            mBusInfo.setBusRegisterMethod(ctmethod)
        if (mAnnotation.annotationType().canonicalName.equals(BusHelper.OkBusUnRegisterAnnotation))
            mBusInfo.setBusUnRegisterMethod(ctmethod)
        if (mAnnotation.annotationType().canonicalName.equals(BusHelper.OkBusAnnotation)) {
            project.logger.info " method:" + c.getName() + " -" + ctmethod.getName()
            mBusInfo.methods.add(ctmethod)
            mBusInfo.annotations.add(mAnnotation)
            if (!isAnnotationByBus) isAnnotationByBus = true
        }
    }

最后:解析完信息后,在onCreate()、onDestory()或带有注解的方法实现注册与反注册的EventBus,以及在当前类增加触发事件的方法。

调用BusHelper.intBus(mBusInfo, path):

static void intBus(BusInfo mBusInfo, String path) {
        if (mBusInfo.clazz.isFrozen()) mBusInfo.clazz.defrost()//解冻
        if (mBusInfo.BusRegisterMethod != null) {//有被BusRegister注解
            mBusInfo.project.logger.error "BusRegisterMethod not null" +
            mBusInfo.BusRegisterMethod.insertAfter(getRegisterEventMethodStr(mBusInfo));
        } else if (mBusInfo.getOnCreateMethod() == null) {//没有OnCreateMethod,创建并加上新代码
            mBusInfo.project.logger.quiet "getOnCreateMethod  null "  + mBusInfo.isActivity
            String pre_create_str = mBusInfo.isActivity ? Activity_OnCreate : Fragment_OnCreate;
            String m = pre_create_str + getRegisterEventMethodStr(mBusInfo) + "}"
            mBusInfo.project.logger.quiet m
            mBusInfo.project.logger.error m
            CtMethod mInitEventMethod = CtNewMethod.make(m, mBusInfo.clazz);
            mBusInfo.clazz.addMethod(mInitEventMethod)
        } else {//有OnCreateMethod,直接插入新代码
            mBusInfo.project.logger.error "OnCreateMethod not null"
            mBusInfo.project.logger.quiet "OnCreateMethod not null"
            mBusInfo.OnCreateMethod.insertAfter(getRegisterEventMethodStr(mBusInfo));
        }
        if (mBusInfo.BusUnRegisterMethod != null) {//有被BusUnRegister注解的方法
            mBusInfo.project.logger.quiet "BusUnRegisterMethod not null"
            mBusInfo.project.logger.error "BusUnRegisterMethod not null"
            mBusInfo.BusUnRegisterMethod.insertAfter(getUnRegisterEventMethodStr(mBusInfo));
        } else if (mBusInfo.OnDestroyMethod == null) {
            mBusInfo.project.logger.quiet "OnDestroyMethod null"
            mBusInfo.project.logger.error "OnDestroyMethod null"
            String m = Pre_OnDestroy + getUnRegisterEventMethodStr(mBusInfo) + "}";
            mBusInfo.project.logger.quiet m
            CtMethod destroyMethod = CtNewMethod.make(m, mBusInfo.clazz)
            mBusInfo.clazz.addMethod(destroyMethod)
        } else {
            mBusInfo.project.logger.quiet "OnDestroyMethod not null"
            mBusInfo.project.logger.error "OnDestroyMethod not null"
            mBusInfo.OnDestroyMethod.insertAfter(getUnRegisterEventMethodStr(mBusInfo));
        }

        mBusInfo.clazz.writeFile(path)
    }

主要是查看是否有使用注解或是否重写oncreate()和onDestory(),随后在带有注解的方法或onCreate()和onDestory()方法的最后通过ctMethod.insertAfter()插入EventBus的注册与反注册java语句或者class.addMethod()添加java方法,随后通过javassit库中的ctclass.writeFile()将新增的内容写回文件中。

反注册:

/**
     * 生成取消事件注册的代码
     * @param mBusInfo
     */
    static String getUnRegisterEventMethodStr(mBusInfo) {
        String dis_Str = "";
        mBusInfo.eventIds.each { id -> dis_Str += "OkBus.getInstance().unRegister(" + id + ");\n" }
        return dis_Str;
    }
注册:
/**
     * 获取初始化OkBus方法的代码
     * @param mBusInfo 事件信息
     * @return
     */
    static String getRegisterEventMethodStr(BusInfo mBusInfo) {
        String CreateStr = "";
        //为当前的类添加事件处理的接口:当前类要实现事件Event接口
        mBusInfo.clazz.addInterface(mBusInfo.clazz.classPool.get("me.brucezz.viewfinder.sample.event.Event"));
        for (int i = 0; i < mBusInfo.getMethods().size(); i++) {
            MethodInfo methodInfo = mBusInfo.getMethods().get(i).getMethodInfo();
            Annotation mAnnotation = mBusInfo.getAnnotations().get(i)
            AnnotationsAttribute attribute = methodInfo.getAttribute(AnnotationsAttribute.visibleTag);
            //获取注解属性
            javassist.bytecode.annotation.Annotation annotation = attribute.getAnnotation(mAnnotation.annotationType().canonicalName);
            //获取注解
            int id = ((IntegerMemberValue) annotation.getMemberValue("value")).getValue();//获取注解的值
            int thread = -1;
            if (annotation.getMemberValue("thread") != null)
                thread = ((IntegerMemberValue) annotation.getMemberValue("thread")).getValue();
            mBusInfo.eventIds.add(id)
            CreateStr += "OkBus.getInstance().register(" + id + ",(Event)this," + thread + ");\n"
        }
        initEventDispatch(mBusInfo)
        return CreateStr;
    }

当前类是实现统一事件的接口Event:注册完后,生成事件分发的逻辑代码方法如下:

/**
     * 生成event事件分发的逻辑代码
     * @param mBusInfo
     * @return
     */
    static initEventDispatch(BusInfo mBusInfo) {
        String SwitchStr = Pre_Switch_Str;//接口实现方法
        for (int i = 0; i < mBusInfo.eventIds.size(); i++) {
            CtMethod method = mBusInfo.getMethods().get(i)
            CtClass[] mParameterTypes = method.getParameterTypes();
            assert mParameterTypes.length <= 1
            boolean one = mParameterTypes.length == 1
            boolean isBaseType = false;
            String packageName = "";
            if (one) {
                String mParameterType = mParameterTypes[0].name;
                switch (mParameterType) {
                //Primitive Types(原始型)	Reference Types(Wrapper Class)(引用型,(包装类))
                    case "boolean": mParameterType = "Boolean"; isBaseType = true; break;
                    case "byte": mParameterType = "Byte"; isBaseType = true; break;
                    case "char": mParameterType = "Character"; isBaseType = true; break;
                    case "float": mParameterType = "Float"; isBaseType = true; break;
                    case "int": mParameterType = "Integer"; isBaseType = true; break;
                    case "long": mParameterType = "Long"; isBaseType = true; break;
                    case "short": mParameterType = "Short"; isBaseType = true; break;
                    case "double": mParameterType = "Double"; isBaseType = true; break;
                }
                mBusInfo.project.logger.quiet "name:" + mParameterType;
                packageName = isBaseType ? "java.lang." + mParameterType : mParameterType;
                mBusInfo.clazz.classPool.importPackage(packageName)
            }//如果是基本数据类型,需要手动拆箱,否则会报错

            String ParamStr = isBaseType ? ("((" + packageName + ")msg.obj)." +
                    mParameterTypes[0].name + "Value()") : ("(" + packageName + ")msg.obj");
            SwitchStr += "case " + mBusInfo.eventIds.get(i) + ":" + method.getName() +
                    "(" + (one ? ParamStr : "") + ");\n break;\n"
            mBusInfo.project.logger.error "pgkname:" + packageName;
        }
        String m = SwitchStr + "}\n}"
        mBusInfo.project.logger.quiet m
        CtMethod mDispatchEventMethod = CtMethod.make(m, mBusInfo.clazz);
        mBusInfo.clazz.addMethod(mDispatchEventMethod)
    }



MainActivity源代码:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private static final String TAG = "MainActivity";
    TextView mTextView;
    Button mButton;
    EditText mEditText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mTextView = (TextView) findViewById(R.id.tv);
        mButton = (Button) findViewById(R.id.btn);
        mEditText = (EditText) findViewById(R.id.et);
        mTextView.setOnClickListener(this);
        mButton.setOnClickListener(this);
        mEditText.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if (v == mTextView) {
            Toast.makeText(this, "1 onTextClick", Toast.LENGTH_SHORT).show();
        } else if (v == mButton) {
            Toast.makeText(this, "1 onButtonClick", Toast.LENGTH_SHORT).show();
            OkBus.getInstance().onEvent(EventTags.FLASH_INIT_UI);
        } else if (v == mEditText) {

        }
    }
    @Bus(EventTags.FLASH_INIT_UI)
    public void initUI() {
        AlphaAnimation anim = new AlphaAnimation(0.8f, 0.1f);
        anim.setDuration(500);
        anim.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                Log.d("jumpToMainPage","====onAnimationEnd");
                OkBus.getInstance().onEvent(EventTags.JUMP_TO_MAIN);
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
        mEditText.startAnimation(anim);
    }

    @Bus(EventTags.JUMP_TO_MAIN)
    public void jumpToMainPage() {
//        TRouter.go(C.HOME);
        SecondActivity.startInstance(this);
        Log.d("jumpToMainPage","====jumpToMainPage");
        finish();
    }
}

经过编译运行之后,class的代码就会被修改为:只列出生成的代码及方法,其它不变的删除了。如下

查看class路径:sample\build\intermediates\classes\debug

    protected void onCreate(Bundle savedInstanceState) {
        OkBus.getInstance().register(1, (Event)this, -1);
        OkBus.getInstance().register(2, (Event)this, -1);
    }
	
    public void call(Message var1) {
        switch(var1.what) {
        case 1:
            this.initUI();
            break;
        case 2:
            this.jumpToMainPage();
        }

    }

    protected void onDestroy() {
        super.onDestroy();
        OkBus.getInstance().unRegister(1);
        OkBus.getInstance().unRegister(2);
    }

可以看见自动生成了call(msg)方法和注册与发注册的语句。这样就完成了频繁书写注册与反注册麻烦事件。

javassit的用法就是这么简单,其它的可以查看javassit手册或者https://blog.csdn.net/u011425751/article/details/51917895

demo:https://download.csdn.net/download/zhongwn/10450171

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值