alibaba / atlas

A powerful Android Dynamic Component Framework.
Apache License 2.0
8.13k stars 1.48k forks source link

[use][bug]: BundleClassLoader 的 startBundle 方法中没有对 STARTING 状态进行处理 #226

Open huaf22 opened 6 years ago

huaf22 commented 6 years ago

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."); } }

`

hwjump commented 6 years ago

能详细描述下特殊场景么? 我们对bundle的start 是做了同步的。

huaf22 commented 6 years ago

@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

hwjump commented 6 years ago

我们其实是不建议使用 DelegateClassLoader 加载LibBundle中的一个类,1来慢,2来隔离性太差。 所以我建议你改一下代码结构,做个更好的隔离出来。 另外后续我们会加强DelegateClassLoader的访问。劲量不让外部直接访问到。

huaf22 commented 6 years ago

@hwjump 那只能先用Host的PathClassLoader来查找,查找不到再根据类的包名去找相应的BundleClassLoader去加载类,淘宝的 ServerHub 框架是怎样加载不同bundle中的类的?