目录

  • 1.宿主和插件的交互
  • 2.插件的安装
  • 3.插件的加载
  • 4.启动插件的Activity
  • 5.Replugin Hook系统ClassLoader

1.宿主和插件的交互

Replugin默认会使用一个常驻进程作为Server端,其他插件进程和宿主进程属于Client端。如果修改不使用常驻进程,宿主的主进程将作为插件管理进程,Server端其实就是创建了一个运行在该进程中的Provider,通过Provider的query方法返回了Binder对象来实现多进程直接的沟通和数据共享,实现插件和宿主之间沟通和数据共享,插件的安装,卸载,更新,状态判断等全部在Server端完成。

RePluginApplication 初始化 RePluginApplication.java

      super.attachBaseContext(base);RePluginConfig c = this.createConfig();if (c == null) {c = new RePluginConfig();}RePluginCallbacks cb = this.createCallbacks();if (cb != null) {c.setCallbacks(cb);}App.attachBaseContext(this, c);  }

App.attachBaseContext(this, c);

        public static void attachBaseContext(Application app, RePluginConfig config) {if (sAttached) {if (LogDebug.LOG) {LogDebug.d(TAG, "attachBaseContext: Already called");}return;}RePluginInternal.init(app);sConfig = config;sConfig.initDefaults(app);IPC.init(app);// 打印当前内存占用情况// 只有开启“详细日志”才会输出,防止“消耗性能”if (LOG && RePlugin.getConfig().isPrintDetailLog()) {LogDebug.printMemoryStatus(LogDebug.TAG, "act=, init, flag=, Start, pn=, framework, func=, attachBaseContext, lib=, RePlugin");}// 初始化HostConfigHelper(通过反射HostConfig来实现)// NOTE 一定要在IPC类初始化之后才使用HostConfigHelper.init();// FIXME 此处需要优化掉AppVar.sAppContext = app;// Plugin Status ControllerPluginStatusController.setAppContext(app);PMF.init(app);  //初始化applicationPMF.callAttach();sAttached = true;}

PMF.init(app);

    public static final void init(Application application) {setApplicationContext(application);PluginManager.init(application);sPluginMgr = new PmBase(application);sPluginMgr.init();Factory.sPluginManager = PMF.getLocal();Factory2.sPLProxy = PMF.getInternal();PatchClassLoaderUtils.patch(application);}

sPluginMgr = new PmBase(application);
PmBase.java

    void init() {RePlugin.getConfig().getCallbacks().initPnPluginOverride();if (HostConfigHelper.PERSISTENT_ENABLE) {// (默认)“常驻进程”作为插件管理进程,则常驻进程作为Server,其余进程作为Clientif (IPC.isPersistentProcess()) {// 初始化“Server”所做工作initForServer();} else {// 连接到ServerinitForClient();}} else {// “UI进程”作为插件管理进程(唯一进程),则UI进程既可以作为Server也可以作为Clientif (IPC.isUIProcess()) {// 1. 尝试初始化Server所做工作,initForServer();// 2. 注册该进程信息到“插件管理进程”中// 注意:这里无需再做 initForClient,因为不需要再走一次BinderPMF.sPluginMgr.attach();} else {// 其它进程?直接连接到Server即可initForClient();}}

初始化“Server”所做工作

    private final void initForServer() {if (LOG) {LogDebug.d(PLUGIN_TAG, "search plugins from file system");}mHostSvc = new PmHostSvc(mContext, this);PluginProcessMain.installHost(mHostSvc);StubProcessManager.schedulePluginProcessLoop(StubProcessManager.CHECK_STAGE1_DELAY);// 兼容即将废弃的p-n方案 by Jiongxuan ZhangmAll = new Builder.PxAll();Builder.builder(mContext, mAll);refreshPluginMap(mAll.getPlugins());// [Newest!] 使用全新的RePlugin APK方案// Added by Jiongxuan Zhangtry {List<PluginInfo> l = PluginManagerProxy.load();if (l != null) {// 将"纯APK"插件信息并入总的插件信息表中,方便查询// 这里有可能会覆盖之前在p-n中加入的信息。本来我们就想这么干,以"纯APK"插件为准refreshPluginMap(l);}} catch (RemoteException e) {if (LOGR) {LogRelease.e(PLUGIN_TAG, "lst.p: " + e.getMessage(), e);}}}

mHostSvc = new PmHostSvc(mContext, this);

    PmHostSvc(Context context, PmBase packm) {mContext = context;mPluginMgr = packm;mServiceMgr = new PluginServiceServer(context);mManager = new PluginManagerServer(context);}

mServiceMgr = new PluginServiceServer(context);

PluginServiceServer负责Server端的服务调度、提供等工作,是服务的提供方,核心类之一// 启动插件Service。说明见PluginServiceClient的定义ComponentName startServiceLocked(Intent intent, Messenger client) {intent = cloneIntentLocked(intent);ComponentName cn = intent.getComponent();
//        ProcessRecord callerPr = retrieveProcessRecordLocked(client);final ServiceRecord sr = retrieveServiceLocked(intent);if (sr == null) {return null;}if (!installServiceIfNeededLocked(sr)) {return null;}sr.startRequested = true;// 加入到列表中,统一管理mServicesByName.put(cn, sr);if (LOG) {LogDebug.i(PLUGIN_TAG, "PSM.startService(): Start! in=" + intent + "; sr=" + sr);}// 从binder线程post到ui线程,去执行Service的onStartCommand操作Message message = mHandler.obtainMessage(WHAT_ON_START_COMMAND);Bundle data = new Bundle();data.putParcelable("intent", intent);message.setData(data);message.obj = sr;mHandler.sendMessage(message);return cn;}// 停止插件的Service。说明见PluginServiceClient的定义int stopServiceLocked(Intent intent) {intent = cloneIntentLocked(intent);ServiceRecord sr = getServiceLocked(intent);if (sr == null) {return 0;}sr.startRequested = false;recycleServiceIfNeededLocked(sr);if (LOG) {LogDebug.i(PLUGIN_TAG, "PSM.stopService(): Stop! in=" + intent + "; sr=" + sr);}return 1;}

mManager = new PluginManagerServer(context);
PluginManagerServer插件管理器,控制插件的安装、卸载、获取等,运行在常驻进程中

initForClient 连接到Server

/*** Client(UI进程)的初始化**/private final void initForClient() {if (LOG) {LogDebug.d(PLUGIN_TAG, "list plugins from persistent process");}// 1. 先尝试连接PluginProcessMain.connectToHostSvc();// 2. 然后从常驻进程获取插件列表refreshPluginsFromHostSvc();}

2.插件的安装

RePlugin.java

    /*** 安装或升级此插件 <p>* 注意: <p>* 1、这里只将APK移动(或复制)到“插件路径”下,不释放优化后的Dex和Native库,不会加载插件 <p>* 2、支持“纯APK”和“p-n”(旧版,即将废弃)插件 <p>* 3、此方法是【同步】的,耗时较少 <p>* 4、不会触发插件“启动”逻辑,因此只要插件“当前没有被使用”,再次调用此方法则新插件立即生效** @param path 插件安装的地址。必须是“绝对路径”。通常可以用context.getFilesDir()来做* @return 安装成功的插件信息,外界可直接读取* @since 2.0.0 (1.x版本为installDelayed)*/public static PluginInfo install(String path) {if (TextUtils.isEmpty(path)) {throw new IllegalArgumentException();}// 判断文件合法性File file = new File(path);if (!file.exists()) {if (LogDebug.LOG) {LogDebug.e(TAG, "install: File not exists. path=" + path);}return null;} else if (!file.isFile()) {if (LogDebug.LOG) {LogDebug.e(TAG, "install: Not a valid file. path=" + path);}return null;}// 若为p-n开头的插件,则必须是从宿主设置的“插件安装路径”上(默认为files目录)才能安装,其余均不允许if (path.startsWith("p-n-")) {String installPath = RePlugin.getConfig().getPnInstallDir().getAbsolutePath();if (!path.startsWith(installPath)) {if (LogDebug.LOG) {LogDebug.e(TAG, "install: Must be installed from the specified path. Path=" + path + "; Allowed=" + installPath);}return null;}}return MP.pluginDownloaded(path);}

MP.pluginDownloaded(path)

    public static final PluginInfo pluginDownloaded(String path) {if (LOG) {LogDebug.d(PLUGIN_TAG, "MP.pluginDownloaded ... path=" + path);}/*** 问题描述:** 对于正在生效的插件,如果当前时机pluginHost没有存活,那么这里会先启动pluginHost,然后再调用它的PluginHost进程的pluginDownloaded接口** 这里的问题是:pluginHost进程在启动过程会通过扫描文件的方式将当前即将生效的插件识别到,* 而在进程ready后,再去调用pluginDownloaded接口的时候会认为它不是新插件,从而不会通过NEW_PLUGIN广播来周知所有进程新插件生效了* 因此,当前进程也不会同步新插件生效的逻辑。* so,问题就来了,当前进程新下载的插件由于pluginHost的逻辑无法正常生效。* 当然该问题只针对p-n格式的插件,而纯APK格式的插件不再使用进程启动的时候通过扫描文件目录的方式来来识别所有准备好的插件** 解决办法:* 对于处于该流程的插件文件(p-n插件)加上lock文件,以表示当前插件正在生效,不需要plugHost进程再次扫描生效了,也就不存在新插件在新进程中成为了老插件*/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;}

PmHostSvc.java

    @Overridepublic 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;}

PluginManagerServer.java

   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;}

3.插件的加载

RePlugin.java

        public static void attachBaseContext(Application app, RePluginConfig config) {if (sAttached) {if (LogDebug.LOG) {LogDebug.d(TAG, "attachBaseContext: Already called");}return;}RePluginInternal.init(app);sConfig = config;sConfig.initDefaults(app);IPC.init(app);// 打印当前内存占用情况// 只有开启“详细日志”才会输出,防止“消耗性能”if (LOG && RePlugin.getConfig().isPrintDetailLog()) {LogDebug.printMemoryStatus(LogDebug.TAG, "act=, init, flag=, Start, pn=, framework, func=, attachBaseContext, lib=, RePlugin");}// 初始化HostConfigHelper(通过反射HostConfig来实现)// NOTE 一定要在IPC类初始化之后才使用HostConfigHelper.init();// FIXME 此处需要优化掉AppVar.sAppContext = app;// Plugin Status ControllerPluginStatusController.setAppContext(app);PMF.init(app);PMF.callAttach();sAttached = true;}

PMF.callAttach();

public static final void callAttach() {sPluginMgr.callAttach();}
    final void callAttach() {//mClassLoader = PmBase.class.getClassLoader();// 挂载for (Plugin p : mPlugins.values()) {p.attach(mContext, mClassLoader, mLocal);}// 加载默认插件if (PluginManager.isPluginProcess()) {if (!TextUtils.isEmpty(mDefaultPluginName)) {//Plugin p = mPlugins.get(mDefaultPluginName);if (p != null) {boolean rc = p.load(Plugin.LOAD_APP, true);if (!rc) {if (LOG) {LogDebug.d(PLUGIN_TAG, "failed to load default plugin=" + mDefaultPluginName);}}if (rc) {mDefaultPlugin = p;mClient.init(p);}}}}}

boolean rc = p.load(Plugin.LOAD_APP, true);

    final boolean load(int load, boolean useCache) {PluginInfo info = mInfo;boolean rc = loadLocked(load, useCache);// 尝试在此处调用Application.onCreate方法// Added by Jiongxuan Zhangif (load == LOAD_APP && rc) {callApp();}// 如果info改了,通知一下常驻// 只针对P-n的Type转化来处理,一定要通知,这样Framework_Version也会得到更新if (rc && mInfo != info) {UpdateInfoTask task = new UpdateInfoTask((PluginInfo) mInfo.clone());Tasks.post2Thread(task);}return rc;}

4.启动插件的Activity

RePlugin.java

    public static boolean startActivity(Context context, Intent intent) {// TODO 先用旧的开启Activity方案,以后再优化ComponentName cn = intent.getComponent();if (cn == null) {// TODO 需要支持Action方案return false;}String plugin = cn.getPackageName();String cls = cn.getClassName();return Factory.startActivityWithNoInjectCN(context, intent, plugin, cls, IPluginManager.PROCESS_AUTO);}

PluginLibraryInternalProxy.java

public boolean startActivity(Context context, Intent intent, String plugin, String activity, int process, boolean download)ComponentName cn = mPluginMgr.mLocal.loadPluginActivity(intent, plugin, activity, process);if (cn == null) {if (LOG) {LogDebug.d(PLUGIN_TAG, "plugin cn not found: intent=" + intent + " plugin=" + plugin + " activity=" + activity + " process=" + process);}return false;}
public ComponentName loadPluginActivity(Intent intent, String plugin, String activity, int process) {ActivityInfo ai = null;String container = null;PluginBinderInfo info = new PluginBinderInfo(PluginBinderInfo.ACTIVITY_REQUEST);try {// 获取 ActivityInfo(可能是其它插件的 Activity,所以这里使用 pair 将 pluginName 也返回)ai = getActivityInfo(plugin, activity, intent);if (ai == null) {if (LOG) {LogDebug.d(PLUGIN_TAG, "PACM: bindActivity: activity not found");}return null;}// 存储此 Activity 在插件 Manifest 中声明主题到 Intentintent.putExtra(INTENT_KEY_THEME_ID, ai.theme);if (LOG) {LogDebug.d("theme", String.format("intent.putExtra(%s, %s);", ai.name, ai.theme));}// 根据 activity 的 processName,选择进程 ID 标识if (ai.processName != null) {process = PluginClientHelper.getProcessInt(ai.processName);}// 容器选择(启动目标进程)IPluginClient client = MP.startPluginProcess(plugin, process, info);if (client == null) {return null;}// 远程分配坑位container = client.allocActivityContainer(plugin, process, ai.name, intent);......
}

IPluginClient client = MP.startPluginProcess(plugin, process, info);

public static final IPluginClient startPluginProcess(String plugin, int process, PluginBinderInfo info) throws RemoteException {return PluginProcessMain.getPluginHost().startPluginProcess(plugin, process, info);}
/*** sPluginHostLocal 常驻进程使用,非常驻进程为null buyuntao* sPluginHostRemote 非常驻进程使用,常驻进程为null,用于非常驻进程连接常驻进程  buyuntao* @hide 内部框架使用*/public static final IPluginHost getPluginHost() {if (sPluginHostLocal != null) {return sPluginHostLocal;}// 可能是第一次,或者常驻进程退出了if (sPluginHostRemote == null) {if (LogDebug.LOG) {if (IPC.isPersistentProcess()) {LogDebug.e(PLUGIN_TAG, "插件框架未正常初始化");throw new RuntimeException("插件框架未正常初始化");}}// 再次唤起常驻进程connectToHostSvc();}return sPluginHostRemote;}
class PmHostSvc extends IPluginHost.Stub {@Overridepublic IPluginClient startPluginProcess(String plugin, int process, PluginBinderInfo info) throws RemoteException {return mPluginMgr.startPluginProcessLocked(plugin, process, info);}...}
class PluginProcessPer extends IPluginClient.Stub {public String allocActivityContainer(String plugin, int process, String target, Intent intent) throws RemoteException {// 一旦有分配,则进入监控状态(一是避免不退出的情况,二也是最重要的是避免现在就退出的情况)RePlugin.getConfig().getEventCallbacks().onPrepareAllocPitActivity(intent);String loadPlugin = null;// 如果UI进程启用,尝试使用传过来的插件,强制用UI进程if (Constant.ENABLE_PLUGIN_ACTIVITY_AND_BINDER_RUN_IN_MAIN_UI_PROCESS) {if (IPC.isUIProcess()) {loadPlugin = plugin;process = IPluginManager.PROCESS_UI;} else {loadPlugin = plugin;}}// 如果不成,则再次尝试使用默认插件if (TextUtils.isEmpty(loadPlugin)) {if (mDefaultPlugin == null) {if (LOGR) {LogRelease.e(PLUGIN_TAG, "a.a.c p i n");}return null;}loadPlugin = mDefaultPlugin.mInfo.getName();}//String container = bindActivity(loadPlugin, process, target, intent);if (LOG) {LogDebug.d(PLUGIN_TAG, "PACM: eval plugin " + loadPlugin + ", target=" + target + ", container=" + container);}return container;}...
}

5.Replugin Hook系统ClassLoader

application初始化 PMF.java

public static final void init(Application application) {setApplicationContext(application);PluginManager.init(application);sPluginMgr = new PmBase(application);sPluginMgr.init();Factory.sPluginManager = PMF.getLocal();Factory2.sPLProxy = PMF.getInternal();PatchClassLoaderUtils.patch(application);}

PatchClassLoaderUtils.patch(application);

/*** 对宿主的HostClassLoader做修改。这是RePlugin中唯一需要修改宿主私有属性的位置了** @author RePlugin Team*/
public class PatchClassLoaderUtils {private static final String TAG = "PatchClassLoaderUtils";public static boolean patch(Application application) {try {// 获取Application的BaseContext (来自ContextWrapper)Context oBase = application.getBaseContext();if (oBase == null) {if (LOGR) {LogRelease.e(PLUGIN_TAG, "pclu.p: nf mb. ap cl=" + application.getClass());}return false;}// 获取mBase.mPackageInfo// 1. ApplicationContext - Android 2.1// 2. ContextImpl - Android 2.2 and higher// 3. AppContextImpl - Android 2.2 and higherObject oPackageInfo = ReflectUtils.readField(oBase, "mPackageInfo");if (oPackageInfo == null) {if (LOGR) {LogRelease.e(PLUGIN_TAG, "pclu.p: nf mpi. mb cl=" + oBase.getClass());}return false;}// mPackageInfo的类型主要有两种:// 1. android.app.ActivityThread$PackageInfo - Android 2.1 - 2.3// 2. android.app.LoadedApk - Android 2.3.3 and higherif (LOG) {Log.d(TAG, "patch: mBase cl=" + oBase.getClass() + "; mPackageInfo cl=" + oPackageInfo.getClass());}// 获取mPackageInfo.mClassLoaderClassLoader oClassLoader = (ClassLoader) ReflectUtils.readField(oPackageInfo, "mClassLoader");if (oClassLoader == null) {if (LOGR) {LogRelease.e(PLUGIN_TAG, "pclu.p: nf mpi. mb cl=" + oBase.getClass() + "; mpi cl=" + oPackageInfo.getClass());}return false;}// 外界可自定义ClassLoader的实现,但一定要基于RePluginClassLoader类ClassLoader cl = RePlugin.getConfig().getCallbacks().createClassLoader(oClassLoader.getParent(), oClassLoader);// 将新的ClassLoader写入mPackageInfo.mClassLoaderReflectUtils.writeField(oPackageInfo, "mClassLoader", cl);// 设置线程上下文中的ClassLoader为RePluginClassLoader// 防止在个别Java库用到了Thread.currentThread().getContextClassLoader()时,“用了原来的PathClassLoader”,或为空指针Thread.currentThread().setContextClassLoader(cl);if (LOG) {Log.d(TAG, "patch: patch mClassLoader ok");}} catch (Throwable e) {e.printStackTrace();return false;}return true;}
}

ClassLoader cl = RePlugin.getConfig().getCallbacks().createClassLoader(oClassLoader.getParent(), oClassLoader);

    public RePluginClassLoader createClassLoader(ClassLoader parent, ClassLoader original) {return new RePluginClassLoader(parent, original);}

Replugin的ClassLoade

在Replugin中有两个ClassLoader,一个加载宿主RePluginClassLoader,一个加载插件apk类的PluginDexClassLoader

RePluginClassLoader.java

@Overrideprotected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {//Class<?> c = null;c = PMF.loadClass(className, resolve);if (c != null) {return c;}//try {c = mOrig.loadClass(className);// 只有开启“详细日志”才会输出,防止“刷屏”现象if (LogDebug.LOG && RePlugin.getConfig().isPrintDetailLog()) {LogDebug.d(TAG, "loadClass: load other class, cn=" + className);}return c;} catch (Throwable e) {//}//return super.loadClass(className, resolve);}

PluginDexClassLoader.java

    @Overrideprotected Class<?> loadClass(String className, boolean resolve) throws ClassNotFoundException {// 插件自己的Class。从自己开始一直到BootClassLoader,采用正常的双亲委派模型流程,读到了就直接返回Class<?> pc = null;ClassNotFoundException cnfException = null;try {pc = super.loadClass(className, resolve);if (pc != null) {// 只有开启“详细日志”才会输出,防止“刷屏”现象if (LogDebug.LOG && RePlugin.getConfig().isPrintDetailLog()) {LogDebug.d(TAG, "loadClass: load plugin class, cn=" + className);}return pc;}} catch (ClassNotFoundException e) {// Do not throw "e" nowcnfException = e;if (PluginDexClassLoaderPatch.need2LoadFromHost(className)) {try {return loadClassFromHost(className, resolve);} catch (ClassNotFoundException e1) {// Do not throw "e1" nowcnfException = e1;if (LogDebug.LOG) {LogDebug.e(TAG, "loadClass ClassNotFoundException, from HostClassLoader&&PluginClassLoader, cn=" + className + ", pluginName=" + mPluginName);}}} else {if (LogDebug.LOG) {LogDebug.e(TAG, "loadClass ClassNotFoundException, from PluginClassLoader, cn=" + className + ", pluginName=" + mPluginName);}}}// 若插件里没有此类,则会从宿主ClassLoader中找,找到了则直接返回// 注意:需要读取isUseHostClassIfNotFound开关。默认为关闭的。可参见该开关的说明if (RePlugin.getConfig().isUseHostClassIfNotFound()) {try {return loadClassFromHost(className, resolve);} catch (ClassNotFoundException e) {// Do not throw "e" nowcnfException = e;}}// At this point we can throw the previous exceptionif (cnfException != null) {throw cnfException;}return null;}

Android 插件化框架replugin replugin-host-library 源码解析相关推荐

  1. 安卓Android与H5双向交互MathJax展示数学公式(源码+解析)

    安卓Android与H5双向交互MathJax展示数学公式(源码+解析) 博主就今天周五又做了个需求(安卓Android与H5交互),原来上线的功能是服务器配置过来的学习报告(一个H5页面)并提供原始 ...

  2. Shadow插件化框架设计——replugin原理(架构师进阶之旅)

    DroidPlugin原理解析 从系统设计的角度,组件和窗口的逻辑实体都存在于系统服务,比如Activity创建后,其逻辑控制主体在AMS,对于窗口,其逻辑控制主体在WMS android将逻辑主体放 ...

  3. 滴滴开源Android插件化框架VirtualAPK原理分析

    概述 滴滴出行公司的首个对外开源项目 - VirtualAPK.地址:github.com/didi/Virtua- 滴滴自行研发了这款插件化框架,功能全面.兼容性好,还能够适用于有耦合的业务插件,这 ...

  4. Android 插件化框架DroidPlugin

    上一次项目迭代中,接触到了插件化框架. 使用场景:我们的app需要集成某一直播app.即在不安装第三方直播app到手机的情况下,点击我们app内部的某一连接能跳转到直播app中,运行里面原有的所有功能 ...

  5. 通讯框架 t-io 学习——websocket 部分源码解析

    前言 前端时间看了看t-io的websocket部分源码,于是抽时间看了看websocket的握手和他的通讯机制.本篇只是简单记录一下websocket握手部分. WebSocket握手 好多人都用过 ...

  6. Android特别的数据结构(二)ArrayMap源码解析

    1. 数据结构 public final class ArrayMap<K,V> implements Map<K,V> 由两个数组组成,一个int[] mHashes用来存放 ...

  7. Android特别的数据结构(一) SparseArray源码解析

    1.数据结构 class SparseArray<E> implements Cloneable 由两个数组构成,一个数组mKeys类型为int[],存放Key,一个数组mValues类型 ...

  8. EasyPusher实现安卓Android手机直播推送同步录像功能(源码解析)

    EasyPusher是一款非常棒的推送客户端.稳定.高效.低延迟,音视频同步等都特别好.装在安卓上可作为一款单兵设备来用.说到单兵,在项目中通常都需要边传边录的功能,因此后来EasyPusher也加入 ...

  9. Android 插件化原理 完胜360插件框架 技术实战

    性能优化 Android 性能优化 (一)APK高效瘦身 http://blog.csdn.net/whb20081815/article/details/70140063 Android 性能优化 ...

  10. Android插件化技术调研

    一.技术背景 Android的插件化技术,目前已经比较成熟,微信.淘宝.携程.360手机助手中都应用到了插件化.插件化技术的特点是无需单独安装apk,即可运行,即插即用,无需升级宿主应用,减少app的 ...

最新文章

  1. 使用 lombok 简化 Java 代码
  2. 你知道我今天为什么来公司上班吗?
  3. ajax 阻止默认提交,jQuery验证插件:在对ajax调用servlet时,submitHandler不会阻止默认提交-返回false无效...
  4. LeetCode39.组合总和 JavaScript
  5. c++ vector最大值_第14章 火柴人的无尽冒险(《C和C++游戏趣味编程》配套教学视频)...
  6. C#语法基础之第三节
  7. Vector找最大值 最小值
  8. 中等职业计算机等级考试,中等职业学校计算机等级考试题库(含答案):EXCEL
  9. 最长递增字串的三种做法
  10. django orm 操作表
  11. OSPF虚链路技术原理与注意点
  12. ios开发之商城类软件 - 框架构思
  13. Alex 的 Hadoop 菜鸟教程: 第3课 Hadoop 安装教程 - 非HA方式 (一台server)
  14. 企业级PHP发卡网源码,金发卡企业级发卡平台源码
  15. 色彩设计原理(里面有配色方案,也有配色网站)
  16. 奥克兰大学商学院计算机专业,奥克兰大学的商科专业 推荐三大专业
  17. 手机端 19FPS 的实时目标检测算法:YOLObile
  18. 大学医学院有计算机专业吗,上大学时辛苦一点,将来工作轻松一点,这些专业可以做到...
  19. 树上统计——基于树的搜索
  20. mysql中BY是什么意思,order是什么意思-sql中的orderby是什么意思它是在什 – 手机爱问...

热门文章

  1. GSM 第二代移动通信网络
  2. 【三极管知识】之【9011,9012,9013,9014,8050,8550 三极管的区别】
  3. KRC跨境商城系 拍卖系统 竞拍系统 商城系统 虚拟支付源码
  4. 化学到底是不是一个好专业?该不该转行?————试图以此文终结所有相关讨论
  5. 阿里巴巴开源项目汇总-(前端)
  6. HTML5代码雨程序
  7. Leetcode——904. 水果成篮
  8. pwnable.kr 第一题fd
  9. 人工智能工程化丨中小企业AI中台落地指南
  10. B站的经典封面制作方法