package zeus.plugin;
import android.content.res.AssetManager;
import android.text.TextUtils;
import org.json.JSONObject;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
/**
* 插件类,包括插件的安装、卸载、清除
* Created by huangjian on 2016/6/21.
*/
public class ZeusPlugin {
private String mPluginId; //使用插件的安装目录作为插件id
private String mInstalledPathInfo = ""; //安装插件的随机路径信息
private boolean isInstalling = false;
private boolean isAssetInstalling = false;
protected ZeusPlugin(String pluginId) {
mPluginId = pluginId;
}
public synchronized boolean install() {
isInstalling = true;
//创建插件安装目录
PluginUtil.createDir(PluginUtil.getPlugDir(mPluginId));
//将当前时间记录为插件的随机数,等效于android系统后面~1、~2等
mInstalledPathInfo = String.valueOf(System.currentTimeMillis());
//获取插件apk文件
String path = PluginUtil.getZipPath(mPluginId);
//插件文件不存在,则安装asset中的默认插件。
if (!PluginUtil.exists(path)) {
if (PluginUtil.iszeusPlugin(mPluginId)) {
isInstalling = false;
mInstalledPathInfo = getInstalledPathInfoNoCache();
return false;
}
return installAssetPlugin();
}
//把下载路径下的插件文件,直接重命名到安装目录,不需要耗时的拷贝过程。
boolean ret = PluginUtil.rename(PluginUtil.getZipPath(mPluginId), getAPKPath(mPluginId));
if (!ret) {
isInstalling = false;
mInstalledPathInfo = getInstalledPathInfoNoCache();
return false;
}
//拷贝so文件,一些插件是没有so文件,而这个方法耗时还稍微高点,所以对于没有so的插件和补丁是不会拷贝的。
if (!PluginUtil.isHotfixWithoutSoFile(mPluginId) &&
!PluginUtil.isPluginWithoutSoFile(mPluginId) &&
!copySoFile(mInstalledPathInfo, PluginUtil.getCpuArchitecture())) {
isInstalling = false;
mInstalledPathInfo = getInstalledPathInfoNoCache();
return false;
}
//校验是否下载的是正确文件,如果插件下载错误则获取这个配置文件就会失败。
PluginManifest meta = getPluginMeta();
if (meta == null) {
PluginUtil.deleteFile(new File(getAPKPath(mPluginId)));
mInstalledPathInfo = getInstalledPathInfoNoCache();
isInstalling = false;
return false;
}
if (!PluginUtil.writePathInfo(mPluginId, mInstalledPathInfo)) {
isInstalling = false;
mInstalledPathInfo = getInstalledPathInfoNoCache();
return false;
}
PluginManager.addInstalledPlugin(mPluginId, meta);
isInstalling = false;
return true;
}
/**
* 安装assets中的插件,assets中插件的文件名要为 mPluginId +".apk"
*
* @return 是否成功
*/
public boolean installAssetPlugin() {
PluginManifest meta;
synchronized (this) {
isAssetInstalling = true;
if (PluginManager.isInstall(mPluginId)) {
isInstalling = false;
return true;
}
PluginUtil.createDir(PluginUtil.getPlugDir(mPluginId));
mInstalledPathInfo = String.valueOf(System.currentTimeMillis());
FileOutputStream out = null;
InputStream in = null;
try {
AssetManager am = PluginManager.mBaseResources.getAssets();
in = am.open(mPluginId + PluginConfig.PLUGIN_SUFF);
PluginUtil.createDirWithFile(getAPKPath(mPluginId));
out = new FileOutputStream(getAPKPath(mPluginId), false);
byte[] temp = new byte[2048];
int len;
while ((len = in.read(temp)) > 0) {
out.write(temp, 0, len);
}
} catch (Exception e) {
e.printStackTrace();
mInstalledPathInfo = getInstalledPathInfoNoCache();
isInstalling = false;
return false;
} finally {
PluginUtil.close(in);
PluginUtil.close(out);
}
//拷贝so文件
if (!copySoFile(mInstalledPathInfo, PluginUtil.getCpuArchitecture())) {
isInstalling = false;
mInstalledPathInfo = getInstalledPathInfoNoCache();
return false;
}
meta = getPluginMeta();
if (meta == null) {
PluginUtil.deleteFile(new File(getAPKPath(mPluginId)));
isAssetInstalling = false;
mInstalledPathInfo = getInstalledPathInfoNoCache();
return false;
}
if (!PluginUtil.writePathInfo(mPluginId, mInstalledPathInfo)) {
isAssetInstalling = false;
mInstalledPathInfo = getInstalledPathInfoNoCache();
return false;
}
isAssetInstalling = false;
}
PluginManager.addInstalledPlugin(mPluginId, meta);
return true;
}
/**
* 清除之前版本的旧数据
*/
public synchronized void clearOldPlugin() {
if (getInstalledPathInfo() == null || isAssetInstalling || isInstalling) return;
File pluginDir = new File(PluginUtil.getPlugDir(mPluginId));
String installedPathInfo = getInstalledPathInfoNoCache();
if (TextUtils.isEmpty(installedPathInfo)) return;
if (pluginDir.exists() && pluginDir.isDirectory()) {
File[] list = pluginDir.listFiles();
if (list == null) return;
for (File f : list) {
String fileFullName = f.getName();
if (fileFullName.endsWith(PluginConfig.PLUGIN_JAR_SUFF) || fileFullName.endsWith(PluginConfig.PLUGIN_SUFF)) {
String fileName = fileFullName.substring(0, fileFullName.lastIndexOf("."));
if (!fileName.equalsIgnoreCase(installedPathInfo)) {
f.delete();
File dir = new File(f.getParent() + "/" + fileName);
PluginUtil.deleteDirectory(dir);
File cacheFile = new File(PluginUtil.getDexCacheFilePath(fileName));
if (cacheFile.exists()) {
cacheFile.delete();
}
}
}
}
}
}
/**
* 将插件中的lib库拷贝入手机内存中。
* 根据当前手机cpu的类型拷贝合适的lib库入手机内存中
*
* @param installedPathInfo 安装的随机路径信息
* @param cpuType CPU_AMR:1 CPU_X86:2 CPU_MIPS:3 具体见{@link PluginUtil}中的getCpuArchitecture()和getLibFile()方法
* @return 是否成功
*/
protected boolean copySoFile(String installedPathInfo, int cpuType) {
String insideLibPath = PluginUtil.getInsidePluginPath() + mPluginId + "/" + installedPathInfo + "/";
PluginUtil.createDir(insideLibPath);
String apkLibPath = PluginUtil.getLibFile(cpuType);
//首先将apk中libs文件夹下的一级so文件拷贝
return PluginUtil.unzipFile(getAPKPath(mPluginId), insideLibPath, apkLibPath);
}
/**
* 获取插件已经安装的apk路径
*
* @param pluginName 插件id
* @return 插件已经安装的apk路径
*/
public String getAPKPath(String pluginName) {
return PluginUtil.getPlugDir(pluginName) + getInstalledPathInfo() + PluginConfig.PLUGIN_SUFF;
}
/**
* 获取当前安装的随机路径信息,不使用缓存,直接读取文件
*
* @return 当前安装的随机路径信息
*/
public String getInstalledPathInfoNoCache() {
return PluginUtil.getInstalledPathInfo(mPluginId);
}
/**
* 获取插件清单文件信息,不使用缓存,读取速度很快
*
* @return 插件清单文件信息
*/
public PluginManifest getPluginMeta() {
PluginManifest meta = null;
String result = readMeta();
if (!TextUtils.isEmpty(result)) {
meta = parserMeta(result);
}
return meta;
}
/**
* 解析清单文件
*
* @param metaString meta字符串
* @return PluginManifest对象
*/
private PluginManifest parserMeta(String metaString) {
PluginManifest meta = new PluginManifest();
try {
JSONObject jObject = new JSONObject(metaString.replaceAll("\r|\n", ""));
meta.name = jObject.optString(PluginManifest.PLUG_NAME);
meta.minVersion = jObject.optString(PluginManifest.PLUG_MIN_VERSION);
meta.maxVersion = jObject.optString(PluginManifest.PLUG_MAX_VERSION);
meta.version = jObject.optString(PluginManifest.PLUG_VERSION);
meta.mainClass = jObject.optString(PluginManifest.PLUG_MAINCLASS);
meta.otherInfo = jObject.optString(PluginManifest.PLUG_OTHER_INFO);
} catch (Exception e) {
e.printStackTrace();
return null;
}
return meta;
}
/**
* 卸载某个插件,通常情况下不需要卸载,除非需要显示调用
*
* @return 是否成功
*/
public boolean uninstall() {
try {
PluginManager.unInstalledPlugin(mPluginId);
//删除手机内存中/data/data/packageName/plugins/mPluginName下的文件
File baseModulePathF = new File(PluginUtil.getPlugDir(mPluginId));
PluginUtil.deleteDirectory(baseModulePathF);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 读取meta文件
*
* @return meta字符串
*/
private String readMeta() {
return PluginUtil.readZipFileString(getAPKPath(mPluginId), PluginConfig.PLUGINWEB_MAINIFEST_FILE);
}
/**
* 获取当前安装的随机路径信息,有缓存则使用缓存
*
* @return 当前安装的随机路径信息
*/
private String getInstalledPathInfo() {
if (!TextUtils.isEmpty(mInstalledPathInfo)) {
return mInstalledPathInfo;
}
mInstalledPathInfo = PluginUtil.getInstalledPathInfo(mPluginId);
return mInstalledPathInfo;
}
}