public
class
MainActivity extends AppCompatActivity {
private static final String TAG
=
"ayuan-log"
;
@Override
protected void onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(TAG,
"onCreate: MainActivity启动"
);
}
public void onClick(View view) {
loadDex();
}
private void loadDex() {
Log.d(TAG,
"loadDex: 开始加载"
);
/
/
1.
拷贝自定义资源(assets)的dex到程序目录下
String dexPath
=
copyDex(
"Main2Activity.dex"
);
/
/
2.
创建一个加载了该Dex文件的Dex类加载器, 可以反射获取其中的类和函数
DexClassLoader dexClassLoader
=
getLoader(dexPath);
/
/
3.
通过反射, 启动dex中的Activity
execClassMethod(
dexClassLoader,
/
/
classLoader
"com.example.myapplication.Main2Activity"
,
/
/
类名
"onCreate"
,
/
/
方法名
this);
/
/
参数: MainActivity.this
}
private void execClassMethod(DexClassLoader dexClassLoader,
/
/
类所属的ClassLoader
String className,
/
/
函数所处的类名
String methodName,
/
/
函数名
Activity as ) {
/
/
函数参数, 此处传入了MainActivity.this 用于显示
try
{
/
/
替换PathClassLoader为自定义classLoader, startActivity 底层会从PathClassLoader寻找加载的类
replaceClassLoader(dexClassLoader);
/
/
获取类类型, 构造Intent
Class clazz
=
dexClassLoader.loadClass(className);
Intent intent
=
new Intent(MainActivity.this, clazz);
startActivity(intent);
/
/
调用构造函数(无参构造), 获取实例 getConstructor(参数类型数组)
/
/
Constructor cons
=
clazz.getConstructor(new Class[]{});
/
/
调用构造方法创建对象, newInstance(参数数组)
/
/
Object
instance
=
cons.newInstance(new
Object
[]{});
/
/
获取成员方法
/
/
Method methodTest
=
clazz.getDeclaredMethod(methodName, new Class[]{Activity.
class
});
/
/
取消java 访问检查
/
/
methodTest.setAccessible(true);
/
/
调用方法 invoke(实例, 方法参数数组)
/
/
methodTest.invoke(instance, new
Object
[]{as});
}catch(Exception e){
e.printStackTrace();
}
}
private void replaceClassLoader(DexClassLoader dexClassLoader) {
try
{
/
/
1.
private static ActivityThread sCurrentActivityThread
=
ActivityThread.currentActivityThread()
/
/
获取类类型
Class clzActivityThread
=
Class.forName(
"android.app.ActivityThread"
);
/
/
获取静态方法
Method methodCurrentActivityThread
=
clzActivityThread.getDeclaredMethod(
"currentActivityThread"
);
/
/
调用静态方法, 返回静态成员变量
Object
sCurrentActivityThread
=
methodCurrentActivityThread.invoke(null, new
Object
[]{});
/
/
2.
private ActivityThread$AppBindData mBoundApplication
=
sCurrentActivityThread 反射 mBoundApplication
/
/
获取类类型
Class clzAppBinData
=
Class.forName(
"android.app.ActivityThread$AppBindData"
);
/
/
获取字段类型
Field fieldBoundApplication
=
clzActivityThread.getDeclaredField(
"mBoundApplication"
);
/
/
获取实例sCurrentActivityThread 对应的字段对象
fieldBoundApplication.setAccessible(true);
Object
mBoundApplication
=
fieldBoundApplication.get(sCurrentActivityThread);
/
/
3.
private LoadedApk info
=
mBoundApplication 反射 info
/
/
获取字段类类型
Class clzLoadedApk
=
Class.forName(
"android.app.LoadedApk"
);
/
/
获取字段类型
Field fieldInfo
=
clzAppBinData.getDeclaredField(
"info"
);
/
/
获取实例mBoundApplication 对应的字段对象
fieldInfo.setAccessible(true);
Object
info
=
fieldInfo.get(mBoundApplication);
/
/
4.
private ClassLoader mClassLoader
=
info 反射 mClassLoader
/
/
获取类类型
Class clzClassLoader
=
Class.forName(
"java.lang.ClassLoader"
);
/
/
获取字段类型
Field fieldClassLoader
=
clzLoadedApk.getDeclaredField(
"mClassLoader"
);
/
/
获取实例mBoundApplication 对应的字段对象
fieldClassLoader.setAccessible(true);
fieldClassLoader.
set
(info, dexClassLoader);
} catch (Exception e) {
e.printStackTrace();
}
}
/
/
创建一个加载了该Dex文件的Dex类加载器, 可以反射获取其中的类和函数
private DexClassLoader getLoader(String dexPath) {
DexClassLoader dexClassLoader
=
new DexClassLoader(
dexPath,
/
/
要加载的Dex文件路径
getCacheDir().toString(),
/
/
优化之后的文件路径
null,
/
/
native 库路径, 由于没有使用到native,null即可
getClassLoader()
/
/
父ClassLoader
);
Log.d(TAG,
"getLoader: "
+
dexClassLoader);
return
dexClassLoader;
}
/
/
拷贝assets
/
文件 到 app
/
files
/
下
private String copyDex(String dexName) {
/
/
获取assets目录管理器
AssetManager as
=
getAssets();
/
/
目的地址
String path
=
getFilesDir()
+
File
.separator
+
dexName;
/
/
/
data
/
user
/
0
/
com.example.myapplication
/
files
/
Main2Activity.dex
Log.d(TAG,
"copyDex: path: "
+
path);
/
/
将文件拷贝到目的地址
try
{
/
/
创建文件流
FileOutputStream out
=
new FileOutputStream(path);
/
/
打开文件 assets
/
dexName
InputStream
is
=
as.
open
(dexName);
/
/
循环读取并写入文件
byte[]
buffer
=
new byte[
1024
];
int
len
=
0
;
while
((
len
=
is
.read(
buffer
))!
=
-
1
){
out.write(
buffer
,
0
,
len
);
}
/
/
关闭文件
out.close();
}catch(Exception e){
e.printStackTrace();
return
"";
}
return
path;
}
}