Classloader 分为三种: 1: pathClassloader: Android 用来加载系统文件 和 应用的主文件 2: DexClassloader: Android 用来加载 jar/apk/dex 文件 3: URLClassloader: 可以加载java的jar包,但是Dalvik 虚拟机不支持这种加载方式
public class ProxyActivity extends AppCompatActivity {
private ProxyActivityInterface pluginObj;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//在这里拿到了真实跳转的activity 拿出来 再去启动真实的activity
String className = getIntent().getStringExtra("ClassName");
//通过反射在去启动一个真实的activity 拿到Class对象
try {
Class<?> plugClass = getClassLoader().loadClass(className);
Constructor<?> pluginConstructor = plugClass.getConstructor(new Class[]{});
//因为插件的activity实现了我们的标准
pluginObj = (ProxyActivityInterface) pluginConstructor.newInstance(new Object[]{});
pluginObj.attach(this);//注入上下文
pluginObj.onCreate(new Bundle());//一定要调用onCreate
} catch (Exception e) {
if (e.getClass().getSimpleName() .equals("ClassCastException")){
//我这里是直接拿到异常判断的 ,也可的 拿到上面的plugClass对象判断有没有实现我们的接口
finish();
Toast.makeText(this,"非法页面",Toast.LENGTH_LONG).show();
return;
}
e.printStackTrace();
}
}
]
//为什么要重写这个呢 因为这个是插件内部startactivity调用的 将真正要开启的activity的类名穿过来
//然后取出来,启动我们的占坑的activity 在我们真正要启动的赛进去
@Override
public void startActivity(Intent intent) {
String className1=intent.getStringExtra("ClassName");
Intent intent1 = new Intent(this, ProxyActivity.class);
intent1.putExtra("ClassName", className1);
super.startActivity(intent1);
}
//重写classLoader
@Override
public ClassLoader getClassLoader() {
return HookManager.getInstance().getClassLoader();
}
//重写Resource
@Override
public Resources getResources() {
return HookManager.getInstance().getResource();
}
@Override
protected void onStart() {
super.onStart();
pluginObj.onStart();
}
@Override
protected void onDestroy() {
super.onDestroy();
pluginObj.onDestroy();
}
@Override
protected void onPause() {
super.onPause();
pluginObj.onPause();
}
}
public class HookManager {
private static final HookManager ourInstance = new HookManager();
private Resources resources;
private DexClassLoader loader;
public PackageInfo packageInfo;
public static HookManager getInstance() {
return ourInstance;
}
private HookManager() {
}
//用来加载插件
public void loadPlugin(Activity activity) {
// 假如这里是从网络获取的插件 我们直接从sd卡获取 然后读取到我们的cache目录
String pluginName = "plugin.apk";
File filesDir = activity.getDir("plugin", activity.MODE_PRIVATE);
String filePath = new File(filesDir, pluginName).getAbsolutePath();
File file = new File(filePath);
if (file.exists()) {
file.delete();
}
FileInputStream is = null;
FileOutputStream os = null;
//读取的目录
try {
is = new FileInputStream(new File(Environment.getExternalStorageDirectory(), pluginName));
//要输入的目录
os = new FileOutputStream(filePath);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
int len = 0;
byte[] buffer = new byte[1024];
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
File f = new File(filePath);
if (f.exists()) {
Toast.makeText(activity, "dex overwrite", Toast.LENGTH_SHORT).show();
}
loadPathToPlugin(activity);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
os.close();
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
private void loadPathToPlugin(Activity activity) {
File filesDir = activity.getDir("plugin", activity.MODE_PRIVATE);
String name = "plugin.apk";
String path = new File(filesDir, name).getAbsolutePath();
//然后我们开始加载我们的apk 使用DexClassLoader
File dexOutDir = activity.getDir("dex", activity.MODE_PRIVATE);
loader = new DexClassLoader(path, dexOutDir.getAbsolutePath(), null, activity.getClassLoader());
//通过PackAgemanager 来获取插件的第一个activity是哪一个
PackageManager packageManager = activity.getPackageManager();
packageInfo = packageManager.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES);
//然后开始加载我们的资源 肯定要使用Resource 但是它是AssetManager创建出来的 就是AssertManager 有一个addAssertPath 这个方法 但是私有的 所有使用反射
Class<?> assetManagerClass = AssetManager.class;
try {
AssetManager assertManagerObj = (AssetManager) assetManagerClass.newInstance();
Method addAssetPathMethod = assetManagerClass.getMethod("addAssetPath", String.class);
addAssetPathMethod.setAccessible(true);
addAssetPathMethod.invoke(assertManagerObj, path);
//在创建一个Resource
resources = new Resources(assertManagerObj, activity.getResources().getDisplayMetrics(), activity.getResources().getConfiguration());
} catch (Exception e) {
e.printStackTrace();
}
}
//对外提供插件的classLoader
public ClassLoader getClassLoader() {
return loader;
}
//插件中的Resource
public Resources getResource() {
return resources;
}
}