From 4fb15f8c41e20bde9522d2fa9952ae52087af40d Mon Sep 17 00:00:00 2001 From: zhangjiongxuan Date: Tue, 8 Aug 2017 18:42:24 +0800 Subject: [PATCH 1/2] Resolved the issue of "Overwriting the plug-in, it may not be notified to other processes" Also modified: * Added the "getParentInfo" method in the PluginInfo class * IsNewOrUpdatedPlugin removed from PmBase because it is no longer needed --- .../java/com/qihoo360/loader2/PmBase.java | 16 +----- .../java/com/qihoo360/loader2/PmHostSvc.java | 49 +++++++++++++------ .../qihoo360/replugin/model/PluginInfo.java | 49 +++++++++---------- .../packages/PluginManagerServer.java | 8 +-- 4 files changed, 61 insertions(+), 61 deletions(-) diff --git a/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/PmBase.java b/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/PmBase.java index 380ba59f..733085af 100644 --- a/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/PmBase.java +++ b/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/PmBase.java @@ -32,7 +32,6 @@ import com.qihoo360.i.Factory; import com.qihoo360.i.IModule; import com.qihoo360.i.IPluginManager; -import com.qihoo360.replugin.utils.ReflectUtils; import com.qihoo360.mobilesafe.api.Tasks; import com.qihoo360.replugin.IHostBinderFetcher; import com.qihoo360.replugin.RePlugin; @@ -49,6 +48,7 @@ import com.qihoo360.replugin.helper.LogRelease; import com.qihoo360.replugin.model.PluginInfo; import com.qihoo360.replugin.packages.PluginManagerProxy; +import com.qihoo360.replugin.utils.ReflectUtils; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -997,20 +997,6 @@ final Plugin getPlugin(String plugin) { return mPlugins.get(plugin); } - // 是否为新的,或已经更新过的插件 - // 注意:若“插件正在运行”时触发更新,则这里应返回false,这样外界将不会发送“新插件”广播 - final boolean isNewOrUpdatedPlugin(PluginInfo info) { - Plugin p = mPlugins.get(info.getName()); - - // 满足三个条件的其一就表示为"新插件",可以做下一步处理 - // 1. p为空,表示为全新插件 - // 2. 要安装的版本比当前的要高,且未运行 - // 3. "待定更新"插件(插件未运行) - return p == null || - (p.mInfo.getVersionValue() < info.getVersionValue() && !RePlugin.isPluginRunning(info.getName())) || - (info.getPendingUpdate() != null && !RePlugin.isPluginRunning(info.getName())); - } - final Plugin loadPackageInfoPlugin(String plugin, PluginCommImpl pm) { Plugin p = Plugin.cloneAndReattach(mContext, mPlugins.get(plugin), mClassLoader, pm); return loadPlugin(p, Plugin.LOAD_INFO, true); diff --git a/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/PmHostSvc.java b/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/PmHostSvc.java index a8308ba8..dd59c604 100644 --- a/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/PmHostSvc.java +++ b/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/loader2/PmHostSvc.java @@ -328,26 +328,43 @@ public PluginInfo pluginDownloaded(String path) throws RemoteException { } if (pi != null) { - // 插件是否需要更新 - if (!mPluginMgr.isNewOrUpdatedPlugin(pi)) { - return pi; - } - - // 常驻进程上下文 - mPluginMgr.newPluginFound(pi, false); + // 通常到这里,表示“安装已成功”,这时不管处于什么状态,都应该通知外界更新插件内存表 + syncPluginInfo2All(pi); + } - // 通知其它进程 - Intent intent = new Intent(PmBase.ACTION_NEW_PLUGIN); - intent.putExtra(RePluginConstants.KEY_PERSIST_NEED_RESTART, mNeedRestart); - intent.putExtra("obj", pi); - IPC.sendLocalBroadcast2AllSync(mContext, intent); + return pi; + } - if (LOG) { - LogDebug.d(PLUGIN_TAG, "pluginDownloaded complete, info=" + pi); - } + private void syncPluginInfo2All(PluginInfo pi) { + // PS:若更新了“正在运行”的插件(属于“下次重启进程后更新”),则由于install返回的是“新的PluginInfo”,为防止出现“错误更新”,需要使用原来的 + // + // 举例,有一个正在运行的插件A(其Info为PluginInfoOld)升级到新版(其Info为PluginInfoNew),则: + // 1. mManager.getService().install(path) 的返回值为:PluginInfoNew + // 2. PluginInfoOld在常驻进程中的内容修改为:PluginInfoOld.mPendingUpdate = PendingInfoNew + // 3. 同步到各进程,这里存在两种可能: + // a) (有问题)同步的是PluginInfoNew,则所有进程的内存表都强制更新到新的Info上,因此【正在运行的】插件信息将丢失,会出现严重问题 + // b) (没问题)同步的是PluginInfoOld,只不过这个Old里面有个mPendingUpdate指向PendingInfoNew,则不会有问题,旧的仍被使用,符合预期 + // 4. 最终install方法的返回值是PluginInfoNew,这样最外面拿到的就是安装成功的新插件信息,符合开发者的预期 + PluginInfo needToSyncPi; + PluginInfo parent = pi.getParentInfo(); + if (parent != null) { + needToSyncPi = parent; + } else { + needToSyncPi = pi; } - return pi; + // 在常驻进程内更新插件内存表 + mPluginMgr.newPluginFound(needToSyncPi, false); + + // 通知其它进程去更新 + Intent intent = new Intent(PmBase.ACTION_NEW_PLUGIN); + intent.putExtra(RePluginConstants.KEY_PERSIST_NEED_RESTART, mNeedRestart); + intent.putExtra("obj", needToSyncPi); + IPC.sendLocalBroadcast2AllSync(mContext, intent); + + if (LOG) { + LogDebug.d(TAG, "syncPluginInfo2All: Sync complete! syncPi=" + needToSyncPi); + } } private PluginInfo pluginDownloadedForPn(String path) { diff --git a/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/replugin/model/PluginInfo.java b/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/replugin/model/PluginInfo.java index 4c70aee4..b4b139f7 100644 --- a/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/replugin/model/PluginInfo.java +++ b/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/replugin/model/PluginInfo.java @@ -34,8 +34,8 @@ import com.qihoo360.replugin.RePluginInternal; import com.qihoo360.replugin.helper.JSONHelper; import com.qihoo360.replugin.helper.LogDebug; - import com.qihoo360.replugin.utils.FileUtils; + import org.json.JSONException; import org.json.JSONObject; @@ -101,14 +101,18 @@ public class PluginInfo implements Parcelable, Cloneable { // 若插件需要更新,则会有此值 private PluginInfo mPendingUpdate; - private boolean mIsThisPendingUpdateInfo; // 若插件需要卸载,则会有此值 private PluginInfo mPendingDelete; // 若插件需要同版本覆盖安装更新,则会有此值 private PluginInfo mPendingCover; - private boolean mIsPendingCover; + private boolean mIsPendingCover; // 若当前为“新的PluginInfo”且为“同版本覆盖”,则为了能区分路径,则需要将此字段同步到Json文件中 + + // 若当前为“新的PluginInfo”,则其“父Info”是什么? + // 通常当前这个Info会包裹在“mPendingUpdate/mPendingDelete/mPendingCover”内 + // 此信息【不会】做持久化工作。下次重启进程后会消失 + private PluginInfo mParentInfo; private PluginInfo(JSONObject jo) { initPluginInfo(jo); @@ -150,7 +154,6 @@ private PluginInfo(String pkgName, String alias, int low, int high, int version, */ public PluginInfo(PluginInfo pi) { this.mJson = JSONHelper.cloneNoThrows(pi.mJson); - this.mIsThisPendingUpdateInfo = pi.mIsThisPendingUpdateInfo; if (pi.mPendingUpdate != null) { this.mPendingUpdate = new PluginInfo(pi.mPendingUpdate); } @@ -161,6 +164,9 @@ public PluginInfo(PluginInfo pi) { if (pi.mPendingCover != null) { this.mPendingCover = new PluginInfo(pi.mPendingCover); } + if (pi.mParentInfo != null) { + this.mParentInfo = new PluginInfo(pi.mParentInfo); + } } private void initPluginInfo(JSONObject jo) { @@ -206,11 +212,6 @@ private String makeName(String pkgName, String alias) { */ public static PluginInfo parseFromPackageInfo(PackageInfo pi, String path) { ApplicationInfo ai = pi.applicationInfo; - if (ai == null) { - // 几乎不可能,但为保险起见,返回Null - return null; - } - String pn = pi.packageName; String alias = null; int low = 0; @@ -322,9 +323,9 @@ public boolean isUsed() { if (isPnPlugin()) { // 为兼容以前逻辑,p-n仍是判断dex是否存在 return isDexExtracted(); - } else if (isThisPendingUpdateInfo()) { + } else if (getParentInfo() != null) { // 若PluginInfo是其它PluginInfo中的PendingUpdate,则返回那个PluginInfo的Used即可 - return RePlugin.isPluginUsed(getName()); + return getParentInfo().isUsed(); } else { // 若是纯APK,且不是PendingUpdate,则直接从Json中获取 return mJson.optBoolean("used"); @@ -625,9 +626,7 @@ public void setFrameworkVersionByMeta(Bundle meta) { setFrameworkVersion(frameVer); } - /** - * 获取JSON对象。仅内部使用 - */ + // @hide public JSONObject getJSON() { return mJson; } @@ -669,21 +668,17 @@ public void update(PluginInfo info) { } /** - * 此PluginInfo是否是一个位于其它PluginInfo中的PendingUpdate?只在调用RePlugin.install方法才能看到

- * 注意:仅框架内部使用 + * 若此Info为“新PluginInfo”,则这里返回的是“其父Info”的内容。通常和PendingUpdate有关 * - * @return 是否是PendingUpdate的PluginInfo + * @return 父PluginInfo */ - public boolean isThisPendingUpdateInfo() { - return mIsThisPendingUpdateInfo; + public PluginInfo getParentInfo() { + return mParentInfo; } - /** - * 此PluginInfo是否是一个位于其它PluginInfo中的PendingUpdate?

- * 注意:仅框架内部使用 - */ - public void setIsThisPendingUpdateInfo(boolean updateInfo) { - mIsThisPendingUpdateInfo = updateInfo; + // @hide + public void setParentInfo(PluginInfo parent) { + mParentInfo = parent; } static PluginInfo createByJO(JSONObject jo) { @@ -769,8 +764,8 @@ private void toContentString(StringBuilder b) { } // 当前是否为PendingUpdate的信息 - if (mIsThisPendingUpdateInfo) { - b.append("[isTPUI] "); + if (mParentInfo != null) { + b.append("[HAS_PARENT] "); } // 插件类型 diff --git a/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/replugin/packages/PluginManagerServer.java b/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/replugin/packages/PluginManagerServer.java index 9ce716a7..526d61bb 100644 --- a/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/replugin/packages/PluginManagerServer.java +++ b/replugin-host-library/replugin-host-lib/src/main/java/com/qihoo360/replugin/packages/PluginManagerServer.java @@ -28,16 +28,16 @@ import com.qihoo360.loader2.MP; import com.qihoo360.loader2.PluginNativeLibsHelper; import com.qihoo360.mobilesafe.api.Tasks; -import com.qihoo360.replugin.utils.pkg.PackageFilesUtil; import com.qihoo360.replugin.RePlugin; import com.qihoo360.replugin.RePluginEventCallbacks; import com.qihoo360.replugin.RePluginInternal; import com.qihoo360.replugin.base.IPC; -import com.qihoo360.replugin.utils.FileUtils; import com.qihoo360.replugin.helper.LogDebug; import com.qihoo360.replugin.helper.LogRelease; import com.qihoo360.replugin.model.PluginInfo; import com.qihoo360.replugin.model.PluginInfoList; +import com.qihoo360.replugin.utils.FileUtils; +import com.qihoo360.replugin.utils.pkg.PackageFilesUtil; import java.io.File; import java.io.IOException; @@ -297,7 +297,6 @@ private void updateOrLater(PluginInfo curPli, PluginInfo instPli) { } if (instPli.getVersion() > curPli.getVersion()) { // 高版本升级 - instPli.setIsThisPendingUpdateInfo(true); curPli.setPendingUpdate(instPli); curPli.setPendingDelete(null); curPli.setPendingCover(null); @@ -313,6 +312,9 @@ private void updateOrLater(PluginInfo curPli, PluginInfo instPli) { 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()); From 91e358bc44dad6e766353e4f1c5d162f8ac1d780 Mon Sep 17 00:00:00 2001 From: zhangjiongxuan Date: Tue, 8 Aug 2017 18:58:15 +0800 Subject: [PATCH 2/2] Add "Thread.getContextClassLoader" test in Sample --- .../qihoo360/replugin/sample/host/MainActivity.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/replugin-sample/host/app/src/main/java/com/qihoo360/replugin/sample/host/MainActivity.java b/replugin-sample/host/app/src/main/java/com/qihoo360/replugin/sample/host/MainActivity.java index 273445e2..3fc18d1c 100644 --- a/replugin-sample/host/app/src/main/java/com/qihoo360/replugin/sample/host/MainActivity.java +++ b/replugin-sample/host/app/src/main/java/com/qihoo360/replugin/sample/host/MainActivity.java @@ -98,6 +98,18 @@ public void run() { }, 1000); } }); + + // 刻意使用Thread的ClassLoader来测试效果 + testThreadClassLoader(); + } + + private void testThreadClassLoader() { + // 在2.1.7及以前版本,如果直接调用此方法,则拿到的ClassLoader可能是PathClassLoader或者为空。有极个别Java库会用到此方法 + // 这里务必确保:cl == getClassLoader(),才符合预期 + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + if (cl != getClassLoader()) { + throw new RuntimeException("Thread.current.classLoader != getClassLoader(). cl=" + cl + "; getC=" + getClassLoader()); + } } private static final int REQUEST_CODE_DEMO1 = 0x011;