Skip to content

Commit c2f295d

Browse files
committed
Use DexClassLoader instead of the MultiDex library.
1 parent 0978df8 commit c2f295d

File tree

7 files changed

+127
-1293
lines changed

7 files changed

+127
-1293
lines changed

src/src/com/tns/DexFactory.java

Lines changed: 115 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@
1010
import java.io.InputStreamReader;
1111
import java.io.InvalidClassException;
1212
import java.io.OutputStreamWriter;
13-
import java.lang.reflect.InvocationTargetException;
14-
import java.util.ArrayList;
13+
import java.util.HashMap;
1514
import java.util.HashSet;
16-
import java.util.List;
15+
import java.util.zip.ZipEntry;
16+
import java.util.zip.ZipOutputStream;
1717

1818
import com.tns.bindings.ProxyGenerator;
19-
import com.tns.multidex.MultiDex;
2019

20+
import dalvik.system.DexClassLoader;
2121
import android.content.Context;
2222
import android.content.pm.ApplicationInfo;
2323
import android.content.pm.PackageInfo;
@@ -26,34 +26,31 @@
2626

2727
public class DexFactory
2828
{
29-
private Context context;
30-
3129
private static final String SECONDARY_DEX_FOLDER_NAME = "code_cache" + File.separator + "secondary-dexes";
3230

33-
private String proxyPath;
31+
private String dexPath;
32+
private String odexPath;
33+
private String dexThumb;
34+
private Context context;
3435

3536
private ProxyGenerator proxyGenerator;
36-
37-
private File odexDir;
38-
39-
private HashSet<String> injectedProxyClasses = new HashSet<String>();
37+
private HashMap<String, Class<?>> injectedDexClasses = new HashMap<String, Class<?>>();
4038

41-
private String proxyThumb;
42-
4339
public DexFactory(Context context)
4440
{
4541
this.context = context;
4642

47-
ApplicationInfo applicationInfo = context.getApplicationInfo();
48-
proxyPath = applicationInfo.dataDir + File.separator + SECONDARY_DEX_FOLDER_NAME + File.separator;
49-
proxyGenerator = new ProxyGenerator(proxyPath);
43+
ApplicationInfo applicationInfo = this.context.getApplicationInfo();
44+
this.dexPath = applicationInfo.dataDir + File.separator + SECONDARY_DEX_FOLDER_NAME + File.separator;
45+
this.odexPath = applicationInfo.dataDir + File.separator + SECONDARY_DEX_FOLDER_NAME + File.separator + "odex" + File.separator;
46+
this.proxyGenerator = new ProxyGenerator(dexPath);
5047
ProxyGenerator.IsLogEnabled = Platform.IsLogEnabled;
51-
File dexDir = new File(proxyPath);
52-
odexDir = new File(dexDir.getAbsolutePath() + File.separator + "odex" + File.separator);
48+
49+
File odexDir = new File(dexPath + File.separator + "odex" + File.separator);
5350
odexDir.mkdirs();
54-
55-
updateProxyThumbAndPurgeCachedProxies(dexDir);
56-
proxyGenerator.setProxyThumb(proxyThumb);
51+
52+
this.updateDexThumbAndPurgeCache();
53+
this.proxyGenerator.setProxyThumb(this.dexThumb);
5754
}
5855

5956
static long totalGenTime = 0;
@@ -62,92 +59,79 @@ public DexFactory(Context context)
6259

6360
public Class<?> resolveClass(String name, String className, String[] methodOverrides) throws ClassNotFoundException, IOException
6461
{
62+
if(className.contains("NativeScriptActivity"))
63+
{
64+
// Do not extend NativeScriptActivity - it is already extended
65+
return NativeScriptActivity.class;
66+
}
67+
6568
String fullClassName = className.replace("$", "_") + "-" + name;
66-
67-
if (!injectedProxyClasses.contains(fullClassName))
69+
Class<?> existingClass = this.injectedDexClasses.get(fullClassName);
70+
if(existingClass != null)
6871
{
69-
File proxyFile = getProxyFile(fullClassName);
70-
71-
if (proxyFile == null)
72-
{
73-
long startGenTime = System.nanoTime();
74-
String proxyFilePath = "";
75-
76-
if (Platform.IsLogEnabled)
77-
{
78-
Log.d(Platform.DEFAULT_LOG_TAG, "generating proxy in place");
79-
}
80-
proxyFilePath = generateProxy(name, className, methodOverrides);
81-
proxyFile = new File(proxyFilePath);
82-
long stopGenTime = System.nanoTime();
83-
totalGenTime += stopGenTime - startGenTime;
84-
if (Platform.IsLogEnabled)
85-
{
86-
Log.d(Platform.DEFAULT_LOG_TAG, "Finished inplace gen took: " + (stopGenTime - startGenTime) / 1000000.0 + "ms");
87-
Log.d(Platform.DEFAULT_LOG_TAG, "TotalGenTime: " + totalGenTime / 1000000.0 + "ms");
88-
}
89-
}
72+
return existingClass;
73+
}
9074

91-
long startMultiDexTime = System.nanoTime();
92-
List<File> files = new ArrayList<File>();
93-
files.add(proxyFile);
94-
try
95-
{
96-
MultiDex.installSecondaryDexes(context.getClassLoader(), odexDir, files);
97-
injectedProxyClasses.add(fullClassName);
98-
}
99-
catch (IllegalArgumentException e)
100-
{
101-
e.printStackTrace();
102-
}
103-
catch (IllegalAccessException e)
104-
{
105-
e.printStackTrace();
106-
}
107-
catch (NoSuchFieldException e)
108-
{
109-
e.printStackTrace();
110-
}
111-
catch (InvocationTargetException e)
112-
{
113-
e.printStackTrace();
114-
}
115-
catch (NoSuchMethodException e)
116-
{
117-
e.printStackTrace();
118-
}
119-
long stopMultiDexTime = System.nanoTime();
120-
totalMultiDexTime += (stopMultiDexTime - startMultiDexTime);
75+
String classToProxy = this.getClassToProxyName(className);
76+
String dexFilePath = classToProxy + "-" + name;
77+
File dexFile = this.getDexFile(dexFilePath);
78+
79+
if (dexFile == null)
80+
{
81+
long startGenTime = System.nanoTime();
12182
if (Platform.IsLogEnabled)
12283
{
123-
Log.d(Platform.DEFAULT_LOG_TAG, "Finished injecting into multidex: " + proxyFile.getAbsolutePath() + " took: " + (stopMultiDexTime - startMultiDexTime) / 1000000.0 + "ms");
124-
Log.d(Platform.DEFAULT_LOG_TAG, "TotalMultiDexTime: " + totalMultiDexTime / 1000000.0 + "ms");
84+
Log.d(Platform.DEFAULT_LOG_TAG, "generating proxy in place");
12585
}
126-
127-
128-
long startLoadDexTime = System.nanoTime();
129-
// String classToProxyName = className.replace("$", "_");
130-
// className = classToProxyName;
131-
132-
Class<?> loaded = context.getClassLoader().loadClass(fullClassName);
133-
long stopLoadDexTime = System.nanoTime();
134-
totalLoadDexTime += (stopLoadDexTime - startLoadDexTime);
86+
87+
dexFilePath = this.generateDex(name, classToProxy, methodOverrides);
88+
dexFile = new File(dexFilePath);
89+
long stopGenTime = System.nanoTime();
90+
totalGenTime += stopGenTime - startGenTime;
13591
if (Platform.IsLogEnabled)
13692
{
137-
Log.d(Platform.DEFAULT_LOG_TAG, "Finished loading class : " + fullClassName + " took: " + (stopMultiDexTime - startMultiDexTime) / 1000000.0 + "ms");
138-
Log.d(Platform.DEFAULT_LOG_TAG, "TotalLoadDexTime: " + totalLoadDexTime / 1000000.0 + "ms");
93+
Log.d(Platform.DEFAULT_LOG_TAG, "Finished inplace gen took: " + (stopGenTime - startGenTime) / 1000000.0 + "ms");
94+
Log.d(Platform.DEFAULT_LOG_TAG, "TotalGenTime: " + totalGenTime / 1000000.0 + "ms");
13995
}
96+
}
97+
98+
String jarFilePath = dexFile.getPath().replace(".dex", ".jar");
99+
File jarFile = new File(jarFilePath);
100+
101+
Class<?> result;
102+
if(!jarFile.exists())
103+
{
104+
FileOutputStream jarFileStream = new FileOutputStream(jarFile);
140105

141-
return loaded;
106+
ZipOutputStream out = new ZipOutputStream(jarFileStream);
107+
out.putNextEntry(new ZipEntry("classes.dex"));
108+
byte[] dexData = new byte[(int)dexFile.length()];
109+
FileInputStream fi = new FileInputStream(dexFile);
110+
fi.read(dexData, 0, dexData.length);
111+
fi.close();
112+
out.write(dexData);
113+
out.closeEntry();
114+
out.close();
115+
142116
}
143-
144-
return findClass(fullClassName);
117+
118+
DexClassLoader dexClassLoader = new DexClassLoader(jarFilePath, this.odexPath, null, this.context.getClassLoader());
119+
result = dexClassLoader.loadClass(fullClassName);
120+
121+
this.injectedDexClasses.put(fullClassName, result);
122+
123+
return result;
145124
}
146125

147126
public Class<?> findClass(String className) throws ClassNotFoundException
148127
{
149128
String canonicalName = className.replace('/', '.');
150-
return context.getClassLoader().loadClass(canonicalName);
129+
Class<?> existingClass = this.injectedDexClasses.get(canonicalName);
130+
if(existingClass != null)
131+
{
132+
return existingClass;
133+
}
134+
return this.context.getClassLoader().loadClass(canonicalName);
151135
}
152136

153137
public static String strJoin(String[] array, String separator)
@@ -168,8 +152,8 @@ public static String strJoin(String[] array, String separator)
168152
}
169153
return sbStr.toString();
170154
}
171-
172-
private File getProxyFile(String className) throws InvalidClassException
155+
156+
private String getClassToProxyName(String className) throws InvalidClassException
173157
{
174158
String classToProxy = className;
175159

@@ -182,46 +166,40 @@ private File getProxyFile(String className) throws InvalidClassException
182166
{
183167
throw new InvalidClassException("Can't generate proxy of proxy");
184168
}
169+
170+
return classToProxy;
171+
}
185172

186-
String classToProxyFile = classToProxy.replace("$", "_");
173+
private File getDexFile(String className) throws InvalidClassException
174+
{
175+
String classToProxyFile = className.replace("$", "_");
187176

188-
if (proxyThumb != null)
177+
if (this.dexThumb != null)
189178
{
190-
classToProxyFile += "-" + proxyThumb;
179+
classToProxyFile += "-" + this.dexThumb;
191180
}
192181

193-
String proxyFilePath = proxyPath + classToProxyFile + ".dex";
194-
File proxyFile = new File(proxyFilePath);
195-
if (proxyFile.exists())
182+
String dexFilePath = dexPath + classToProxyFile + ".dex";
183+
File dexFile = new File(dexFilePath);
184+
if (dexFile.exists())
196185
{
197186
if (Platform.IsLogEnabled)
198187
{
199-
Log.d(Platform.DEFAULT_LOG_TAG, "Looking for proxy file: " + proxyFilePath + " Result: proxy file Found. ClassName: " + className);
188+
Log.d(Platform.DEFAULT_LOG_TAG, "Looking for proxy file: " + dexFilePath + " Result: proxy file Found. ClassName: " + className);
200189
}
201-
return proxyFile;
190+
return dexFile;
202191
}
203192

204193
if (Platform.IsLogEnabled)
205194
{
206-
Log.d(Platform.DEFAULT_LOG_TAG, "Looking for proxy file: " + proxyFilePath + " Result: NOT Found. Proxy Gen needed. ClassName: " + className);
195+
Log.d(Platform.DEFAULT_LOG_TAG, "Looking for proxy file: " + dexFilePath + " Result: NOT Found. Proxy Gen needed. ClassName: " + className);
207196
}
208197
return null;
209198
}
210199

211-
private String generateProxy(String proxyName, String className, String[] methodOverrides) throws ClassNotFoundException, IOException
200+
private String generateDex(String proxyName, String className, String[] methodOverrides) throws ClassNotFoundException, IOException
212201
{
213-
String classToProxyName = className;
214-
if (className.startsWith("com.tns.gen."))
215-
{
216-
classToProxyName = className.substring(12);
217-
}
218-
219-
if (classToProxyName.startsWith("com.tns.gen."))
220-
{
221-
throw new InvalidClassException("Can't generate proxy of proxy");
222-
}
223-
224-
Class<?> classToProxy = Class.forName(classToProxyName);
202+
Class<?> classToProxy = Class.forName(className);
225203

226204
HashSet<String> methodOverridesSet = null;
227205
if (methodOverrides != null)
@@ -237,43 +215,46 @@ private String generateProxy(String proxyName, String className, String[] method
237215
return proxyGenerator.generateProxy(proxyName, classToProxy, methodOverridesSet);
238216
}
239217

240-
private void updateProxyThumbAndPurgeCachedProxies(File proxyDir)
218+
private void updateDexThumbAndPurgeCache()
241219
{
242-
proxyThumb = generateProxyThumb();
243-
if (proxyThumb == null)
220+
this.dexThumb = this.generateDexThumb();
221+
if (this.dexThumb == null)
244222
{
245223
throw new RuntimeException("Error generating proxy thumb 1");
246224
}
247225

248-
String oldProxyThumb = getCachedProxyThumb(proxyDir);
249-
if (proxyThumb.equals(oldProxyThumb))
226+
File dexDir = new File(this.dexPath);
227+
String oldDexThumb = this.getCachedProxyThumb(dexDir);
228+
if (this.dexThumb.equals(oldDexThumb))
250229
{
251230
return;
252231
}
253232

254-
if (oldProxyThumb != null)
233+
if (oldDexThumb != null)
255234
{
256-
purgeProxiesByThumb(oldProxyThumb, proxyDir);
235+
this.purgeDexesByThumb(oldDexThumb, dexDir);
236+
this.purgeDexesByThumb(oldDexThumb, new File(odexPath));
257237
}
258238
else
259239
{
260-
//purge all dex files in proxy dir if no thumg file is found
261-
purgeProxiesByThumb(".dex", proxyDir);
240+
//purge all dex files if no thumb file is found
241+
this.purgeDexesByThumb(null, dexDir);
242+
this.purgeDexesByThumb(null, new File(odexPath));
262243
}
263244

264-
saveNewProxyThumb(proxyThumb, proxyDir);
245+
this.saveNewDexThumb(this.dexThumb, dexDir);
265246
}
266247

267-
private void saveNewProxyThumb(String newProxyThumb, File proxyDir)
248+
private void saveNewDexThumb(String newDexThumb, File dexDir)
268249
{
269-
File cachedThumbFile = new File(proxyDir, "proxyThumb");
250+
File cachedThumbFile = new File(dexDir, "proxyThumb");
270251
try
271252
{
272253
FileOutputStream out = new FileOutputStream(cachedThumbFile, false);
273254
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
274255
try
275256
{
276-
writer.write(newProxyThumb);
257+
writer.write(newDexThumb);
277258
writer.newLine();
278259
writer.flush();
279260
}
@@ -295,7 +276,7 @@ private void saveNewProxyThumb(String newProxyThumb, File proxyDir)
295276
}
296277
}
297278

298-
private String generateProxyThumb()
279+
private String generateDexThumb()
299280
{
300281
try
301282
{
@@ -313,7 +294,7 @@ private String generateProxyThumb()
313294
return null;
314295
}
315296

316-
private void purgeProxiesByThumb(String cachedproxyThumb, File pathToPurge)
297+
private void purgeDexesByThumb(String cachedDexThumb, File pathToPurge)
317298
{
318299
if (!pathToPurge.isDirectory())
319300
{
@@ -328,13 +309,13 @@ private void purgeProxiesByThumb(String cachedproxyThumb, File pathToPurge)
328309
File purgeCandidate = new File(pathToPurge, filename);
329310
if (purgeCandidate.isDirectory())
330311
{
331-
purgeProxiesByThumb(cachedproxyThumb, purgeCandidate);
312+
this.purgeDexesByThumb(cachedDexThumb, purgeCandidate);
332313
}
333314
else
334315
{
335-
if (!filename.contains(cachedproxyThumb))
316+
if (cachedDexThumb != null && !filename.contains(cachedDexThumb))
336317
{
337-
return;
318+
continue;
338319
}
339320

340321
boolean b = purgeCandidate.delete();
@@ -374,4 +355,4 @@ private String getCachedProxyThumb(File proxyDir)
374355

375356
return null;
376357
}
377-
}
358+
}

0 commit comments

Comments
 (0)