How can I use PathClassLoader to replace the deprecated DexFile APIs?
Asked Answered
C

1

7

I've got a class in which I do some runtime annotation scanning, but it uses the deprecated DexFile APIs which causes a warning to appear in LogCat:

  • W/zygote64: Opening an oat file without a class loader. Are you using the deprecated DexFile APIs?.

I'd like to get rid of this message and use the proper APIs. The docs suggest PathClassLoader, but I don't see how it is equivalent to DexFile in functionality. I can use a PathClassLoader in conjunction with a DexFile instance, and while it does work, it gives me even more warnings and takes longer to scan. I've included the annotation scanner I wrote below for the sake of clarity. If anyone can suggest how to get rid of these warning messages and an alternative to DexFile, so I don't get hit with broken functionality after it's removed, I'd be super appreciative.

class AnnotationScanner {
    companion object {
        fun classesWithAnnotation(
            context: Context,
            annotationClass: Class<out Annotation>,
            packageName: String? = null
        ): Set<Class<*>> {

            return Pair(context.packageCodePath, context.classLoader)
                .letAllNotNull { packageCodePath, classLoader ->
                    Pair(DexFile(packageCodePath), classLoader)
                }
                ?.letAllNotNull { dexFile, classLoader ->
                    dexFile
                        .entries()
                        ?.toList()
                        ?.filter { entry ->
                            filterByPackageName(packageName, entry)
                        }
                        ?.map {
                            dexFile.loadClass(it, classLoader)
                        }
                        ?.filter { aClass ->
                            filterByAnnotation(aClass, annotationClass)
                        }
                        ?.toSet()
                } ?: emptySet<Class<*>>().wlog { "No ${annotationClass.simpleName} annotated classes found" }
        }

        private fun filterByAnnotation(aClass: Class<*>?, annotationClass: Class<out Annotation>): Boolean {
            return aClass
                ?.isAnnotationPresent(annotationClass)
                ?.also {
                    it.ifTrue {
                        Timber.w("Found ${annotationClass.simpleName} on $aClass")
                    }
                }
                ?: false
        }

        private fun filterByPackageName(packageName: String?, entry: String) =
            packageName?.let { entry.toLowerCase().startsWith(it.toLowerCase()) } ?: true
    }
}
Cyst answered 1/5, 2018 at 16:36 Comment(3)
Did you find a way around this?Nunatak
No, I never got the time to sink into finding a better solution. I imagine the best solution would be to avoid runtime annotation processing.Cyst
Thanks, that's what I ended up doing as wellNunatak
F
0

You can say that there's nothing that replace DexFile for your case but there's another way to scan files using Annotation Processor you can search to find documentation about it

I'll give you an example on how to get classes names

instead of going to scan classes in the runtime you can scan in the build time and write on a java class a list of classes names and then use that generated class to get the classes names

@SupportedAnnotationTypes("*")
public class Processor extends AbstractProcessor {
    private ProcessingEnvironment mProcessingEnvironment;
    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        mProcessingEnvironment = processingEnvironment;
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        Types typeUtils = mProcessingEnvironment.getTypeUtils();
        List<String> modelsClassesNames = new ArrayList<>();
        TypeElement oModelTypeElement = processingEnv.getElementUtils().getTypeElement("com.insidjam.core.orm.OModel"); // Replace com.example.OModel with the package and name of your OModel class
        mProcessingEnvironment.getMessager().printMessage(Diagnostic.Kind.NOTE, "Generating models names");
        for (TypeElement annotation : annotations) {
            for(Element element : roundEnv.getRootElements()){
                if (element.getKind().isClass()) {
                    TypeMirror oModelType = oModelTypeElement.asType();
                    TypeMirror elementType = element.asType();
                    if (typeUtils.isSubtype(elementType, oModelType)) {
                        String className = ((TypeElement) element).getQualifiedName().toString();
                        modelsClassesNames.add(className);
                        System.out.println("Processing model: " + className);
                    }
                }
            }
        }
        generateClass(modelsClassesNames);
        return true;
    }
    private void generateClass(List<String> classesNames) {
        try {
            String baseClassName = "ModelRegistry";
            String relativeClassName = "com.example.annotationprocessor."+baseClassName;
            JavaFileObject jfo = mProcessingEnvironment.getFiler().createSourceFile(relativeClassName);
            try (Writer writer = jfo.openWriter()) {
                writer.write("package com.example.annotationprocessor;\n\n");
                writer.write("public class " + baseClassName + " {\n\n");
                writer.write("    public static String[] getClassesNames() {\n");
                writer.write("        return new String[] {\n");
                for(int i = 0; i < classesNames.size(); i++){
                    String className = classesNames.get(i);
                    writer.write("            \"");
                    writer.write(className);
                    if(i < classesNames.size() -1) {
                        writer.write("\",");
                    }else{
                        writer.write("\"");
                    }
                }
                writer.write("                             };\n");
                writer.write("    }\n");
                writer.write("}\n");
            }
        } catch (Exception e) {
            mProcessingEnvironment.getMessager().printMessage(Diagnostic.Kind.NOTE, "Unable to write ******" + e.getMessage());
            e.printStackTrace();
        }
    }
}

and then use that generated class as follows import com.example.annotationprocessor.ModelRegistry; ModelRegistry.getClassesNames()

Flowage answered 27/4 at 15:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.