美文网首页
时间打印插件

时间打印插件

作者: 禅座 | 来源:发表于2019-06-14 13:50 被阅读0次
class CostTimePlugin extends Transform implements Plugin<Project> {
    def isLibrary

    @Override
    void apply(Project project) {
        isLibrary = project.plugins.hasPlugin(LibraryPlugin)
        def android
        if (isLibrary) {
            android = project.extensions.getByType(LibraryExtension)
        } else {
            android = project.extensions.getByType(AppExtension)
        }
        android.registerTransform(this)
    }

    @Override
    String getName() {
        return "costPlugin"
    }

    @Override
    Set getInputTypes() {
        return TransformManager.CONTENT_CLASS
    }

    @Override
    Set getScopes() {
        if (isLibrary) {
            return TransformManager.PROJECT_ONLY
        }
        return TransformManager.SCOPE_FULL_PROJECT
    }

    @Override
    boolean isIncremental() {
        return false
    }

    @Override
    void transform(com.android.build.api.transform.Context context, Collection<TransformInput> inputs, Collection<TransformInput> referencedInputs, TransformOutputProvider outputProvider, boolean isIncremental) throws IOException, TransformException, InterruptedException {
        println '//===============asm visit start===============//'
        def startTime = System.currentTimeMillis()
        inputs.each { TransformInput input ->
            input.directoryInputs.each { DirectoryInput directoryInput ->
                //坐等遍历class并被ASM操作
                if (directoryInput.file.isDirectory()) {
                    directoryInput.file.eachFileRecurse { File file ->
                        def name = file.name
                        if (name.endsWith(".class") && !name.startsWith("R\$") &&
                                !"R.class".equals(name) && !"BuildConfig.class".equals(name)) {
                            println name + ' is changing...'
                            ClassReader cr = new ClassReader(file.bytes)
                            ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS)
                            ClassVisitor cv = new CostClassVisitor(cw)
                            cr.accept(cv, EXPAND_FRAMES)
                            byte[] code = cw.toByteArray()
                            FileOutputStream fos = new FileOutputStream(
                                    file.parentFile.absolutePath + File.separator + name);
                            fos.write(code)
                            fos.close()
                        }
                    }
                }
                def dest = outputProvider.getContentLocation(directoryInput.name,
                        directoryInput.contentTypes, directoryInput.scopes,
                        Format.DIRECTORY)
                FileUtils.copyDirectory(directoryInput.file, dest)
            }
            input.jarInputs.each { JarInput jarInput ->
                def jarName = jarInput.name
                def md5Name = DigestUtils.md5Hex(jarInput.file.getAbsolutePath())
                if (jarName.endsWith(".jar")) {
                    jarName = jarName.substring(0, jarName.length() - 4)
                }
                def dest = outputProvider.getContentLocation(jarName + md5Name,
                        jarInput.contentTypes, jarInput.scopes, Format.JAR)
                FileUtils.copyFile(jarInput.file, dest)
            }
        }
        def cost = (System.currentTimeMillis() - startTime) / 1000
        println "plugin cost $cost secs"
        println '//===============asm visit end===============//'
    }
}
public class CostClassVisitor extends ClassVisitor {
    private boolean inject = false;
    private String mClassName = "";
    private String mThreshold = "0";

    public CostClassVisitor(ClassVisitor classVisitor) {
        super(Opcodes.ASM5, classVisitor);
    }

    @Override
    public void visit(int i, int i1, String s, String s1, String s2, String[] strings) {
        super.visit(i, i1, s, s1, s2, strings);
        mClassName = s.substring(s.lastIndexOf("/") + 1);
    }

    @Override
    public AnnotationVisitor visitAnnotation(String s, boolean b) {
        if ("Lcom/meitu/asm/Cost;".equals(s)) {
            inject = true;
        }
        return super.visitAnnotation(s, b);
    }

    @Override
    public MethodVisitor visitMethod(int access, final String name, String desc, String signature, String[] exceptions) {
        mThreshold = "0";
        MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
        mv = new AdviceAdapter(Opcodes.ASM5, mv, access, name, desc) {
            private boolean methodInject = false;

            @Override
            public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
                if ("Lcom/meitu/asm/Cost;".equals(desc)) {
                    methodInject = true;
                    AnnotationVisitor av = super.visitAnnotation(desc, visible);
                    AnnotationVisitor annotationVisitor = new AnnotationVisitor(Opcodes.ASM5, av) {
                        @Override
                        public void visit(String s, Object o) {
                            super.visit(s, o);
                            if (s.equals("standardTime")) {
                                mThreshold = o + "";
                            }
                        }

                    };
                    return annotationVisitor;
                } else {
                    return super.visitAnnotation(desc, visible);
                }
            }

            @Override
            public void visitInsn(int i) {
                super.visitInsn(i);
            }

            @Override
            public void visitMethodInsn(int i, String s, String s1, String s2, boolean b) {
                if (methodInject) {
                    mv.visitLdcInsn("cost_" + mClassName + "_" + name + "_" + s1);
                    mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
                    mv.visitMethodInsn(INVOKESTATIC, "com/meitu/asm/TimeCache", "setStartTime", "(Ljava/lang/String;J)V", false);
                    super.visitMethodInsn(i, s, s1, s2, b);
                    mv.visitLdcInsn("cost_" + mClassName + "_" + name + "_" + s1);
                    mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
                    mv.visitMethodInsn(INVOKESTATIC, "com/meitu/asm/TimeCache", "setEndTime", "(Ljava/lang/String;J)V", false);

                    mv.visitLdcInsn("cost_" + mClassName + "_" + name + "_" + s1);
                    mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
                    mv.visitInsn(DUP);
                    mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
                    mv.visitLdcInsn("cost_" + mClassName + "_" + name + "_" + s1);
                    mv.visitMethodInsn(INVOKESTATIC, "com/meitu/asm/TimeCache", "getCostTime", "(Ljava/lang/String;)Ljava/lang/String;", false);
                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
                    mv.visitLdcInsn("," + 0 + "ms");
                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
                    mv.visitMethodInsn(INVOKESTATIC, "android/util/Log", "d", "(Ljava/lang/String;Ljava/lang/String;)I", false);
                    mv.visitInsn(POP);
                } else {
                    super.visitMethodInsn(i, s, s1, s2, b);
                }
            }

            @Override
            protected void onMethodEnter() {
                if (inject || methodInject) {
                    mv.visitLdcInsn("cost_" + mClassName + "_" + name);
                    mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
                    mv.visitMethodInsn(INVOKESTATIC, "com/meitu/asm/TimeCache", "setStartTime", "(Ljava/lang/String;J)V", false);
                }
            }

            @Override
            protected void onMethodExit(int opcode) {
                if (inject || methodInject) {
                    mv.visitLdcInsn("cost_" + mClassName + "_" + name);
                    mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false);
                    mv.visitMethodInsn(INVOKESTATIC, "com/meitu/asm/TimeCache", "setEndTime", "(Ljava/lang/String;J)V", false);

                    mv.visitLdcInsn("cost_" + mClassName + "_" + name);
                    mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
                    mv.visitInsn(DUP);
                    mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
                    mv.visitLdcInsn("cost_" + mClassName + "_" + name);
                    mv.visitMethodInsn(INVOKESTATIC, "com/meitu/asm/TimeCache", "getCostTime", "(Ljava/lang/String;)Ljava/lang/String;", false);
                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
                    mv.visitLdcInsn("," + mThreshold + "ms");
                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
                    mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
                    mv.visitMethodInsn(INVOKESTATIC, "android/util/Log", "d", "(Ljava/lang/String;Ljava/lang/String;)I", false);
                    mv.visitInsn(POP);
                }
            }
        };
        return mv;
    }
}

相关文章

网友评论

      本文标题:时间打印插件

      本文链接:https://www.haomeiwen.com/subject/iehrxctx.html