Reputation: 1263
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
}
}
Upvotes: 7
Views: 3657
Reputation: 504
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()
Upvotes: 0