Java源码示例:com.qihoo360.replugin.model.PluginInfo
示例1
private void install(){
AssetManager assets = this.getAssets();
// String path = "file:///android_asset/app-release.apk";
String dest = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator+ "app-release.apk";
PluginInfo pluginInfo = RePlugin.install(dest);
Log.e(TAG,"pluginInfo:"+pluginInfo);
if (pluginInfo != null) {
Toast.makeText(MainActivity.this, "插件安装成功",Toast.LENGTH_SHORT).show();
boolean preload = RePlugin.preload(pluginInfo);
if (preload){
Toast.makeText(MainActivity.this, "预加载完成", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this, "插件安装失败",Toast.LENGTH_SHORT).show();
}
}
}
示例2
/**
* @param action
*/
private final void registerReceiverAction(final String action) {
IntentFilter filter = new IntentFilter(action);
LocalBroadcastManager.getInstance(mContext).registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (action.equals(intent.getAction())) {
PluginInfo info = intent.getParcelableExtra("obj");
if (info != null) {
switch (action) {
case ACTION_NEW_PLUGIN:
// 非常驻进程上下文
newPluginFound(info, intent.getBooleanExtra(RePluginConstants.KEY_PERSIST_NEED_RESTART, false));
break;
case ACTION_UNINSTALL_PLUGIN:
pluginUninstalled(info);
break;
}
}
}
}
}, filter);
}
示例3
/**
* 模拟安装外置插件
* 注意:为方便演示,外置插件临时放置到Host的assets/external目录下,具体说明见README</p>
*
* @param pluginName 待安装的插件名
*/
private void simulateInstallExternalPlugin(String pluginName) {
PluginInfo info = RePlugin.getPluginInfo(pluginName);
if (info == null) {
String pluginFilePath = getFilesDir().getAbsolutePath() + File.separator + PLUGIN_APK;
File pluginFile = new File(pluginFilePath);
if (!pluginFile.exists()) {
copyAssetsFileToAppFiles(PLUGIN_PATH, PLUGIN_APK);
if (pluginFile.exists()) {
info = RePlugin.install(pluginFilePath);
}
}
}
if (info != null) {
RePlugin.startActivity(MainActivity.this, RePlugin.createIntent(info.getName(), "com.qihoo360.replugin.sample.demo3.MainActivity"));
} else {
Toast.makeText(MainActivity.this, "install external plugin failed", Toast.LENGTH_SHORT).show();
}
}
示例4
/**
* 安装此插件 <p>
* 注意: <p>
* 1、这里只将APK移动(或复制)到“插件路径”下,不释放优化后的Dex和Native库,不会加载插件 <p>
* 2、支持“纯APK”和“p-n”(旧版,即将废弃)插件 <p>
* 3、此方法是【同步】的,耗时较少
*
* @param path 插件安装的地址。必须是“绝对路径”。通常可以用context.getFilesDir()来做
* @return 安装成功的插件信息,外界可直接读取
* @since 2.0.0 (1.x版本为installDelayed)
*/
public static PluginInfo install(String path) {
if (!RePluginFramework.mHostInitialized) {
return null;
}
try {
Object obj = ProxyRePluginVar.install.call(null, path);
if (obj != null) {
// 跨ClassLoader进行parcel对象的构造
Parcel p = ParcelUtils.createFromParcelable((Parcelable) obj);
return PluginInfo.CREATOR.createFromParcel(p);
}
} catch (Exception e) {
if (LogDebug.LOG) {
e.printStackTrace();
}
}
return null;
}
示例5
/**
* 预加载此插件。此方法会立即释放优化后的Dex和Native库,但不会运行插件代码。 <p>
* 使用场景:在“安装”完成后“提前释放Dex”(时间算在“安装过程”中)。这样下次启动插件时则速度飞快 <p>
* 注意: <p>
* 1、该方法非必须调用(见“使用场景”)。换言之,只要涉及到插件加载,就会自动完成preload操作,无需开发者关心 <p>
* 2、Dex和Native库会占用大量的“内部存储空间”。故除非插件是“确定要用的”,否则不必在安装完成后立即调用此方法 <p>
* 3、该方法为【同步】调用,且耗时较久(尤其是dex2oat的过程),建议在线程中使用
*
* @param pi 要加载的插件信息
* @return 预加载是否成功
* @hide
* @see #install(String)
* @since 2.0.0
*/
public static boolean preload(PluginInfo pi) {
if (!RePluginFramework.mHostInitialized) {
return false;
}
try {
// 跨classloader创建PluginInfo对象
// TODO 如果有更优雅的方式,可优化
Object p = ParcelUtils.createFromParcelable(pi, RePluginEnv.getHostCLassLoader(), "com.qihoo360.replugin.model.PluginInfo");
Object obj = ProxyRePluginVar.preload2.call(null, p);
if (obj != null) {
return (Boolean) obj;
}
} catch (Exception e) {
if (LogDebug.LOG) {
e.printStackTrace();
}
}
return false;
}
示例6
/**
* 获取指定插件的信息
*
* @param name 插件名
* @return PluginInfo对象
* @since 1.2.0
*/
public static PluginInfo getPluginInfo(String name) {
if (!RePluginFramework.mHostInitialized) {
return null;
}
try {
Object obj = ProxyRePluginVar.getPluginInfo.call(null, name);
if (obj != null) {
// 跨ClassLoader进行parcel对象的构造
Parcel p = ParcelUtils.createFromParcelable((Parcelable) obj);
return PluginInfo.CREATOR.createFromParcel(p);
}
} catch (Exception e) {
if (LogDebug.LOG) {
e.printStackTrace();
}
}
return null;
}
示例7
/**
* @deprecated 待优化
* 插件进程调度
* @param plugin
* @param process
* @return
*/
@Deprecated
static final int allocProcess(String plugin, int process) {
if (AppConstant.PLUGIN_NAME_UI.equals(plugin) || process == IPluginManager.PROCESS_UI) {
return IPluginManager.PROCESS_UI;
}
if (PluginProcessHost.isCustomPluginProcess(process)) {
return process;
}
PluginInfo info = PluginTable.getPluginInfo(plugin);
if (info == null) {
if (LOG) {
LogDebug.d(PLUGIN_TAG, "alloc process: plugin not found: name=" + plugin);
}
return IPluginManager.PROCESS_AUTO;
}
synchronized (PROCESSES) {
return allocProcessLocked(plugin);
}
}
示例8
final void pluginUninstalled(PluginInfo info) {
if (LOG) {
LogDebug.d(PLUGIN_TAG, "Clear plugin cache. pn=" + info.getName());
}
// 移除卸载插件的HashMap缓存
if (mPlugins.containsKey(info.getName())) {
mPlugins.remove(info.getName());
}
// 移除卸载插件表快照
PluginTable.removeInfo(info);
// 移除内存中插件的PackageInfo、Resources、ComponentList和DexClassLoader缓存对象
AllPluginsInfoPool.clearCachedPlugin(AllPluginsInfoPool.queryCachedFilename(info.getName()));
}
示例9
private void updateAllIfNeeded() {
// FIXME 务必保证sync方法被调用,否则有可能判断不准确
int updateNum = 0;
for (PluginInfo pi : mList) {
if (updateIfNeeded(pi)) {
updateNum++;
}
}
if (LogDebug.LOG) {
LogDebug.d(TAG, "updateAllIfNeeded: Updated " + updateNum + " plugins");
}
if (updateNum > 0) {
mList.save(mContext);
}
}
示例10
private void updateNow(PluginInfo curInfo, PluginInfo newInfo) {
final boolean covered = newInfo.getIsPendingCover();
if (covered) {
move(curInfo, newInfo);
} else {
// 删除旧版本插件,不管是不是p-n的,且清掉Dex和Native目录
delete(curInfo);
}
newInfo.setType(PluginInfo.TYPE_EXTRACTED);
if (LogDebug.LOG) {
LogDebug.i(TAG, "updateNow: Update. pn=" + curInfo.getVersion() +
"; cur_ver=" + curInfo.getVersion() + "; update_ver=" + newInfo.getVersion());
}
if (covered) {
curInfo.setPendingCover(null);
} else {
curInfo.update(newInfo);
curInfo.setPendingUpdate(null);
}
}
示例11
/**
* 提取文件到目标位置,并处理文件夹是否存在,是否校验,是否强制覆盖,是否需要释放SO库
* @param context
* @param info PluginInfo对象(asset的相对路径,可包含子路径)
* @param dir 目标文件夹(asset的输出目录)
* @param dexOutputDir 成功提取该文件时,是否删除同名的DEX文件
* @return
*/
public static final boolean quickExtractTo(Context context, final PluginInfo info, final String dir, final String dstName, String dexOutputDir) {
QuickExtractResult result = quickExtractTo(context, info.getPath(), dir, dstName, dexOutputDir);
// 释放失败 || 被释放的文件已经存在
switch (result) {
case FAIL:
return false;
case EXISTED:
return true;
default:
// 释放插件里的Native(SO)库文件
// Added by Jiongxuan Zhang
File file = new File(dir + "/" + dstName);
File libDir = info.getNativeLibsDir();
boolean rc = PluginNativeLibsHelper.install(file.getAbsolutePath(), libDir);
if (!rc) {
if (LOGR) {
LogRelease.e(TAG, "a u e rc f so " + file.getPath());
}
return rc;
}
return true;
}
}
示例12
/**
* 常驻进程进行插件卸载
*
* @param pi
*/
public static boolean uninstall(PluginInfo pi) throws RemoteException {
if (pi == null) {
// 不太可能到这里
return false;
}
if (pi.isPnPlugin()) {
// 是常驻进程?老逻辑直接走dex文件存在判断,也无需做处理
return false;
}
if (sRemote == null) {
// 常驻已挂掉,可以认为无需处理
if (LogRelease.LOGR) {
LogRelease.e(PLUGIN_TAG, "pmc.uuin: s=null");
}
return false;
}
return sRemote.uninstall(pi);
}
示例13
private int install(ContentValues cv) {
if (cv == null) {
return 0;
}
String pit = cv.getAsString(KEY_PLUGIN_INFO);
if (TextUtils.isEmpty(pit)) {
return 0;
}
PluginInfo pi = PluginInfo.parseFromJsonText(pit);
// 开始加载ClassLoader
ClassLoader cl = PluginMgrFacade.getLocal().loadPluginClassLoader(pi);
if (cl != null) {
return 1;
} else {
return 0;
}
}
示例14
static final void replaceInfo(PluginInfo info) {
boolean rc = false;
PluginInfo pi = null;
synchronized (PLUGINS) {
pi = PLUGINS.get(info.getName());
if (pi != null) {
if (pi.canReplaceForPn(info)) {
putPluginInfo(info);
rc = true;
}
}
}
if (LOG) {
LogDebug.d(PLUGIN_TAG, "replace plugin table: info=" + info + " rc=" + rc);
}
}
示例15
@Override
public boolean pluginExtracted(String path) throws RemoteException {
if (LOG) {
LogDebug.d(PLUGIN_TAG, "pluginExtracted: path=" + path);
}
//
File f = new File(path);
PluginInfo info = PluginInfo.build(f);
if (info == null) {
return false;
}
// 常驻进程上下文
mPluginMgr.newPluginFound(info, false);
// 通知其它进程
Intent intent = new Intent(PluginMgr.ACTION_NEW_PLUGIN);
intent.putExtra(RePluginConstants.KEY_PERSIST_NEED_RESTART, mNeedRestart);
intent.putExtra("obj", info);
IPC.sendLocalBroadcast2AllSync(mContext, intent);
return true;
}
示例16
static final void updatePlugin(PluginInfo info) {
if (LOG) {
LogDebug.d(PLUGIN_TAG, "update plugin table: info=" + info);
}
synchronized (PLUGINS) {
// 检查插件是否已经被禁用
if (RePlugin.getConfig().getCallbacks().isPluginBlocked(info)) {
if (LOG) {
LogDebug.d(PLUGIN_TAG, "update plugin table: plugin is blocked, in=" + info);
}
return;
}
// 此处直接使用该插件,没有考虑是否只采用最新版
putPluginInfo(info);
}
}
示例17
/**
* 确保版本和插件唯一
* @param array
* @param info
* @param replace true表示更新相同的,false表示不更新相同的
* @return
*/
private final boolean insert(ArrayList<PluginInfo> array, PluginInfo info, boolean replace) {
for (int i = 0; i < array.size(); i++) {
PluginInfo pi = array.get(i);
// 存在
if (pi.getName().equals(info.getName())) {
// 忽略
if (replace) {
if (PluginInfo.VERSION_COMPARATOR.compare(pi, info) > 0) {
return false;
}
} else {
if (PluginInfo.VERSION_COMPARATOR.compare(pi, info) >= 0) {
return false;
}
}
// 更新
others.add(array.get(i));
array.set(i, info);
return true;
}
}
// 不存在,添加
array.add(info);
return true;
}
示例18
/**
* @param info
* @return
*/
final void addNormal(PluginInfo info) {
PluginInfo pi = null;
// FIXME 用all表
if ((pi = getBuiltin(info.getName())) != null && pi.getVersionValue() == info.getVersionValue()) {
if (LOG) {
LogDebug.d(PLUGIN_TAG, "builtin plugin: normal=" + info);
}
} else if ((pi = getV5(info.getName())) != null && pi.getVersionValue() == info.getVersionValue()) {
if (LOG) {
LogDebug.d(PLUGIN_TAG, "v5 plugin: normal=" + info);
}
} else {
others.add(info);
return;
}
insert(normals, info, false);
}
示例19
private static void deleteUnknownDexs(Context context, PxAll all) {
HashSet<String> names = new HashSet<>();
for (PluginInfo p : all.getPlugins()) {
names.add(p.getDexFile().getName());
}
File dir = context.getDir(AppConstant.LOCAL_PLUGIN_ODEX_SUB_DIR, 0);
File files[] = dir.listFiles();
if (files != null) {
for (File f : files) {
if (names.contains(f.getName())) {
continue;
}
if (LOG) {
LogDebug.d(PLUGIN_TAG, "delete unknown dex=" + f.getAbsolutePath());
}
try {
FileUtils.forceDelete(f);
} catch (IOException e) {
if (LOG) {
LogDebug.d(PLUGIN_TAG, "can't delete unknown dex=" + f.getAbsolutePath(), e);
}
}
}
}
}
示例20
private static void deleteUnknownLibs(Context context, PxAll all) {
HashSet<String> names = new HashSet<>();
for (PluginInfo p : all.getPlugins()) {
names.add(p.getNativeLibsDir().getName());
}
File dir = context.getDir(AppConstant.LOCAL_PLUGIN_DATA_LIB_DIR, 0);
File files[] = dir.listFiles();
if (files != null) {
for (File f : files) {
if (names.contains(f.getName())) {
continue;
}
if (LOG) {
LogDebug.d(PLUGIN_TAG, "delete unknown libs=" + f.getAbsolutePath());
}
try {
FileUtils.forceDelete(f);
} catch (IOException e) {
if (LOG) {
LogDebug.d(PLUGIN_TAG, "can't delete unknown libs=" + f.getAbsolutePath(), e);
}
}
}
}
}
示例21
@Override
public void onObserveViewModel(@NonNull SettingsViewModel viewModel) {
super.onObserveViewModel(viewModel);
binding.setSettingsViewModel(viewModel);
viewModel.cacheSize.set(FileUtil.getCacheSizeStr(this));
int pluginNum = 0;
for (PluginInfo pluginInfo : RePlugin.getPluginInfoList()) {
pluginNum += pluginInfo.isUsed() ? 1 : 0;
}
viewModel.pluginNum.set(pluginNum);
}
示例22
@Override
public void install(Context context, String filename, Update update) {
PluginInfo info = RePlugin.install(filename);
if (!info.getAlias().equals(pluginName)) {
// 校验是否插件的别名能匹配上。将不正确的卸载掉
Toast.makeText(context, String.format("install plugin failed: need alias for %s but is %s", pluginName, info.getAlias()), Toast.LENGTH_SHORT).show();
RePlugin.uninstall(info.getAlias());
} else {
RePlugin.startActivity(this.context, intent);
}
}
示例23
/**
* 获取所有插件的列表(指已安装的)
*
* @return PluginInfo的表
* @since 2.0.0(1.x版本为getExistPlugins)
*/
public static List<PluginInfo> getPluginInfoList() {
if (!RePluginFramework.mHostInitialized) {
return null;
}
try {
List list = (List) ProxyRePluginVar.getPluginInfoList.call(null);
if (list != null && list.size() > 0) {
List<PluginInfo> ret = new ArrayList<>();
for (Object o : list) {
// 跨ClassLoader进行parcel对象的构造
Parcel p = ParcelUtils.createFromParcelable((Parcelable) o);
PluginInfo nPi = PluginInfo.CREATOR.createFromParcel(p);
ret.add(nPi);
}
return ret;
}
} catch (Exception e) {
if (LogDebug.LOG) {
e.printStackTrace();
}
}
return null;
}
示例24
/**
* 预加载此插件。此方法会立即释放优化后的Dex和Native库,但不会运行插件代码。 <p>
* 具体用法可参见preload(PluginInfo)的说明
*
* @param pluginName 要加载的插件名
* @return 预加载是否成功
* @see #preload(PluginInfo)
* @since 2.0.0
*/
public static boolean preload(String pluginName) {
PluginInfo pi = getPluginInfo(pluginName);
if (pi == null) {
// 插件不存在,无法加载Dex
if (LogDebug.LOG) {
LogDebug.e(TAG, "preload: Plugin not found! pn=" + pluginName);
}
return false;
}
return preload(pi);
}
示例25
/**
* 获取当前插件的版本号,可以是VersionCode,也可以是meta-data中的ver。
*
* @param name 插件名
* @return 插件版本号。若为-1则表示插件不存在
* @since 2.0.0
*/
public static int getPluginVersion(String name) {
PluginInfo pi = RePluginOS.getPlugin(name, false);
if (pi == null) {
return -1;
}
return pi.getVersion();
}
示例26
private boolean onInstall(String path, boolean immediately) {
PluginInfo pi = RePlugin.install(path);
// Okay
if (pi != null) {
if (LogDebug.LOG) {
LogDebug.i(TAG, "onInstall: Install Success! cur=" + RePlugin.getPluginInfo(pi.getName()));
}
if (immediately) {
if (RePlugin.preload(pi)) {
if (LogDebug.LOG) {
LogDebug.i(TAG, "onInstall: Preload Success! pn=" + pi.getName());
}
return true;
} else {
if (LogDebug.LOG) {
LogDebug.e(TAG, "onInstall: Preload Error! pn=" + pi.getName());
}
}
}
} else {
if (LogDebug.LOG) {
LogDebug.e(TAG, "onInstall: Install Error! path=" + path);
}
}
return false;
}
示例27
private List<PluginInfo> loadLocked() {
if (!mList.load(mContext)) {
return null;
}
// 执行“更新或删除Pending”插件,并返回结果
return updateAllLocked();
}
示例28
private List<PluginInfo> updateAllLocked() {
// 判断是否需要更新插件(只有未运行的才可以)
updateAllIfNeeded();
// TODO 扫描一下,看看文件在不在
return mList.cloneList();
}
示例29
/**
* 插件卸载
* 判断插件是否已安装:插件未安装,不做处理
* 插件已安装,正在运行,则记录“卸载状态”,推迟到到主程序进程重启的时执行卸载
* 插件已安装,未在运行,则直接删除Dex、Native库等资源
*
* @param pluginName
* @return 插件卸载成功与否
*/
public static final boolean pluginUninstall(String pluginName) {
if (LOG) {
LogDebug.d(PLUGIN_TAG, "RePluginOS.pluginUninstall ... pluginName=" + pluginName);
}
PluginInfo pi = getPlugin(pluginName, true);
// 插件未安装
if (pi == null) {
if (LOG) {
LogDebug.d(PLUGIN_TAG, "Not installed. pluginName=" + pluginName);
}
return true;
}
// 插件已安装
try {
return PluginManagerProxy.uninstall(pi);
} catch (RemoteException e) {
e.printStackTrace();
if (LOG) {
e.printStackTrace();
}
}
return false;
}
示例30
private static void putPluginInfo(PluginInfo info) {
// 同时加入PackageName和Alias(如有)
PLUGINS.put(info.getPackageName(), info);
if (!TextUtils.isEmpty(info.getAlias())) {
// 即便Alias和包名相同也可以再Put一次,反正只是覆盖了相同Value而已
PLUGINS.put(info.getAlias(), info);
}
}