.jar
and .apk
files containing a classes.dex
entry. This can be used to
execute code not installed as part of an application.This class loader requires an application-private, writable directory to cache optimized classes. Use
Context.getCodeCacheDir()
to create such a directory:File dexOutputDir = context.getCodeCacheDir();
public DexClassLoader (String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent)
Creates a DexClassLoader
that finds interpreted and native code. Interpreted classes are found in a set of DEX files contained in Jar or APK files.
The path lists are separated using the character specified by the path.separator
system property, which defaults to :
.
Parameters
dexPath |
the list of jar/apk files containing classes and resources, delimited by File.pathSeparator , which defaults to ":" on Android |
---|---|
optimizedDirectory |
directory where optimized dex files should be written; must not be null |
libraryPath |
the list of directories containing native libraries, delimited by File.pathSeparator ; may be null |
parent | the parent class loader |
java.lang.Object | |||
↳ | java.lang.ClassLoader | ||
↳ | dalvik.system.BaseDexClassLoader | ||
↳ | dalvik.system.DexClassLoader |
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class clazz = pathList.findClass(name);
if (clazz == null) {
throw new ClassNotFoundException(name);
}
return clazz;
}
protected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {
Class<?> clazz = findLoadedClass(className);
if (clazz == null) {
try {
clazz = parent.loadClass(className, false);
} catch (ClassNotFoundException e) {
// Don't want to see this.
}
if (clazz == null) {
clazz = findClass(className);
}
}
return clazz;
}
public interface ILib {
public void sayHello();
}
public class Lib implements ILib {
@Override
public void sayHello() {
// TODO Auto-generated method stub
System.out.println("Hello from Thrid Dex Lib");
}
}
private void copy2SDCard(File dexInternalStoragePath) {
BufferedInputStream bis;
try {
bis = new BufferedInputStream(getAssets().open(LIB_JAR));
OutputStream dexWriter = new BufferedOutputStream(
new FileOutputStream(dexInternalStoragePath));
byte[] buf = new byte[BUF_SIZE];
int len;
while ((len = bis.read(buf, 0, BUF_SIZE)) > 0) {
dexWriter.write(buf, 0, len);
}
dexWriter.close();
bis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private String LIB_JAR = "mylib.jar";
static final int BUF_SIZE = 8 * 1024;
static final String TAG = "MCL";
public class MyDexClassLoader extends DexClassLoader {
public final static String TAG = "MCL";
public MyDexClassLoader(String dexPath, String optimizedDirectory,
String libraryPath, ClassLoader parent) {
super(dexPath, optimizedDirectory, libraryPath, parent);
// TODO Auto-generated constructor stub
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// TODO Auto-generated method stub
Log.i(TAG, "[CL] findCLass: " + name);
return super.findClass(name);
}
@Override
public String findLibrary(String name) {
// TODO Auto-generated method stub
Log.i(TAG, "[CL] findLibrary: " + name);
return super.findLibrary(name);
}
@Override
protected URL findResource(String name) {
// TODO Auto-generated method stub
Log.i(TAG, "[CL] findResource: " + name);
return super.findResource(name);
}
@Override
protected Enumeration<URL> findResources(String name) {
// TODO Auto-generated method stub
Log.i(TAG, "[CL] findResource: " + name);
return super.findResources(name);
}
@Override
protected synchronized Package getPackage(String name) {
// TODO Auto-generated method stub
Log.i(TAG, "[CL] getPackage: " + name);
return super.getPackage(name);
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "MyDexClassLoader: - " + super.toString();
}
@Override
public URL getResource(String resName) {
// TODO Auto-generated method stub
Log.i(TAG, "[CL] getResource: " + resName);
return super.getResource(resName);
}
@Override
public Enumeration<URL> getResources(String resName) throws IOException {
// TODO Auto-generated method stub
Log.i(TAG, "[CL] getResource: " + resName);
return super.getResources(resName);
}
@Override
public InputStream getResourceAsStream(String resName) {
// TODO Auto-generated method stub
Log.i(TAG, "[CL] getResourceAsStream: " + resName);
return super.getResourceAsStream(resName);
}
@Override
public Class<?> loadClass(String className) throws ClassNotFoundException {
// TODO Auto-generated method stub
Log.i(TAG, "[CL] loadClass: " + className);
return super.loadClass(className);
}
@Override
protected Class<?> loadClass(String className, boolean resolve)
throws ClassNotFoundException {
// TODO Auto-generated method stub
// Log.i(TAG, String.format("[CL] loadClass: Name - %s, resolved - %b",
// className, resolve));
return super.loadClass(className, resolve);
}
@Override
protected Package[] getPackages() {
// TODO Auto-generated method stub
Log.i(TAG, "[CL] getPackages");
return super.getPackages();
}
@Override
protected Package definePackage(String name, String specTitle,
String specVersion, String specVendor, String implTitle,
String implVersion, String implVendor, URL sealBase)
throws IllegalArgumentException {
// TODO Auto-generated method stub
Log.i(TAG, "[CL] definePackage: " + name);
return super.definePackage(name, specTitle, specVersion, specVendor,
implTitle, implVersion, implVendor, sealBase);
}
@Override
public void setClassAssertionStatus(String cname, boolean enable) {
// TODO Auto-generated method stub
Log.i(TAG, String.format(
"[CL] setClassAssertionStatus: Name - %s, enable - %b", cname,
enable));
super.setClassAssertionStatus(cname, enable);
}
@Override
public void setPackageAssertionStatus(String pname, boolean enable) {
// TODO Auto-generated method stub
Log.i(TAG, String.format(
"[CL] setPackageAssertionStatus: Name - %s, enable - %b",
pname, enable));
super.setPackageAssertionStatus(pname, enable);
}
@Override
public void setDefaultAssertionStatus(boolean enable) {
// TODO Auto-generated method stub.
Log.i(TAG, "setDefaultAssertionStatus: " + enable);
super.setDefaultAssertionStatus(enable);
}
@Override
public void clearAssertionStatus() {
// TODO Auto-generated method stub
Log.i(TAG, "clearAssertionStatus");
super.clearAssertionStatus();
}
private void testDynamicLoad() {
Log.i(TAG, "$$$$$$$$$$$$$$$This is my test$$$$$$$$$$$$$$$");
ClassLoader parentcl = getClassLoader();
File libFile = new File(getDir("dex", Context.MODE_PRIVATE), LIB_JAR);
copy2SDCard(libFile);
String dexLibPath = getDir("outdex", Context.MODE_PRIVATE)
.getAbsolutePath();
MyDexClassLoader cl = new MyDexClassLoader(libFile.getAbsolutePath(),
dexLibPath, null, parentcl);
try {
Class dtClz = cl.loadClass("paul.example.thirddex.Lib");
ILib lib = (ILib) dtClz.newInstance();
lib.sayHello();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public class MainActivity extends ActionBarActivity {
Button button;
private String LIB_JAR = "mylib.jar";
static final int BUF_SIZE = 8 * 1024;
static final String TAG = "MCL";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
testDynamicLoad();
}
});
}
@SuppressLint("NewApi")
private void testDynamicLoad() {
Log.i(TAG, "$$$$$$$$$$$$$$$This is my test$$$$$$$$$$$$$$$");
ClassLoader parentcl = getClassLoader();
File libFile = new File(getDir("dex", Context.MODE_PRIVATE), LIB_JAR);
copy2SDCard(libFile);
String dexLibPath = getDir("outdex", Context.MODE_PRIVATE)
.getAbsolutePath();
MyDexClassLoader cl = new MyDexClassLoader(libFile.getAbsolutePath(),
dexLibPath, null, parentcl);
try {
Class dtClz = cl.loadClass("paul.example.thirddex.Lib");
ILib lib = (ILib) dtClz.newInstance();
lib.sayHello();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void copy2SDCard(File dexInternalStoragePath) {
BufferedInputStream bis;
try {
bis = new BufferedInputStream(getAssets().open(LIB_JAR));
OutputStream dexWriter = new BufferedOutputStream(
new FileOutputStream(dexInternalStoragePath));
byte[] buf = new byte[BUF_SIZE];
int len;
while ((len = bis.read(buf, 0, BUF_SIZE)) > 0) {
dexWriter.write(buf, 0, len);
}
dexWriter.close();
bis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
分析:
若工程中沒有刪除Lib.java文件,則該應用的ClassLoader會包含該類,MyDexClassLoader加載Lib Class的時候,會先通過parent ClassLoader來加載該類,也就是該應用的ClassLoade加載,若Lib.java未刪除,則parent ClassLoader會加載成功。
若刪除了Lib.java文件,則parent ClassLoader未加載成功,會用MyDexClassLoader來加載。這也就解釋了上面兩個結果。
另外,若沒有定義接口,則在調用該類方法的時候,必須通過反射來調用,比較麻煩,而定義了接口,就可以轉化爲接口來調用。
有幾項需要注意,
1. 在打包java文件的時候,一定注意不要將接口文件打包進來
2. dexcalssloader的優化目錄一定要是程序私有目錄
3. 若該工程內包含Lib.java,則一定要刪除。也可以新建一個Android項目,包含Lib.java,然後在另一個項目中動態加載Lib。
工程下載地址
http://download.csdn.net/detail/u014088294/9251541