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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章