Open huaf22 opened 6 years ago
能详细描述下特殊场景么? 我们对bundle的start 是做了同步的。
@hwjump 我的项目中有一个 FirstBundle 依赖 LibBundle,LibBundle的Application的onCreate方法中调用了Host的一个方法通过 DelegateClassLoader 加载LibBundle中的一个类; 运行时是 Host 先安装 FirstBundle,FirstBundle 中加载了 LibBundle 中的一个类,这时就会触发LibBundle的安装, 当 Atlas 框架调用 LibBundle 的Application的onCreate方法时,onCreate方法会调用Host的一个方法来加载一个LibBundle中的类,这时DelegateClassLoader会走到loadFromInstalledBundles方法通过遍历每个Bundle来查找类,查找到 FirstBundle 时,因为FirstBundle有一个 LibBundle 的依赖,所以又会去通过下面的代码查找 LibBundle 中的类
BundleClassLoader类的findClass方法代码片段: // find class in dependency bundle if (dependencies != null) { for (String dependencyBundle : dependencies) { try { BundleImpl impl = (BundleImpl) Atlas.getInstance().getBundle(dependencyBundle); if(impl!=null) { impl.startBundle(); clazz = ((BundleClassLoader) impl.getClassLoader()).loadOwnClass(classname); if (clazz != null) { return clazz; } }else{ Log.e("BundleClassLoader",String.format("%s is not success installed by %s",""+dependencyBundle,location)); } } catch (Throwable e) { e.printStackTrace(); } } }
虽然你说Atlas框架中通过了synchronized的方式同步startBundle方法,但是debug查看方法栈时的确会发现同一个 BundleImpl对象的startBundle方法会嵌套调用,并且所有调用都在主线程中; AtlasCore的版本为5.0.7.55,AtlasPlugin的版本为2.3.3.rc37
我们其实是不建议使用 DelegateClassLoader 加载LibBundle中的一个类,1来慢,2来隔离性太差。 所以我建议你改一下代码结构,做个更好的隔离出来。 另外后续我们会加强DelegateClassLoader的访问。劲量不让外部直接访问到。
@hwjump 那只能先用Host的PathClassLoader来查找,查找不到再根据类的包名去找相应的BundleClassLoader去加载类,淘宝的 ServerHub 框架是怎样加载不同bundle中的类的?
BundleClassLoader 的 startBundle 方法中没有对 STARTING 的 state 进行处理, 在特殊场景下会导致该 Bundle 的自定义 Application 的 onCreate 方法会被重复调用,最后会导致方法栈溢出
` public synchronized void startBundle() { if (state == UNINSTALLED) { throw new IllegalStateException("Cannot start uninstalled bundle " + toString()); } if (state == ACTIVE) { return; } if (state == INSTALLED) { throw new RuntimeException("can not start bundle which is not resolved"); } // 需要添加对 STARTING 状态的判断 if (state == STARTING) { return; } state = STARTING; Framework.notifyBundleListeners(BundleEvent.BEFORE_STARTED, this); Framework.notifyBundleListeners(BundleEvent.STARTED, this); if (Framework.DEBUG_BUNDLES) { Log.i("Framework","Bundle " + toString() + " started."); } }
`