美文网首页
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