记一次不同ClassLoader问题

记一次不同ClassLoader问题

业务中心日志打点明明时单例却有多个实例
MweeLogInner 的init方法

 private static LoggerHandler logHandler;
 private static LogRepository logDBUtil;
synchronized (MwLogInner.class) {
            if (inited && !TextUtils.isEmpty(shopId) && !TextUtils.isEmpty(hostId)) {
                return;
            }
            inited = true;
        }

这样写应该只会被init一次 debug的时候却有多个logDBUtil和logHandler实例 怀疑和插件有关系。

(https://upload-images.jianshu.io/upload_images/1839763-d406f6dbae42b1f2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
.png)

image-20190130122012793.png

分别打印出ClassLoader 可以看到确实不是同一个classloader

深入理解Android中的ClassLoader

ClassLoader双亲加载机制:先看该类有没加载, 没加载先让parent去加载 parent加载不到再自己去加载.

DexClassLoader和pathClassLoader都是派生自BaseClassLoader :

DexClassLoader有个optimizedDirectory参数可以加载任何路径的apk/dex/jar , PathClassLoader没有optimizedDirectory,所以它只能加载内部的dex,这些大都是存在系统中已经安装过的apk里面的,比如/data/app中的apk。这个也是PathClassLoader作为默认的类加载器的原因,因为一般程序都是安装了,在打开,这时候PathClassLoader就去加载指定的apk(解压成dex,然后在优化成odex)就可以了

所以很有可能是com.mwee.plugin.PluginClassLoader加载的是插件apk中的dex,而系统的PathClassLoader 加载的是base apk中的dex 这就是产生不同实例的原因(根本不是同一个类)一个类,由不同的类加载器实例加载的话,会在方法区产生两个不同的类,彼此不可见,并且在堆中生成不同Class实例。因此单例也是相互隔离的


分析

DinnerApplication

 @Override
    public void onCreate() {
        //需要阻塞
        AppLog.initConfig(this);
        //启动业务中心
        if (ProcessUtil.isMainProcess(this)) {
            if (!(ClientBindProcessor.isActived() && !ClientBindProcessor.isCurrentHostMain())) {
                WakeUpBizCenter.callServerProcess(this);
                ServerConnector.getInstance().initConnection(this);
            }
        }
    }
  • 这里绑定到ServerService 服务开启bizcenter进程重新走到oncreate方法 这里 AppLog.initConfig(this)中的类 由系统的类加载器加载

  • ServerSevice onCreate中loader = MydPluginLoader.getInstance(getApplicationContext());检查插件更新

  • loadPlugin()->loadApl()->active()->createPluginProxy()

PluginProcessor

protected void loadApk(Context context, String apkPath) {
        // 4.1以后不能够将optimizedDirectory设置到sd卡目录, 否则抛出异常.
        File optimizedDirectoryFile = context.getDir("dex", 0);
        classLoader = new PluginClassLoader(apkPath, optimizedDirectoryFile.getAbsolutePath(),
                null, context.getClassLoader());

        prepareResouce(context, apkPath);

    }

serverProxy = pluginManger.reflectNewObject(CLZ_PROXY);

 protected Object reflectNewObject(String clz) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        Class<?> mLoadClass = classLoader.loadClass(clz);
        Constructor constructor = mLoadClass.getConstructor();
        return constructor.newInstance();
    }

所以ServerProxy是由自定义的ClassLoader加载的.之后通过反射调用ServerProxy.work到BizCenterApplication.initEnv 其中的ServerLog.initConfig(context)也是由PluginClassLoader从dex中加载的

解决

DinnerApplication中先做判断主进程和打印init,业务中心进程不init

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容