package zeus.plugin;
import android.app.Activity;
import android.app.Application;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.text.TextUtils;
import android.view.LayoutInflater;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import dalvik.system.DexClassLoader;
/**
* 插件管理类,管理插件的初始化、安装、卸载、加载等等
* 所有的方法和对象都为静态。
* <p/>
* Created by huangjian on 2016/6/21.
*/
public class PluginManager {
private static final String PLUGIN_INSTALLED_LIST_FILE_NAME = "plugin.installedlist"; //已安装插件列表的文件地址
/*start---这些对象的强引用系统也会一直持有,所以这里不会有内存泄漏---start*/
public static volatile ClassLoader mNowClassLoader = null; //正在使用的ClassLoader
public static volatile ClassLoader mBaseClassLoader = null; //系统原始的ClassLoader
public static volatile Resources mNowResources; //正在使用的Resources
public static volatile Resources mBaseResources; //原始的resources
public static volatile Context mBaseContext; //原始的application中的BaseContext,不能是其他的,否则会内存泄漏
private static Object mPackageInfo = null; //ContextImpl中的LoadedAPK对象mPackageInfo
/*end---这些对象的强引用系统也会一直持有,所以这里不会有内存泄漏---end*/
private static HashMap<String, PluginManifest> mInstalledPluginList = null; //已安装的插件列表
private static HashMap<String, PluginManifest> mLoadedPluginList = null; //已加载的插件列表
private static HashMap<String, String> mLoadedDiffPluginPathinfoList = null;
private static final Object mInstalledPluginListLock = new Object(); //插件安装的锁,支持多线程安装
private static final Object mLoadedPluginListLock = new Object(); //修改已加载的插件对象的锁
private static final Object mLoadLock = new Object(); //插件加载的锁,支持多线程加载
private static boolean isIniteInstallPlugins = false; //插件是否已经初始化
/**
* 缓存插件的对象
*/
private static HashMap<String, ZeusPlugin> mPluginMap = new HashMap<>();
/**
* 得在插件相关的方法调用之前调用
*
* @param application application
*/
public static void init(Application application) {
//初始化一些成员变量和加载已安装的插件
mPackageInfo = PluginUtil.getField(application.getBaseContext(), "mPackageInfo");
mBaseContext = application.getBaseContext();
mNowClassLoader = mBaseContext.getClassLoader();
mBaseClassLoader = mBaseContext.getClassLoader();
mNowResources = mBaseContext.getResources();
mBaseResources = mNowResources;
//更改系统的Instrumentation对象,以便创建插件的activity
Object mMainThread = PluginUtil.getField(mBaseContext, "mMainThread");
PluginUtil.setField(mMainThread, "mInstrumentation", new ZeusInstrumentation());
//创建插件的相关文件夹目录
createPath();
//加载已安装过的插件
loadInstalledPlugins();
//清除老版本的插件,最好放到软件退出时调用,防止让启动速度变慢
clearOldPlugin();
//安装内置插件
Thread initPluginThread = new Thread(new Runnable() {
@Override
public void run() {
installInitPlugins();
}
});
initPluginThread.setName("initPluginThread");
initPluginThread.start();
}
private static void createPath() {
PluginUtil.createDir(PluginUtil.getInsidePluginPath());
PluginUtil.createDir(PluginUtil.getDexCacheParentDirectPath());
}
/**
* 安装初始的内置插件
*/
private static void installInitPlugins() {
HashMap<String, PluginManifest> installedList = getInstalledPlugin();
HashMap<String, Integer> defaultList = getDefaultPlugin();
for (String key : defaultList.keySet()) {
int installVersion = -1;
int defaultVersion = defaultList.get(key);
if (installedList.containsKey(key)) {
installVersion = installedList.get(key).getVersion();
}
ZeusPlugin plugin = getPlugin(key);
if (defaultVersion > installVersion) {
boolean ret = plugin.installAssetPlugin();
//提前将dex文件优化为odex或者opt文件
if (ret) {
try {
new DexClassLoader(PluginUtil.getAPKPath(key), PluginUtil.getDexCacheParentDirectPath(), null, mBaseClassLoader.getParent());
} catch (Throwable e) {
e.printStackTrace();
}
}
}
}
}
/**
* 获取已安装的插件列表
*
* @return 已安装的插件列表
*/
public static HashMap<String, PluginManifest> getInstalledPlugin() {
if (mInstalledPluginList != null) {
return mInstalledPluginList;
} else {
return mInstalledPluginList = readInstalledPlugin();
}
}
/**
* 获取某插件是否安装
*
* @param pluginId 插件ID
* @return 某插件是否安装
*/
public static boolean isInstall(String pluginId) {
return isInstall(pluginId, -1);
}
/**
* 获取某插件是否安装
*
* @param pluginId 插件ID
* @return 某插件是否安装
*/
public static boolean isInstall(String pluginId, int version) {
getInstalledPlugin();
return mInstalledPluginList != null && mInstalledPluginList.containsKey(pluginId) && mInstalledPluginList.get(pluginId).getVersion() >= version;
}
/**
* 获取已经被加载到系统中的插件列表
*
* @return 已经被加载到系统中的插件列表
*/
public static HashMap<String, PluginManifest> getLoadedPlugin() {
return mLoadedPluginList;
}
/**
* 判断某个插件是否已经被加载
*
* @param pluginId 插件ID
* @return 某个插件是否已经被加载
*/
public static boolean isLoaded(String pluginId) {
return mLoadedPluginList != null && mLoadedPluginList.containsKey(pluginId);
}
private static void putLoadedPlugin(String pluginId, PluginManifest pluginManifest, String pathinfo) {
synchronized (mLoadedPluginListLock) {
if (mLoadedPluginList == null) {
mLoadedPluginList = new HashMap<>();
}
if(mLoadedDiffPluginPathinfoList == null){
mLoadedDiffPluginPathinfoList = new HashMap<>();
}
mLoadedPluginList.put(pluginId, pluginManifest);
mLoadedDiffPluginPathinfoList.put(pluginId, pathinfo);
}
}
/**
* 获取内置插件列表
*
* @return 内置插件列表
*/
public static HashMap<String, Integer> getDefaultPlugin() {
return PluginConfig.mDefaultList;
}
private static HashMap<String, PluginManifest> readInstalledPlugin() {
synchronized (mInstalledPluginListLock) {
HashMap<String, PluginManifest> pluginList = new HashMap<>();
String path = getInstalledPluginListFilePath();
File file = new File(path);
if (file.exists()) {
try {
InputStream inputStream = new FileInputStream(file);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buffer = new byte[512];
int length;
int count = 0;
while ((length = inputStream.read(buffer, 0, 512)) > 0) {
bos.write(buffer, 0, length);
count += length;
}
if (count > 0) {
String result = new String(bos.toByteArray(), "UTF-8");
PluginUtil.close(inputStream);
PluginUtil.close(bos);
JSONArray jObject = new JSONArray(result);
int jLength = jObject.length();
for (int i = 0; i < jLength; i++) {
JSONObject f = jObject.getJSONObject(i);
String id = f.optString("id", "");
//兼容老版本,新版本中version字段已不存在
int version = f.optInt("version", 1);
String meta = f.optString("meta","");
PluginManifest pluginManifest;
if(!TextUtils.isEmpty(meta)){
pluginManifest = new PluginManifest(meta);
}else{
pluginManifest = new PluginManifest();
pluginManifest.version = String.valueOf(version);
pluginManifest.name = id;
}
if (!TextUtils.isEmpty(id)) {
pluginList.put(id, pluginManifest);
}
}
}
PluginUtil.close(inputStream);
PluginUtil.close(bos);
} catch (Exception e) {
e.printStackTrace();
return pluginList;
}
}
return pluginList;
}
}
protected static boolean addInstalledPlugin(String pluginId, PluginManifest pluginManifest) {
if (getInstalledPlugin() != null) {
synchronized (mInstalledPluginListLock) {
mInstalledPluginList.put(pluginId, pluginManifest);
}
save();
return true;
}
return false;
}
private static boolean save() {
synchronized (mInstalledPluginListLock) {
if (mInstalledPluginList == null) return true;
FileOutputStream out = null;
try {
JSONArray array = new JSONArray();
for (String key : mInstalledPluginList.keySet()) {
//新版本中不再使用此字段
// int version = mInstalledPluginList.get(key).getVersion();
JSONObject obj = new JSONObject();
obj.put("id", key);
// obj.put("version", version);
obj.put("meta",mInstalledPluginList.get(key).toString());
array.put(obj);
}
String result = array.toString();
String path = getInstalledPluginListFilePath();
File file = new File(path);
if (!file.exists()) {
file.createNewFile();
}
out = new FileOutputStream(file);
out.write(result.getBytes());
} catch (Exception e) {
e.printStackTrace();
return false;
} finally {
PluginUtil.close(out);
}
}
return true;
}
private static String getInstalledPluginListFilePath() {
return PluginUtil.getInsidePluginPath() + PLUGIN_INSTALLED_LIST_FILE_NAME;
}
/**
* 加载某个插件,如果已经加载了直接返回
* 如果已经加载了老版本插件,而新版本插件也安装了,此方法也不会加载最新版本的插件
*
* @param pluingId 插件的ID
* @return 是否加载成功
*/
public static boolean loadPlugin(String pluingId) {
return loadLastVersionPlugin(pluingId);
}
/**
* 加载最新版本的插件,如果老版本的该插件已经被加载过,则清除之前的版本后再加载最新版本。
* 此方法不可在插件的使用过程中调用,只能在插件运行之前,或插件退出之后。
* 如果运行过程中可能会出现class不一致的情况,但也不是一定的,有的插件还是可以运行时调用的,
* 但通常生效都得下次进入插件时才生效,但不必退出软件再进才生效
*
* @param pluingId 插件的ID
* @return 是否加载成功
*/
public static boolean loadLastVersionPlugin(String pluingId) {
ZeusPlugin plugin = getPlugin(pluingId);
PluginManifest meta = plugin.getPluginMeta();
int version = -1;
if (meta != null) {
version = Integer.valueOf(meta.version);
}
return loadPlugin(pluingId, version);
}
/**
* 加载指定版本的插件
*
* @param pluginId 插件ID
* @param version 要加载插件的版本号
* @return 是否加载成功
*/
public static boolean loadPlugin(String pluginId, int version) {
synchronized (mLoadLock) {
if (getLoadedPlugin() != null && getLoadedPlugin().get(pluginId) != null && getLoadedPlugin().get(pluginId).getVersion() >= version) {
return true;
}
String pathInfo = PluginUtil.getInstalledPathInfo(pluginId);
String pluginApkPath = PluginUtil.getAPKPath(pluginId, pathInfo);
ZeusPlugin plugin = getPlugin(pluginId);
if (!PluginUtil.exists(pluginApkPath)) {
if (getDefaultPlugin().containsKey(pluginId)) {
if (!plugin.installAssetPlugin()) {
return false;
} else {
pluginApkPath = PluginUtil.getAPKPath(pluginId);
}
} else {
return false;
}
}
PluginManifest meta = plugin.getPluginMeta();
if (meta == null || Integer.valueOf(meta.version) < version || !isSupport(pluginId)) return false;
ClassLoader cl = mNowClassLoader;
if (PluginUtil.isHotFix(pluginId)) {
loadHotfixPluginClassLoader(pluginId);
} else {
//如果一个老版本的插件已经被加载了,则需要先移除
if (getLoadedPlugin() != null && getLoadedPlugin().containsKey(pluginId)) {
if (cl instanceof ZeusClassLoader) {
ZeusClassLoader classLoader = (ZeusClassLoader) cl;
//移除老版本的插件
classLoader.removePlugin(pluginId);
//添加新版本的插件
classLoader.addAPKPath(pluginId, pluginApkPath, PluginUtil.getLibFileInside(pluginId));
}
} else {
if (cl instanceof ZeusClassLoader) {
ZeusClassLoader classLoader = (ZeusClassLoader) cl;
classLoader.addAPKPath(pluginId, pluginApkPath, PluginUtil.getLibFileInside(pluginId));
} else {
ZeusClassLoader classLoader = new ZeusClassLoader(cl);
classLoader.addAPKPath(pluginId, pluginApkPath, PluginUtil.getLibFileInside(pluginId));
PluginUtil.setField(mPackageInfo, "mClassLoader", classLoader);
Thread.currentThread().setContextClassLoader(classLoader);
mNowClassLoader = classLoader;
}
}
putLoadedPlugin(pluginId, meta, pathInfo);
}
clearViewConstructorCache();
if (!PluginUtil.isHotfixWithoutResFile(pluginId)) {
reloadInstalledPluginResources();
}
}
return true;
}
private static boolean isSupport(String pluginId){
int versionCode = -1;
try {
versionCode = mBaseContext.getPackageManager().getPackageInfo(mBaseContext.getPackageName(), 0).versionCode;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
PluginManifest manifest = getPlugin(pluginId).getPluginMeta();
int maxVersion = TextUtils.isEmpty(manifest.maxVersion) ? Integer.MAX_VALUE : Integer.valueOf(manifest.maxVersion);
int minVersion = TextUtils.isEmpty(manifest.minVersion) ? -1 : Integer.valueOf(manifest.minVersion);
//如果宿主不支持该插件或补丁则不加载
return !(versionCode != -1 &&
(versionCode > maxVersion ||
versionCode < minVersion));
}
/**
* 系统会记录当前已经加载过的view的所有的构造函数,避免反射,提高性能
* 但是我们插件一旦更新后,view的构造函数就已经无效了,所以要清理这些缓存
*/
private static void clearViewConstructorCache() {
try {
Field field = LayoutInflater.class.getDeclaredField("sConstructorMap");
field.setAccessible(true);
Map map = (Map) field.get(null);
map.clear();
} catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException e1) {
e1.printStackTrace();
}
}
private static void clearCacheObject(Object drawableCache) {
if (drawableCache == null) return;
Method clearMethod = PluginUtil.getMethod(drawableCache.getClass(), "clear");
if (clearMethod != null) {
try {
clearMethod.invoke(drawableCache);
} catch (Exception ignored) {
}
}
//高android版本走这里
Object mThemedEntries = PluginUtil.getField(drawableCache, "mThemedEntries");
if (mThemedEntries != null) {
clearMethod = PluginUtil.getMethod(mThemedEntries.getClass(), "clear");
if (clearMethod != null) {
try {
clearMethod.invoke(mThemedEntries);
} catch (Exception ignored) {
}
}
}
}
/**
* 清除resources中的图片缓存,降低内存消耗的一个办法
* 不调用也不会产生严重问题
*
* @param resouces resouces
*/
private static void clearResoucesDrawableCache(Resources resouces) {
resouces.finishPreloading();
resouces.flushLayoutCache();
clearCacheObject(PluginUtil.getField(resouces, "mDrawableCache"));
clearCacheObject(PluginUtil.getField(resouces, "mColorDrawableCache"));
clearCacheObject(PluginUtil.getField(resouces, "mColorStateListCache"));
clearCacheObject(PluginUtil.getField(resouces, "mAnimatorCache"));
clearCacheObject(PluginUtil.getField(resouces, "mStateListAnimatorCache"));
}
/**
* 加载所有已安装的插件的资源,并清除资源中的缓存
* 一旦插件发现变化,则使用一个新的Resources
* 这样之前resources的缓存都不会对新resources产生影响
*/
private static void reloadInstalledPluginResources() {
try {
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = AssetManager.class.getMethod("addAssetPath", String.class);
addAssetPath.invoke(assetManager, mBaseContext.getPackageResourcePath());
if (mLoadedPluginList != null && mLoadedPluginList.size() != 0) {
//每个插件的packageID都不能一样
for (String id : mLoadedPluginList.keySet()) {
//只有带有资源的补丁才会执行添加到assetManager中
if (!PluginUtil.isHotfixWithoutResFile(id)) {
addAssetPath.invoke(assetManager, PluginUtil.getAPKPath(id, mLoadedDiffPluginPathinfoList.get(id)));
}
}
}
//这里提前创建一个resource是因为Resources的构造函数会对AssetManager进行一些变量的初始化
//还不能创建系统的Resources类,否则中兴系统会出现崩溃问题
PluginResources newResources = new PluginResources(assetManager,
mBaseContext.getResources().getDisplayMetrics(),
mBaseContext.getResources().getConfiguration());
PluginUtil.setField(mBaseContext, "mResources", newResources);
//这是最主要的需要替换的,如果不支持插件运行时更新,只留这一个就可以了
PluginUtil.setField(mPackageInfo, "mResources", newResources);
//清除一下之前的resource的数据,释放一些内存
//因为这个resource有可能还被系统持有着,内存都没被释放
clearResoucesDrawableCache(mNowResources);
mNowResources = newResources;
//需要清理mtheme对象,否则通过inflate方式加载资源会报错
//如果是activity动态加载插件,则需要把activity的mTheme对象也设置为null
PluginUtil.setField(mBaseContext, "mTheme", null);
} catch (Throwable e) {
e.printStackTrace();
}
}
/**
* 清除已经加载过的插件
*
* @param pluginId 插件id
*/
private static void removeLoadedPlugin(String pluginId) {
synchronized (mLoadLock) {
if (!isLoaded(pluginId)) return;
if (mLoadedPluginList == null) return;
mLoadedPluginList.remove(pluginId);
clearViewConstructorCache();
if (mLoadedPluginList.size() == 0) {
PluginUtil.setField(mPackageInfo, "mClassLoader", mBaseClassLoader);
Thread.currentThread().setContextClassLoader(mBaseClassLoader);
mNowClassLoader = mBaseClassLoader;
Thread.currentThread().setContextClassLoader(mBaseClassLoader);
} else {
ClassLoader cl = mNowClassLoader;
if (!(cl instanceof ZeusClassLoader)) return;
ZeusClassLoader classLoader = (ZeusClassLoader) cl;
classLoader.removePlugin(pluginId);
}
reloadInstalledPluginResources();
}
}
/**
* 从安装列表中删除一个插件,并从内存中清除加载的插件
*
* @param pluginId 插件ID
* @return true表明删除成功,false表明出现了异常
*/
protected static boolean unInstalledPlugin(String pluginId) {
removeLoadedPlugin(pluginId);
if (getInstalledPlugin() != null) {
synchronized (mInstalledPluginListLock) {
mInstalledPluginList.remove(pluginId);
}
save();
return true;
}
return false;
}
/**
* 清除老版本的插件,软件一启动或者软件退出时调用最佳
*/
public static void clearOldPlugin() {
HashMap<String, PluginManifest> map = getInstalledPlugin();
if (map != null) {
for (String key : map.keySet()) {
ZeusPlugin plugin = getPlugin(key);
plugin.clearOldPlugin();
}
}
}
private synchronized static void loadHotfixPluginClassLoader(String pluginId) {
//如果不是补丁,或者该补丁已经加载过,则不做处理
if (!PluginUtil.isHotFix(pluginId) ||
(getLoadedPlugin() != null && getLoadedPlugin().containsKey(pluginId))) {
return;
}
HashMap<String, PluginManifest> installedPluginMaps = getInstalledPlugin();
//如果开启了instant run,则需要更改为mBaseClassLoader.getParent();
ClassLoader orgClassLoader = mBaseClassLoader;
//以下这段是补丁框架为了兼容android studio 2.0版本以上在debug时的instant run功能,在打正式包的时候请删除这段无用代码
//---start----
if (mBaseClassLoader.getParent().getClass().getSimpleName().equals("IncrementalClassLoader")) {
orgClassLoader = mBaseClassLoader.getParent();
}
//---end----
ClassLoader classLoader = orgClassLoader.getParent();
ZeusHotfixClassLoader hotfixClassLoader;
String pathInfo = PluginUtil.getInstalledPathInfo(pluginId);
if (classLoader instanceof ZeusHotfixClassLoader) {
hotfixClassLoader = (ZeusHotfixClassLoader) classLoader;
hotfixClassLoader.addAPKPath(PluginUtil.getAPKPath(pluginId, pathInfo),
PluginUtil.getLibFileInside(pluginId));
} else {
hotfixClassLoader = new ZeusHotfixClassLoader(PluginUtil.getAPKPath(pluginId, pathInfo),
PluginUtil.getDexCacheParentDirectPath(),
PluginUtil.getLibFileInside(pluginId),
classLoader);
hotfixClassLoader.setOrgAPKClassLoader(orgClassLoader);
PluginUtil.setField(orgClassLoader, "parent", hotfixClassLoader);
}
putLoadedPlugin(pluginId, installedPluginMaps.get(pluginId), pathInfo);
}
private static void loadInstalledPlugins() {
synchronized (mLoadLock) {
if (isIniteInstallPlugins) {
return;
}
HashMap<String, PluginManifest> installedPluginMaps = getInstalledPlugin();
if (installedPluginMaps.isEmpty()) {
isIniteInstallPlugins = true;
return;
}
//获取classloader设置classloader
ZeusClassLoader classLoader = null;
boolean isNeedLoadResource = false;
for (String pluginId : installedPluginMaps.keySet()) {
if(!isSupport(pluginId))continue;
if (PluginUtil.isPlugin(pluginId)) {
if (classLoader == null) {
classLoader = new ZeusClassLoader(mBaseContext.getClassLoader());
}
String pathInfo = PluginUtil.getInstalledPathInfo(pluginId);
classLoader.addAPKPath(pluginId, PluginUtil.getAPKPath(pluginId, pathInfo), PluginUtil.getLibFileInside(pluginId));
putLoadedPlugin(pluginId, installedPluginMaps.get(pluginId), pathInfo);
isNeedLoadResource = true;
}
if (PluginUtil.isHotFix(pluginId)) {
loadHotfixPluginClassLoader(pluginId);
isNeedLoadResource = true;
}
}
clearViewConstructorCache();
if (!isNeedLoadResource) {
isIniteInstallPlugins = true;
return;
}
//设置原始APK所使用的ClassLoader
if (classLoader != null) {
PluginUtil.setField(mPackageInfo, "mClassLoader", classLoader);
Thread.currentThread().setContextClassLoader(classLoader);
mNowClassLoader = classLoader;
}
reloadInstalledPluginResources();
isIniteInstallPlugins = true;
}
}
/**
* Application中以及activity中的getResources()方法都应该调用这个方法
*
* @return 返回正在使用的resources
*/
public static Resources getResources() {
return mNowResources;
}
/**
* 获取最新插件对象,不存在就生成一个
*
* @param pluginId 插件的名称
* @return 获取某个插件对象
*/
public static ZeusPlugin getPlugin(String pluginId) {
ZeusPlugin plugin = null;
try {
plugin = mPluginMap.get(pluginId);
if (plugin != null) {
return plugin;
}
if (PluginUtil.iszeusPlugin(pluginId)) {
plugin = new ZeusPlugin(pluginId);
}
mPluginMap.put(pluginId, plugin);
} catch (Exception e) {
e.printStackTrace();
}
return plugin;
}
public static void startActivity(Activity activity, Intent intent) {
ComponentName componentName = intent.getComponent();
intent.setClassName(componentName.getPackageName(), PluginConfig.PLUGIN_ACTIVITY_FOR_STANDARD);
intent.putExtra(PluginConfig.PLUGIN_REAL_ACTIVITY, componentName.getClassName());
activity.startActivity(intent);
}
public static void startActivity(Intent intent) {
ComponentName componentName = intent.getComponent();
intent.setClassName(componentName.getPackageName(), PluginConfig.PLUGIN_ACTIVITY_FOR_STANDARD);
intent.putExtra(PluginConfig.PLUGIN_REAL_ACTIVITY, componentName.getClassName());
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mBaseContext.startActivity(intent);
}
}