360Replugin使用Install安装高版本插件,无法覆盖内置的低版本插件

在项目中,遇到一个问题。应用本身包含一个内置插件A.jar,打开app,从未使用过A插件,此时通过RePlugin.install方法,安装A插件的高版本。安装回调成功后,插件实际未更新。重启app,通过RePlugin.getPluginInfoList获取到两个A插件,一个低版本jar,一个高版本jar(一脸懵逼)。

场景分析:
失败场景:
1、打开app,插件一次未拉起
2、尝试install高版本插件,广播返回安装成功,实际拉起插件,还是旧版本插件
3、重启app,拉起插件,依然是低版本插件。
4、RePlugin.getPluginInfoList,返回两个一样的插件信息,一个高版本,一个低版本

成功场景:
1、打开app,拉起插件,然后关闭
2、重新打开app,尝试install高版本插件,拉起插件,成功了(TAT)

 通过debug源码,找到代码轨迹:RePlugin.install--->MP.pluginDownloaded(path)----->PluginProcessMain.getPluginHost().pluginDownloaded(path)------->PluginManagerServer.getService().install(path)------>PluginManagerServer.this.installLocked(path)------->PluginManagerServer.updateOrLater(PluginInfo curPli, PluginInfo instPli)等等

public static final PluginInfo pluginDownloaded(String path) {
        if (LOG) {
            LogDebug.d(PLUGIN_TAG, "MP.pluginDownloaded ... path=" + path);
        }

       ProcessLocker lock = null;

        try {
            if (path != null) {
                File f = new File(path);
                String fileName = f.getName();
                String fileDir = f.getParent();
                if (fileName.startsWith("p-n-")) {
                    lock = new ProcessLocker(RePluginInternal.getAppContext(), fileDir, fileName + ".lock");
                }
            }

            if (lock != null && !lock.tryLock()) {
                // 加锁
                if (LOG) {
                    LogDebug.d(PLUGIN_TAG, "MP.pluginDownloaded ... lock file + " + path + " failed! ");
                }
            }

            PluginInfo info = PluginProcessMain.getPluginHost().pluginDownloaded(path);
            if (info != null) {
                RePlugin.getConfig().getEventCallbacks().onInstallPluginSucceed(info);
            }
            return info;
        } catch (Throwable e) {
            if (LOGR) {
                LogRelease.e(PLUGIN_TAG, "mp.pded: " + e.getMessage(), e);
            }
        } finally {
            // 去锁
            if (lock != null) {
                lock.unlock();
            }
        }
        return null;
    }
   

public PluginInfo pluginDownloaded(String path) throws RemoteException {
        if (LOG) {
            LogDebug.d(PLUGIN_TAG, "pluginDownloaded: path=" + path);
        }

        // 通过路径来判断是采用新方案,还是旧的P-N(即将废弃,有多种)方案
        PluginInfo pi;
        String fn = new File(path).getName();
        if (fn.startsWith("p-n-") || fn.startsWith("v-plugin-") || fn.startsWith("plugin-s-") || fn.startsWith("p-m-")) {
            pi = pluginDownloadedForPn(path);
        } else {
            pi = mManager.getService().install(path);
        }

        if (pi != null) {
            // 通常到这里,表示“安装已成功”,这时不管处于什么状态,都应该通知外界更新插件内存表
            syncInstalledPluginInfo2All(pi);

        }
        return pi;
    }

 

 

 

private PluginInfo installLocked(String path) {
        final boolean verifySignEnable = RePlugin.getConfig().getVerifySign();
        final int flags = verifySignEnable ? PackageManager.GET_META_DATA | PackageManager.GET_SIGNATURES : PackageManager.GET_META_DATA;

        // 1. 读取APK内容
        PackageInfo pi = mContext.getPackageManager().getPackageArchiveInfo(path, flags);
        if (pi == null) {
            if (LogDebug.LOG) {
                LogDebug.e(TAG, "installLocked: Not a valid apk. path=" + path);
            }

            RePlugin.getConfig().getEventCallbacks().onInstallPluginFailed(path, RePluginEventCallbacks.InstallResult.READ_PKG_INFO_FAIL);
            return null;
        }

        // 2. 校验插件签名
        if (verifySignEnable) {
            if (!verifySignature(pi, path)) {
                return null;
            }
        }

        // 3. 解析出名字和三元组
        PluginInfo instPli = PluginInfo.parseFromPackageInfo(pi, path);
        if (LogDebug.LOG) {
            LogDebug.i(TAG, "installLocked: Info=" + instPli);
        }
        instPli.setType(PluginInfo.TYPE_NOT_INSTALL);

        // 若要安装的插件版本小于或等于当前版本,则安装失败
        // NOTE 绝大多数情况下,应该在调用RePlugin.install方法前,根据云端回传的信息来判断,以防止下载旧插件,浪费流量
        // NOTE 这里仅做双保险,或通过特殊渠道安装时会有用

        // 注意:这里必须用“非Clone过的”PluginInfo,因为要修改里面的内容
        PluginInfo curPli = MP.getPlugin(instPli.getName(), false);
        if (curPli != null) {
            if (LogDebug.LOG) {
                LogDebug.i(TAG, "installLocked: Has installed plugin. current=" + curPli);
            }

            // 版本较老?直接返回
            final int checkResult = checkVersion(instPli, curPli);
            if (checkResult < 0) {
                RePlugin.getConfig().getEventCallbacks().onInstallPluginFailed(path, RePluginEventCallbacks.InstallResult.VERIFY_VER_FAIL);
                return null;
            } else if (checkResult == 0) {
                instPli.setIsPendingCover(true);
            }
        }

        // 4. 将合法的APK改名后,移动(或复制,见RePluginConfig.isMoveFileWhenInstalling)到新位置
        // 注意:不能和p-n的最终释放位置相同,因为管理方式不一样
        if (!copyOrMoveApk(path, instPli)) {
            RePlugin.getConfig().getEventCallbacks().onInstallPluginFailed(path, RePluginEventCallbacks.InstallResult.COPY_APK_FAIL);
            return null;
        }

        // 5. 从插件中释放 So 文件
        PluginNativeLibsHelper.install(instPli.getPath(), instPli.getNativeLibsDir());

        // 6. 若已经安装旧版本插件,则尝试更新插件信息,否则直接加入到列表中
        if (curPli != null) {
            updateOrLater(curPli, instPli);
        } else {
            mList.add(instPli);
        }

        // 7. 保存插件信息到文件中,下次可直接使用
        mList.save(mContext);

        return instPli;
    }
   

private void updateOrLater(PluginInfo curPli, PluginInfo instPli) {
        if (LogDebug.LOG) {
            LogDebug.d(TAG, "updateOrLater: Need update. pn=" + curPli.getName() +
                    "; cur_ver=" + curPli.getVersion() + "; update_ver=" + instPli.getVersion());
        }
        // 既然要更新到新的"纯APK"方案,自然需要把旧p-n的信息迁移到新列表中
        // FIXME 看看有没有别的兼容问题,尤其是和两个List表之间的
        if (curPli.isPnPlugin()) {
            mList.add(curPli);
        }

        // 已有“待更新版本”?
        PluginInfo curUpdatePli = curPli.getPendingUpdate();
        if (curUpdatePli != null) {
            updatePendingUpdate(curPli, instPli, curUpdatePli);

            // 由于"打算要更新"的前提是插件正在被运行,且下次重启时会清空这个信息,既然这次只是替换"打算要更新"的插件信息
            // 则不必再做后面诸如"插件是否存在"等判断,直接返回即可
            return;
        }

        // 正在运行?Later到下次使用时再释放。否则直接开始更新

       //场景一出现的问题就在这里出现分歧
        if (RePlugin.isPluginRunning(curPli.getName()))
{
            if (LogDebug.LOG) {
                LogDebug.w(TAG, "updateOrLater: Plugin is running. Later. pn=" + curPli.getName());
            }
            if (instPli.getVersion() > curPli.getVersion() ||
                    instPli.getVersion() == curPli.getVersion() && getPluginType(instPli) != getPluginType(curPli)) {
                // 高版本升级
                curPli.setPendingUpdate(instPli);
                curPli.setPendingDelete(null);
                curPli.setPendingCover(null);
                if (LogDebug.LOG) {
                    LogDebug.w(TAG, "updateOrLater: Plugin need update high version. clear PendingDelete and PendingCover.");
                }
            } else if (instPli.getVersion() == curPli.getVersion()) {
                // 同版本覆盖
                curPli.setPendingCover(instPli);
                curPli.setPendingDelete(null);
                // 注意:并不需要对PendingUpdate信息做处理,因为此前的updatePendingUpdate方法时就已经返回了
                if (LogDebug.LOG) {
                    LogDebug.w(TAG, "updateOrLater: Plugin need update same version. clear PendingDelete.");
                }
            }

            // 设置其Parent为curPli,在PmBase.newPluginFound时会用到
            instPli.setParentInfo(curPli);
        } else {
            if (LogDebug.LOG) {
                LogDebug.i(TAG, "updateOrLater: Not running. Update now! pn=" + curPli.getName());
            }
            updateNow(curPli, instPli);//场景二走这边,直接进行了插件升级
        }
    }

 

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);
            newInfo.setIsPendingCover(false);
            //修改isPendingCover属性后必须同时修改json中的path路径
            newInfo.setPath(newInfo.getApkFile().getPath());
        } else {
            curInfo.update(newInfo);
            curInfo.setPendingUpdate(null);

        }
    }

 

 通过查看源码,在这一步出现分歧,在PluginManagerServer.updateOrLater(PluginInfo curPli, PluginInfo instPli)方法中,RePlugin.isPluginRunning(curPli.getName())判断不同,该方法判断即将更新的低版本插件,是否在运行,若在运行下次使用时再释放。否则直接开始更新。那么问题来了,插件使用一次后,再次打开app插件为何未运行?以及在运行时,无法覆盖更新的逻辑是否存在问题?

1、先看第一个问题,打开APP,为拉起插件,插件如何处于running状态?

通过下图看出,因为内置插件做了preload操作,replugin把插件加入到了running状态。

 代码中去除preload后,安装正常...

 还有一些问题,有待继续研究...

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值