美文网首页
Tinker加载流程

Tinker加载流程

作者: Utte | 来源:发表于2018-10-15 18:46 被阅读51次

Application代理

前面只是从TinkerInstaller的两个api去分析了流程,但是分析完毕了,仍然有一些我们还没有涉及到的内容:

  • 记得我们使用Tinker时自定义过一个ApplicationLike,并使用了DefaultLifeCycle注解。
  • 在Manifest中,我们设置了application为注解参数的application。

注解帮我们自动生成了application,这其中发生了什么?

1. DefaultLifeCycle注解生成Application

@DefaultLifeCycle(application = ".MyTinkerApplication",
        flags = ShareConstants.TINKER_ENABLE_ALL,
        loadVerifyFlag = false)
public class CustomApplicationLike extends ApplicationLike {
    // ......
}

注解对应的AbstractProcessor是AnnotationProcessor。

1-1 AnnotationProcessor # getSupportedAnnotationTypes()

getSupportedAnnotationTypes()会返回该Processor处理的所有注解集合,只有DefaultLifeCycle。

@Override
public Set<String> getSupportedAnnotationTypes() {
    final Set<String> supportedAnnotationTypes = new LinkedHashSet<>();
    supportedAnnotationTypes.add(DefaultLifeCycle.class.getName());
    return supportedAnnotationTypes;
}
1-2 AnnotationProcessor # process()

这个方法才是编译时检测到注解而执行的工作。调用了processDefaultLifeCycle()。

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    processDefaultLifeCycle(roundEnv.getElementsAnnotatedWith(DefaultLifeCycle.class));
    return true;
}
1-3 AnnotationProcessor # processDefaultLifeCycle()

读取了一个模板文件,去匹配要修改的字符串,修改成注解的参数设置。就这样为我们自动生成了Application文件。

private void processDefaultLifeCycle(Set<? extends Element> elements) {
    // DefaultLifeCycle
    for (Element e : elements) {
        // 从注解参数得到下面需要替换为的值
        DefaultLifeCycle ca = e.getAnnotation(DefaultLifeCycle.class);
        String lifeCycleClassName = ((TypeElement) e).getQualifiedName().toString();
        String lifeCyclePackageName = lifeCycleClassName.substring(0, lifeCycleClassName.lastIndexOf('.'));
        lifeCycleClassName = lifeCycleClassName.substring(lifeCycleClassName.lastIndexOf('.') + 1);
        String applicationClassName = ca.application();
        if (applicationClassName.startsWith(".")) {
            applicationClassName = lifeCyclePackageName + applicationClassName;
        }
        String applicationPackageName = applicationClassName.substring(0, applicationClassName.lastIndexOf('.'));
        applicationClassName = applicationClassName.substring(applicationClassName.lastIndexOf('.') + 1);
        String loaderClassName = ca.loaderClass();
        if (loaderClassName.startsWith(".")) {
            loaderClassName = lifeCyclePackageName + loaderClassName;
        }
        
        System.out.println("*");
        // 读取该模板文件"/TinkerAnnoApplication.tmpl";
        final InputStream is = AnnotationProcessor.class.getResourceAsStream(APPLICATION_TEMPLATE_PATH);
        final Scanner scanner = new Scanner(is);
        final String template = scanner.useDelimiter("\\A").next();
        // 替换文件中的各种字段
        final String fileContent = template
            .replaceAll("%PACKAGE%", applicationPackageName)
            .replaceAll("%APPLICATION%", applicationClassName)
            .replaceAll("%APPLICATION_LIFE_CYCLE%", lifeCyclePackageName + "." + lifeCycleClassName)
            .replaceAll("%TINKER_FLAGS%", "" + ca.flags())
            .replaceAll("%TINKER_LOADER_CLASS%", "" + loaderClassName)
            .replaceAll("%TINKER_LOAD_VERIFY_FLAG%", "" + ca.loadVerifyFlag());
        // 再将文件写出去
        try {
            JavaFileObject fileObject = processingEnv.getFiler().createSourceFile(applicationPackageName + "." + applicationClassName);
            processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, "Creating " + fileObject.toUri());
            Writer writer = fileObject.openWriter();
            try {
                PrintWriter pw = new PrintWriter(writer);
                pw.print(fileContent);
                pw.flush();
            } finally {
                writer.close();
            }
        } catch (IOException x) {
            processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, x.toString());
        }
    }
}
1-4 生成application

模板文件

package %PACKAGE%;

import com.tencent.tinker.loader.app.TinkerApplication;

/**
 *
 * Generated application for tinker life cycle
 *
 */
public class %APPLICATION% extends TinkerApplication {

    public %APPLICATION%() {
        super(%TINKER_FLAGS%, "%APPLICATION_LIFE_CYCLE%", "%TINKER_LOADER_CLASS%", %TINKER_LOAD_VERIFY_FLAG%);
    }

}

可以看到模板中生成的Application是继承了TinkerApplication的。模板很简单所以大部分逻辑都在TinkerApplication中。super()传入了CustomApplicationLike这个自定义的ApplicationLike,想必TinkerApplication和ApplicationLike关系密切。

public class MyTinkerApplication extends TinkerApplication {
    public MyTinkerApplication() {
        super(7, "com.utte.tinkertest.tinker.module.CustomApplicationLike", "com.tencent.tinker.loader.TinkerLoader", false);
    }
}

2. ApplicationLike

在我们的CustomApplicationLike的onBaseContextAttached()方法中主要是需要调用Tinker初始化的方法,这是官方建议的。

@DefaultLifeCycle(application = ".MyTinkerApplication",
        flags = ShareConstants.TINKER_ENABLE_ALL,
        loadVerifyFlag = false)
public class CustomApplicationLike extends ApplicationLike {

    // ......构造器

    /**
     * 需要在这个方法中完成Tinker的初始化
     * @param base
     */
    @Override
    public void onBaseContextAttached(Context base) {
        super.onBaseContextAttached(base);
        // 使应用支持分包
        MultiDex.install(base);
        // 初始化Tinker
        TinkerManager.installTinker(this);
    }
}
2-1 生命周期

Application实现了ApplicationLifeCycle

public abstract class ApplicationLike implements ApplicationLifeCycle

ApplicationLifeCycle其实就相当于Application生命周期回调和其他一些特殊情况回调的接口,自然ApplicationLike中就实现了这些了。

public interface ApplicationLifeCycle {

    void onCreate();

    void onLowMemory();

    void onTrimMemory(int level);

    void onTerminate();

    void onConfigurationChanged(Configuration newConfig);

    void onBaseContextAttached(Context base);
}

但是再看ApplicationLike,发现是空实现。

@Override
public void onCreate() {
}
@Override
public void onLowMemory() {
}
......

这就只有一种解释,这些回调方法是让我们自定义的ApplicationLike去实现的。似乎能够想到,Application是注解为我们自动生成的,我们不好去编写Application中的代码,而如果Application调用ApplicationLike的这些方法的话,我们去在ApplicationLike编写代码,就达到效果了。

3. TinkerApplicatoin

TinkerApplication自然是继承了Application的。我们看一看Application回调代码,果然验证了之前的猜想。

3-1 猜想验证

onCreate()中调用了applicationLike的onCreate(),其他的回调方法也是这样,都调用了applicationLike的对应方法。这其实就是用到了代理模式。

@Override
public void onCreate() {
    // ......
    applicationLike.onCreate();
}
3-2 TinkerApplication # attachBaseContext()

天真的以为第一步去看onCreate()吗,那就错了,先看attachBaseContext(),这个方法在onCreate()前就会被调用,如果有需求需要将初始化工作更提前,就可以在这个方法中进行。在这里调用了onBaseContextAttached()。

@Override
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    Thread.setDefaultUncaughtExceptionHandler(new TinkerUncaughtHandler(this));
    onBaseContextAttached(base);
}

这里很关键,主要做了三个工作:

  1. 反射获取TinkerLoader,反射拿到其tryLoad()并执行。
  2. 初始化ApplicationLike。
  3. 调用ApplicationLike对应方法。

这样的调用顺序,先tryLoad()再加载ApplicationLike,实际上就保证了Application也能被修复。但是这里说的并不是Application自身,而是它的代理ApplicationLike,如果将Application中的代码完全交给代理AplicationLike,就能做到这一点了。

private void onBaseContextAttached(Context base) {
    // ......赋值时间相关成员变量
    
    // 加载TinkerLoad,执行tryLoad()
    loadTinker();
    
    // 加载applicationLike并赋值
    ensureDelegate();
    
    // 调用代理的onBaseContextAttached()
    applicationLike.onBaseContextAttached(base);
    
    // ......SharedPreference

}

见识少的我觉得这里的设计好棒。如果没有ApplicationLike,那么就算tryLoad()再早,也早不过Application加载,那样的话就不支持Application的修复了,而利用了代理模式,将代码转移到代理中,代理就能够在Tinker之后加载,就可以解决这个问题了。

3-3 TinkerApplication # loadTinker()

再来具体的看一下这方法,就是反射去执行tryLoad()。

private void loadTinker() {
    //reflect tinker loader, because loaderClass may be define by user!
    Class<?> tinkerLoadClass = Class.forName(loaderClassName, false, getClassLoader());
    
    Method loadMethod = tinkerLoadClass.getMethod(TINKER_LOADER_METHOD, TinkerApplication.class);
    Constructor<?> constructor = tinkerLoadClass.getConstructor();
    
    tinkerResultIntent = (Intent) loadMethod.invoke(constructor.newInstance(), this);
}

tryLoad()又调用了tryLoadPatchFilesInternal(),又是一个好长的方法,但是大部分都是检查判断,最重要的部分如下代码,去加载patch中的Dex文件和加载patch中的资源文件,这些内容我们在后序再开展。

private void tryLoadPatchFilesInternal(TinkerApplication app, Intent resultIntent) {

    // ......各种检查和准备下面方法调用参数的工作
    
    //now we can load patch jar
    if (isEnabledForDex) {
        // 加载DEX文件
        boolean loadTinkerJars = TinkerDexLoader.loadTinkerJars(app, patchVersionDirectory, oatDex, resultIntent, isSystemOTA);
        // ......
        if (!loadTinkerJars) {
            Log.w(TAG, "tryLoadPatchFiles:onPatchLoadDexesFail");
            return;
        }
    }
    //now we can load patch resource
    if (isEnabledForResource) {
        // 加载资源文件
        boolean loadTinkerResources = TinkerResourceLoader.loadTinkerResources(app, patchVersionDirectory, resultIntent);
        if (!loadTinkerResources) {
            Log.w(TAG, "tryLoadPatchFiles:onPatchLoadResourcesFail");
            return;
        }
    }
    // 各种后续工作......
}
3-4 TinkerApplication # ensureDelegate()

在调用完tryLoad()后,通过反射拿到applicationLike对象并赋值全局变量。对象名delegateClassName是自动生成的application传入的,就是我们加注解的类。

private synchronized void ensureDelegate() {
    if (applicationLike == null) {
        applicationLike = createDelegate();
    }
}

private ApplicationLike createDelegate() {
    try {
        // Use reflection to create the delegate so it doesn't need to go into the primary dex.
        // And we can also patch it
        Class<?> delegateClass = Class.forName(delegateClassName, false, getClassLoader());
        Constructor<?> constructor = delegateClass.getConstructor(Application.class, int.class, boolean.class,
            long.class, long.class, Intent.class);
        return (ApplicationLike) constructor.newInstance(this, tinkerFlags, tinkerLoadVerifyFlag,
            applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
    } catch (Throwable e) {
        throw new TinkerRuntimeException("createDelegate failed", e);
    }
}

初始化applicationLike完毕,就接着调用其对应的回调。

applicationLike.onBaseContextAttached(base);

之后继续,onCreate()......等等

合成与加载调用回顾

经过前两篇的分析,大致调用执行流程已经清楚了,剩下就是真正的修复算法了,遗留了两处,一处是我们自己调用api后调用到的UpgradePatch的tryPatch(),一处是app开始时调用的tryLoad()。这两个地方就对应着patch合成和加载两个过程。

分别简单回顾一下这两个过程:

a. 合成过程

  1. Tinker收到服务端获取的patch文件。
  2. 开启TinkerPatchService去执行UpgradePatch的tryPatch()。
  3. tryPatch()中会依次进行dex文件合成、.so文件合成和资源文件合成。
  4. 将合成后的新文件放在tinker加载的文件路径中。

b. 加载过程

  1. TinkerApplication的onBaseContextAttached()中反射调用了TinkerLoader的tryLoad()。
  2. 之后分别调用loadTinkerJars()和loadTinkerResources()去加载dex文件和资源文件。

与AndFix的native层进行方法替换不同的是,Tinker采用的是Java层的实现。类加载过程中会去一个Element数组中寻找该类信息,Tinker的思路就是在该类被加载之前去将patch修复后的该类包装到Element放到数组的前端,这样在加载的时候,先找到的就是修复后的信息了,从而达到代替的效果。

具体加载合并算法实现部分由于我能力不够就暂时放一放,但是找到了几篇比较好的博客,我也有看,先记下来,以后再仔细研究研究。

相关文章

网友评论

      本文标题:Tinker加载流程

      本文链接:https://www.haomeiwen.com/subject/mxkqzftx.html