Example for declare(FieldId<?, ?> fieldId, int flags, Object staticValue)
I would like to create a norma modal with all its fields are public but I can't figure our how to use the provided function declare(FieldId, ?> fieldId, int flags, Object staticValue). Can someone please provide me with a working example
I want to create something like that
public class ModelA {
public String value1;
public String value2;
public String value3;
}
My issue is with FieldId value for the declare function.
this.dexMaker.declare(???, Modifier.PUBLIC, "");
I tried to my best knowledge and here is what I managed to make so far The following is the main package com.kn.testcreatingaclassinruntime CreateClassInRunTimeWrapper is another package inside of it
package com.kn.testcreatingaclassinruntime.CreateClassInRunTimeWrapper;
import com.android.dx.DexMaker;
import com.android.dx.FieldId;
import com.android.dx.TypeId;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
/**
* This is a DexMaker wrapper to create classes in run time
* because the existing code is way too big and confusing for me
* @author Ahmed Moussa <email>[email protected]</email>
* @version 1.0
* Created by Ahmed Moussa on 7/2/18.
*/
public final class DexMakerWrapper {
// ClassName
private String ClassName;
// dexMaker used to create the class
private DexMaker dexMaker;
// class declaration after finishing
private Class<?> createdClass;
// make and entry point to the train methodology
public static DexMakerWrapper Constructor() {
return new DexMakerWrapper();
}
// use the it to initiate an object of the dex maker class
private DexMakerWrapper() {
this.dexMaker = new DexMaker();
}
// set class name as well as start class declaration
public DexMakerWrapper setClassName(String ClassName) {
this.ClassName = ClassName;
TypeId<?> classDeclaration = TypeId.get("L" + this.ClassName + ";");
this.dexMaker.declare(classDeclaration, this.ClassName + ".generated", Modifier.PUBLIC, TypeId.OBJECT);
return this;
}
// set class fields with data type String for all of them
public DexMakerWrapper setStringFields(ArrayList<String> Fields) {
TypeId<System> systemType = TypeId.get(System.class);
TypeId<String> DataType = TypeId.get(String.class);
for (String field : Fields) {
FieldId<System, String>FieldDec = systemType.getField(DataType, field);
this.dexMaker.declare(FieldDec, Modifier.PUBLIC, null);
}
return this;
}
// creating the class itself
public Class<?> createClass() {
File outputDir = new File(".");
try {
ClassLoader loader = this.dexMaker.generateAndLoad(DexMakerWrapper.class.getClassLoader(), outputDir);
this.createdClass = loader.loadClass(this.ClassName);
return this.createdClass;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
Here is an example of my use of previous code
val fields = ArrayList<String>()
fields.add("A")
fields.add("B")
fields.add("C")
fields.add("D")
val dex: DexMakerWrapper = DexMakerWrapper.Constructor().setClassName("ModelA").setStringFields(fields)
val createdClass = dex.createClass()
I'm getting a crash java.lang.RuntimeException
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.kn.testcreatingaclassinruntime/com.kn.testcreatingaclassinruntime.MainActivity}: java.lang.IllegalStateException: Undeclared type Ljava/lang/System; declares members: [Ljava/lang/System;.A, Ljava/lang/System;.B, Ljava/lang/System;.C, Ljava/lang/System;.D] []
The crash is happening inside your code DexMaker class inside generate function
Any help would be appreciated.
Another update. For some reason or another I'm unable to figure out the reason for the crash except the following information. Ljava/lang/system is no declared. in TypeDeclaration class declared variable value is always false on it why no idea. I'm also clueless as of what changes this value or what set it. So I changed my Wrapper
package com.kn.testcreatingaclassinruntime.CreateClassInRunTimeWrapper;
import android.content.ContextWrapper;
import com.android.dx.DexMaker;
import com.android.dx.FieldId;
import com.android.dx.TypeId;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
/**
* This is a DexMaker wrapper to create classes in run time
* because the existing code is way too big and confusing for me
* @author Ahmed Moussa <email>[email protected]</email>
* @version 1.0
* Created by Ahmed Moussa on 7/2/18.
*/
public final class DexMakerWrapper {
// ClassName
private String ClassName;
// dexMaker used to create the class
private DexMaker dexMaker;
// class declaration code
private TypeId<?> classDeclaration;
// class declaration after finishing
private Class<?> createdClass;
// make and entry point to the train methodology
public static DexMakerWrapper Constructor() {
return new DexMakerWrapper();
}
// use the it to initiate an object of the dex maker class
private DexMakerWrapper() {
this.dexMaker = new DexMaker();
}
// set class name as well as start class declaration
public DexMakerWrapper setClassName(String ClassName) {
this.ClassName = ClassName;
this.classDeclaration = TypeId.get("Lcom/kn/testcreatingaclassinruntime/CreateClassInRunTimeWrapper/" + this.ClassName + ";");
this.dexMaker.declare(classDeclaration, this.ClassName + ".generated", Modifier.PUBLIC, TypeId.OBJECT);
return this;
}
// set class fields with data type String for all of them
public DexMakerWrapper setStringFields(ArrayList<String> Fields) {
// TypeId<System> systemType = TypeId.get(System.class);
TypeId<String> DataType = TypeId.get(String.class);
for (String field : Fields) {
FieldId<System, String> FieldDec = (FieldId<System, String>) this.classDeclaration.getField(DataType, field); //systemType.getField(DataType, field);
this.dexMaker.declare(FieldDec, Modifier.PUBLIC, null);
}
return this;
}
// creating the class itself
public Class<?> createClass(File f) {
File outputDir = f;
try {
ClassLoader loader = this.dexMaker.generateAndLoad(DexMakerWrapper.class.getClassLoader(), outputDir);
this.createdClass = loader.loadClass(this.ClassName);
return this.createdClass;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
}
And my usage
val fields = ArrayList<String>()
fields.add("A")
fields.add("B")
fields.add("C")
fields.add("D")
var dir = this.dataDir
val createdClass = DexMakerWrapper.Constructor().setClassName("ModelA").setStringFields(fields).createClass(dir)
for (f in createdClass!!.declaredFields) {
println(f.name)
}
Also had to add
Now I'm getting java.lang.ClassNotFoundException: Didn't find class "ModelA" on path: DexPathList[[zip file "/data/user/0/com.kn.testcreatingaclassinruntime/Generated_-1884772861.jar"],nativeLibraryDirectories=[/system/lib, /vendor/lib]]
Now what did I do wrong here?
Example code itself is not running?
Guys, Any help would be deeply appreciated. @drewhannay @moltmann
Managed to test a fix.
// this line should include the absolute path of the class
this.createdClass = loader.loadClass("com.kn.testcreatingaclassinruntime.CreateClassInRunTimeWrapper." + this.ClassName);
So Far I'm able to create the Class in run time as shown in the code. With the wanted structure
public class ModelA {
public String value1;
public String value2;
public String value3;
}
But I'm not able to create new instant out of it and therefore I'm not able to set fields value which is what I want
I need to make an ArrayList of this newly generated Class and set the values inside their fields.
@drewhannay @moltmann
@abti
@swankjesse
@allight