Skip to content

Instantly share code, notes, and snippets.

@nickcaballero
Last active April 10, 2024 10:06
Show Gist options
  • Star 29 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save nickcaballero/7045993 to your computer and use it in GitHub Desktop.
Save nickcaballero/7045993 to your computer and use it in GitHub Desktop.
// The following is in addition to the build.gradle for a normal application, not a library
afterEvaluate { project ->
android.applicationVariants.each { variant ->
File outputFile = new File(variant.mergeAssets.outputDir, "dexter.dex")
def dexterExternalLibraries = task "dexter${variant.name}ExternalLibraries" {
dependsOn variant.mergeAssets
inputs.files configurations.provided
outputs.file outputFile
doLast {
def builder = getPlugins().findPlugin('android').getAndroidBuilder(variant.mergeAssets.variant)
builder.convertByteCode([], configurations.provided, null, outputFile.absolutePath, variant.dex.dexOptions, true)
}
}
variant.processResources.dependsOn.add(dexterExternalLibraries)
variant.javaCompile.classpath += configurations.provided
}
}
configurations {
provided
}
dependencies {
provided 'group:module:version'
}
public class Dexter {
private static String optimizedDirectory = "optimized";
private static String workDirectory = "working";
public static void loadFromAssets(Context context, String fileName) throws Exception {
File optimized = new File(optimizedDirectory);
optimized = context.getDir(optimized.toString(), Context.MODE_PRIVATE);
optimized = new File(optimized, fileName);
optimized.mkdir();
File work = context.getDir(workDirectory, Context.MODE_PRIVATE);
work = new File(work, fileName);
InputStream inputDex = context.getAssets().open(fileName);
FileOutputStream outputDex = new FileOutputStream(work);
byte[] buf = new byte[0x1000];
while (true) {
int r = inputDex.read(buf);
if (r == -1)
break;
outputDex.write(buf, 0, r);
}
outputDex.close();
inputDex.close();
ClassLoader localClassLoader = Dexter.class.getClassLoader();
BaseDexClassLoader classLoader = new DexClassLoader(work.getAbsolutePath(), optimized.getAbsolutePath(), null, localClassLoader);
if(localClassLoader instanceof BaseDexClassLoader) {
Object existing = getDexClassLoaderElements((BaseDexClassLoader) localClassLoader);
Object incoming = getDexClassLoaderElements(classLoader);
Object joined = joinArrays(incoming, existing);
setDexClassLoaderElements((BaseDexClassLoader) localClassLoader, joined);
} else {
throw new UnsupportedOperationException("Class loader not supported");
}
}
private static void setDexClassLoaderElements(BaseDexClassLoader classLoader, Object elements) throws Exception {
Class<BaseDexClassLoader> dexClassLoaderClass = BaseDexClassLoader.class;
Field pathListField = dexClassLoaderClass.getDeclaredField("pathList");
pathListField.setAccessible(true);
Object pathList = pathListField.get(classLoader);
Field dexElementsField = pathList.getClass().getDeclaredField("dexElements");
dexElementsField.setAccessible(true);
dexElementsField.set(pathList, elements);
}
private static Object getDexClassLoaderElements(BaseDexClassLoader classLoader) throws Exception {
Class<BaseDexClassLoader> dexClassLoaderClass = BaseDexClassLoader.class;
Field pathListField = dexClassLoaderClass.getDeclaredField("pathList");
pathListField.setAccessible(true);
Object pathList = pathListField.get(classLoader);
Field dexElementsField = pathList.getClass().getDeclaredField("dexElements");
dexElementsField.setAccessible(true);
Object dexElements = dexElementsField.get(pathList);
return dexElements;
}
private static Object joinArrays(Object o1, Object o2) {
Class<?> o1Type = o1.getClass().getComponentType();
Class<?> o2Type = o2.getClass().getComponentType();
if(o1Type != o2Type)
throw new IllegalArgumentException();
int o1Size = Array.getLength(o1);
int o2Size = Array.getLength(o2);
Object array = Array.newInstance(o1Type, o1Size + o2Size);
int offset = 0, i;
for(i = 0; i < o1Size; i++, offset++)
Array.set(array, offset, Array.get(o1, i));
for(i = 0; i < o2Size; i++, offset++)
Array.set(array, offset, Array.get(o2, i));
return array;
}
}
public class MainActivity extends Activity {
private static String[] DEX_FILES = {"dexter.dex"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
for (String dex : DEX_FILES)
Dexter.loadFromAssets(this, dex);
} catch (Exception e) {
throw new RuntimeException("Unable to load DEX files", e);
}
try {
startActivity(new Intent(MainActivity.this, getClassLoader().loadClass("the real activity")));
} catch (ClassNotFoundException e) {
throw new RuntimeException("Unable to start application", e);
}
}
}
@danielgoncharov
Copy link

Hi hamen,
You should put inside your own dependency for example
provided 'com.google.code.gson:gson:2.2.4'

@creativepsyco
Copy link

Does this approach work for library projects?

@hamen
Copy link

hamen commented Jul 2, 2014

Unfortunately no luck until now. I'm getting

"Could not find property 'variant' on task"

Any idea?

@VishalNehra
Copy link

Same here...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment