目前插件化加载库VirtualApk使用的就是这种原理,使用很方便,并且功能很强大
原理:
startActivity —> PluginActivity – (Hook ProxyActivity)(AMS)检测,当前要启动的Activity是否注册了)ok —》
ActivityThread(即将加载启动Activity)----(要把这个ProxyActivity 换回来 --> PluginActivity)
这里使用了两次Hook,最后启动PluginActivity的时候也会报错,因为插件并没有安装,这时候就需要将插件和宿主app合在一起加载,所以需要三部
1、绕过AMS检测,使用ProxyActivity替换PluginActivity
2、将ProxyActivity换回PluginActivity
3、将两个apk文件一起加载
tips:仍然需要申请权限,否则插件无法启动
下面是具体步骤
1、创建HookApplication
public class HookApplication extends Application {
public static String pluginPath;
@Override
public void onCreate() {
super.onCreate();
pluginPath = getPluginPath(this,"plugin.apk");
try {
hookAMSAction();
} catch (Exception e) {
e.printStackTrace();
}
try {
hookLaunchActivity();
} catch (Exception e) {
e.printStackTrace();
}
try {
pluginToAppAtion();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 将assets中的文件(plugin-debug.apk)拷贝到app的缓存目录,然后返回拷贝之后文件的路径
* @param context
* @param fileName
* @return 例如/data/user/0/com.netease.pluginhookandroid9/cache/plugin-debug.apk
*/
private String getPluginPath(Context context, String fileName) {
File cacheDir = context.getCacheDir();
if (!cacheDir.exists()){
cacheDir.mkdirs();//没有缓存目录,就创建目录
}
File outPath = new File(cacheDir,fileName);//创建输出的文件位置
if (outPath.exists()){
outPath.delete(); //如果已经存在了就删除
}
InputStream is = null;
FileOutputStream fos = null;
try {
boolean newFile = outPath.createNewFile();
if (newFile){
is = context.getAssets().open(fileName);
fos = new FileOutputStream(outPath);
byte[] buf = new byte[is.available()];
int byteCount;
while ((byteCount = is.read())!=-1){
fos.write(buf,0,byteCount);
}
return outPath.getAbsolutePath();
}
}catch (Exception e){
e.printStackTrace(