Android热更新实现原理

Ô­Îijö´¦£ºhttp://blog.csdn.net/lzyzsd/article/details/49843581 

×î½üAndroidÉçÇøµÄ·ÕΧºÜ²»´íÂÁ¬Ðø·Å³öһϵÁеÄAndroid¶¯Ì¬¼ÓÔزå¼þºÍÈȸüп⣬ÕâƪÎÄÕ¾ÍÀ´½éÉÜÒ»ÏÂAndroidÖÐʵÏÖÈȸüеÄÔ­Àí¡£

ClassLoader

ÎÒÃÇÖªµÀJavaÔÚÔËÐÐʱ¼ÓÔضÔÓ¦µÄÀàÊÇͨ¹ýClassLoaderÀ´ÊµÏֵģ¬ClassLoader±¾ÉíÊÇÒ»¸ö³éÏóÀ´£¬AndroidÖÐʹÓÃPathClassLoaderÀà×÷ΪAndroidµÄĬÈϵÄÀà¼ÓÔØÆ÷£¬ 
PathClassLoaderÆäʵʵÏֵľÍÊǼòµ¥µÄ´ÓÎļþϵͳÖмÓÔØÀàÎļþ¡£PathClassLoade±¾Éí¼Ì³Ð×ÔBaseDexClassLoader£¬BaseDexClassLoaderÖØдÁËfindClass·½·¨£¬ 
¸Ã·½·¨ÊÇClassLoaderµÄºËÐÄ

@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
    List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
    Class c = pathList.findClass(name, suppressedExceptions);
    if (c == null) {
        ClassNotFoundException cnfe = new ClassNotFoundException("Didn't find class \\"" + name + "\\" on path: " + pathList);
        for (Throwable t : suppressedExceptions) {
            cnfe.addSuppressed(t);
        }
        throw cnfe;
    }
    return c;
}

¿´Ô´Âë¿ÉÖª£¬BaseDexClassLoader½«findClass·½·¨Î¯ÍиøÁËpathList¶ÔÏóµÄfindClass·½·¨£¬pathList¶ÔÏóÊÇÔÚBaseDexClassLoaderµÄ¹¹Ô캯ÊýÖÐnew³öÀ´µÄ£¬ 
ËüµÄÀàÐÍÊÇDexPathList¡£¿´ÏÂDexPathList.findClassÔ´ÂëÊÇÈçºÎ×öµÄ£º

public Class findClass(String name, List<Throwable> suppressed) {
    for (Element element : dexElements) {
        DexFile dex = element.dexFile;
        if (dex != null) {
            Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed);
            if (clazz != null) {
                return clazz;
            }
        }
    }
    if (dexElementsSuppressedExceptions != null) {
        suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
    }
    return null;
}

Ö±½Ó¾ÍÊDZéÀúdexElementsÁÐ±í£¬È»ºóͨ¹ýµ÷ÓÃelement.dexFile¶ÔÏóÉϵÄloadClassBinaryName·½·¨À´¼ÓÔØÀ࣬Èç¹û·µ»ØÖµ²»ÊÇnull£¬¾Í±íʾ¼ÓÔØÀà³É¹¦£¬»á½«Õâ¸öClass¶ÔÏ󷵻ء£ 
¶ødexElements¶ÔÏóÊÇÔÚDexPathListÀàµÄ¹¹Ô캯ÊýÖÐÍê³É³õʼ»¯µÄ¡£

this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions);

makeDexElementsËù×öµÄÊÂÇé¾ÍÊDZéÀúÎÒÃÇ´«µÝÀ´µÄdexPath£¬È»ºóÒ»´Î¼ÓÔØÿ¸ödexÎļþ¡£

ʵÏÖ

ÉÏÃæ·ÖÎöÁËAndroidÖеÄÀàµÄ¼ÓÔصÄÁ÷³Ì£¬¿ÉÒÔ¿´³öÀ´DexPathList¶ÔÏóÖеÄdexElementsÁбíÊÇÀà¼ÓÔصÄÒ»¸öºËÐÄ£¬Ò»¸öÀàÈç¹ûÄܱ»³É¹¦¼ÓÔØ£¬ÄÇôËüµÄdexÒ»¶¨ 
»á³öÏÖÔÚdexElementsËù¶ÔÓ¦µÄdexÎļþÖУ¬²¢ÇÒdexElementsÖгöÏÖµÄ˳ÐòÒ²ºÜÖØÒª£¬ÔÚdexElementsÇ°Ãæ³öÏÖµÄdex»á±»ÓÅÏȼÓÔØ£¬Ò»µ©Class±»¼ÓÔسɹ¦£¬ 
¾Í»áÁ¢¼´·µ»Ø£¬Ò²¾ÍÊÇ˵£¬ÎÒÃǵÄÈç¹ûÏë×öhotpatch£¬Ò»¶¨Òª±£Ö¤ÎÒÃǵÄhotpacth dexÎļþ³öÏÖÔÚdexElementsÁбíµÄÇ°Ãæ¡£

ҪʵÏÖÈȸüУ¬¾ÍÐèÒªÎÒÃÇÔÚÔËÐÐʱȥ¸ü¸ÄPathClassLoader.pathList.dexElements£¬ÓÉÓÚÕâЩÊôÐÔ¶¼ÊÇprivateµÄ£¬Òò´ËÐèҪͨ¹ý·´ÉäÀ´Ð޸ġ£ÁíÍ⣬¹¹ÔìÎÒÃÇ×Ô¼ºµÄdexÎļþ 
Ëù¶ÔÓ¦µÄdexElementsÊý×éµÄʱºò£¬ÎÒÃÇÒ²¿ÉÒÔ²ÉÈ¡Ò»¸ö±È½ÏÈ¡Çɵķ½Ê½£¬¾ÍÊÇͨ¹ý¹¹ÔìÒ»¸öDexClassLoader¶ÔÏóÀ´¼ÓÔØÎÒÃǵÄdexÎļþ£¬²¢ÇÒµ÷ÓÃÒ»´ÎdexClassLoader.loadClass(dummyClassName); 
·½·¨£¬ÕâÑù£¬dexClassLoader.pathList.dexElementsÖУ¬¾Í»á°üº¬ÎÒÃǵÄdex£¬Í¨¹ý°ÑdexClassLoader.pathList.dexElements²åÈ뵽ϵͳĬÈϵÄclassLoader.pathList.dexElementsÁбíÇ°Ã棬¾Í¿ÉÒÔÈÃϵͳÓÅÏȼÓÔØÎÒÃǵÄdexÖеÄÀ࣬´Ó¶ø¿ÉÒÔʵÏÖÈȸüÐÂÁË¡£ÏÂÃæչʾһ²¿·Ö´úÂë

private static synchronized Boolean injectAboveEqualApiLevel14(
            String dexPath, String defaultDexOptPath, String nativeLibPath, String dummyClassName) {
    Log.i(TAG, "--> injectAboveEqualApiLevel14");
    PathClassLoader pathClassLoader = (PathClassLoader) DexInjector.class.getClassLoader();
    DexClassLoader dexClassLoader = new DexClassLoader(dexPath, defaultDexOptPath, nativeLibPath, pathClassLoader);
    try {
        dexClassLoader.loadClass(dummyClassName);
        Object dexElements = combineArray(
                getDexElements(getPathList(pathClassLoader)),
                getDexElements(getPathList(dexClassLoader)));
        Object pathList = getPathList(pathClassLoader);
        setField(pathList, pathList.getClass(), "dexElements", dexElements);
    } catch (Throwable e) {
        e.printStackTrace();
        return false;
    }
    Log.i(TAG, "<-- injectAboveEqualApiLevel14 End.");
    return true;
}

ÍêÕûµÄdemoÇë²Î¿¼ÎÒµÄGitHub

ÉÏÃæÖ»ÊÇ˵ÁËÒ»ÏÂhotpatchµÄÔ­Àí£¬¾ßÌåʵÏÖµÄʱºò£¬Èç¹ûÄãµÄappÓ¦ÓÃÁËmultidex£¬»¹»áÓöµ½ÆäËûµÄ¿Ó£¬Çë²Î¿¼: