package com.tws.plugin.core; import java.io.File; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import tws.component.log.TwsLog; import com.tws.plugin.content.LoadedPlugin; import com.tws.plugin.util.FileUtil; import dalvik.system.DexClassLoader; /** * 插件间依赖以及so管理 * * @author yongchen * */ public class PluginClassLoader extends DexClassLoader { private static final String TAG = "rick_Print:PluginClassLoader"; private static Hashtable<String, String> soClassloaderMapper = new Hashtable<String, String>(); private String[] dependencies; private List<DexClassLoader> multiDexClassLoaderList; public PluginClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent, String[] dependencies, List<String> multiDexList) { super(dexPath, optimizedDirectory, libraryPath, parent); this.dependencies = dependencies; if (multiDexList != null) { if (multiDexClassLoaderList == null) { multiDexClassLoaderList = new ArrayList<DexClassLoader>(multiDexList.size()); for (String path : multiDexList) { multiDexClassLoaderList.add(new DexClassLoader(path, optimizedDirectory, libraryPath, parent)); } } } } @Override public String findLibrary(String name) { final String thisLoader = getClass().getName() + '@' + Integer.toHexString(hashCode()); final String soPath = super.findLibrary(name); TwsLog.d(TAG, "findLibrary orignal so path : " + soPath + ", current classloader : " + thisLoader); if (soPath != null) { final String soLoader = soClassloaderMapper.get(soPath); if (soLoader == null || soLoader.equals(thisLoader)) { soClassloaderMapper.put(soPath, thisLoader); TwsLog.d(TAG, "findLibrary acturely so path : " + soPath + ", current classloader : " + thisLoader); return soPath; } else { // classloader发生了变化, 创建so副本并返回副本路径, 限制最多10个副本 for (int i = 1; i < 5; i++) { String soPathOfCopyN = tryPath(soPath, i); String soLoaderOfCopyN = soClassloaderMapper.get(soPathOfCopyN); if (thisLoader.equals(soLoaderOfCopyN)) { TwsLog.d(TAG, "findLibrary acturely so path : " + soPathOfCopyN + ", current classloader : " + thisLoader); return soPathOfCopyN; } else if (soLoaderOfCopyN == null) { if (!new File(soPathOfCopyN).exists()) { boolean isSuccess = FileUtil.copyFile(soPath, soPathOfCopyN); if (isSuccess) { soClassloaderMapper.put(soPathOfCopyN, thisLoader); TwsLog.d(TAG, "findLibrary acturely so path : " + soPathOfCopyN + ", current classloader : " + thisLoader); return soPathOfCopyN; } else { return null; } } else { soClassloaderMapper.put(soPathOfCopyN, thisLoader); TwsLog.d(TAG, "findLibrary acturely so path : " + soPathOfCopyN + ", current classloader : " + thisLoader); return soPathOfCopyN; } } } TwsLog.d(TAG, "findLibrary 最多创建5个副本..."); } } return null; } private String tryPath(String orignalPath, int i) { StringBuilder soPathBuilder = new StringBuilder(orignalPath); soPathBuilder.delete(orignalPath.length() - 3, orignalPath.length());// 移除.so后缀 soPathBuilder.append("_").append(i).append(".so"); return soPathBuilder.toString(); } @Override protected Class<?> findClass(String className) throws ClassNotFoundException { Class<?> clazz = null; ClassNotFoundException suppressed = null; try { clazz = super.findClass(className); } catch (ClassNotFoundException e) { suppressed = e; } // 这里判断android.view 是为了解决webview的问题 if (clazz == null && !className.startsWith("android.view")) { if (multiDexClassLoaderList != null) { for (DexClassLoader dexLoader : multiDexClassLoaderList) { try { clazz = dexLoader.loadClass(className); } catch (ClassNotFoundException e) { } if (clazz != null) { break; } } } if (clazz == null && dependencies != null) { for (String dependencePluginId : dependencies) { // 被依赖的插件可能尚未初始化,确保使用前已经初始化 LoadedPlugin plugin = PluginLauncher.instance().startPlugin(dependencePluginId); if (plugin != null) { try { clazz = plugin.pluginClassLoader.loadClass(className); } catch (ClassNotFoundException e) { } if (clazz != null) { break; } } else { TwsLog.e(TAG, "未找到插件 - id=" + dependencePluginId + " name is " + className); } } } } if (clazz == null && suppressed != null) { throw suppressed; } return clazz; } }