Tinker源码解析系列(一)—Application代理机制

上次我们分析了一下热修复的原理并着手实践了一下,这次将带大家一起探究Tinker源码。

引言

从上一篇Android热修复原理探索与实践中我们知道,Tinker实现热修复的原理是将自己的全量patch包插入到dexElements数组的前段,从而达到热修复的目的,对这一块还不熟悉的童鞋可以翻看我的上一篇博客。

以下所有对源码的分析均基于Tinker 1.7.7 版本

Application代理机制

通常我们都是在Application中进行一些初始化的工作,包括tinker的初始化,那么application中所涉及到的类,在tinker初始化完成前就已经被类加载器所加载了,那么我们之前所说的通过将我们的补丁dex包插入到dexElements数组的前段的方法就不起作用了,Tinker是怎么解决这一问题的从而达到能修复Application中所使用到的类的呢?

看过tinker的官方接入文档的同学肯定知道,tinker推荐我们将自己的Application继承自ApplicationLike,那么,我们的切入点便是这里,我们先看一下ApplicationLike的源码(源码较长,只贴出关键部分,下同)

public abstract class ApplicationLike implements ApplicationLifeCycle {
    private final Application application;
    private final Intent      tinkerResultIntent;
    private final long        applicationStartElapsedTime;
    private final long        applicationStartMillisTime;
    private final int         tinkerFlags;
    private final boolean     tinkerLoadVerifyFlag;

    public ApplicationLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag,
                           long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent) {
        this.application = application;
        this.tinkerFlags = tinkerFlags;
        this.tinkerLoadVerifyFlag = tinkerLoadVerifyFlag;
        this.applicationStartElapsedTime = applicationStartElapsedTime;
        this.applicationStartMillisTime = applicationStartMillisTime;
        this.tinkerResultIntent = tinkerResultIntent;
    }

    ...
}

可以看到,ApplicationLike并不是一个真正意义上的Android中的Application,因为他并没有继承自Application,仅是一个普通的类实现了ApplicationLifeCycle这个接口,我们先看一下ApplicationLifeCycle这个接口源码

public interface ApplicationLifeCycle {

    /**
     * Same as {@link Application#onCreate()}.
     */
    void onCreate();

    /**
     * Same as {@link Application#onLowMemory()}.
     */
    void onLowMemory();

    /**
     * Same as {@link Application#onTrimMemory(int level)}.
     * @param level
     */
    void onTrimMemory(int level);

    /**
     * Same as {@link Application#onTerminate()}.
     */
    void onTerminate();

    /**
     * Same as {@link Application#onConfigurationChanged(Configuration newconfig)}.
     */
    void onConfigurationChanged(Configuration newConfig);

    /**
     * Same as {@link Application#attachBaseContext(Context context)}.
     */
    void onBaseContextAttached(Context base);
}

这个接口里面的几个方法,相信你一定很眼熟,没错,这几个方法均是跟Application有关的生命周期的方法,那么为什么我们继承的是一个普通的类,并不是一个真正的Application,app却没有崩溃并且可以正常运行呢,相信聪明的你可能已经想到了,没错,tinker所采用的方法便是将Application隔离起来,使用了代理的模式,从而解决了我们文章开头提到的问题。所以这里有一个需要注意的点就是,如果你在ApplicationLike中需要使用到Application对象,那么你不能使用this关键字,因为它并不是一个真正意义上的Application,而需要使用ApplicationLike中的getApplication方法获取application对象。

那么在tinker中,真正的Application是哪个呢? 实际上是TinkerApplication,这里我们先看一下TinkerApplication的源码

public abstract class TinkerApplication extends Application {
    ...

    private ApplicationLike applicationLike = null;
    /**
     * current build.
     */
    protected TinkerApplication(int tinkerFlags) {
        this(tinkerFlags, "com.tencent.tinker.loader.app.DefaultApplicationLike", TinkerLoader.class.getName(), false);
    }

    /**
     * @param delegateClassName The fully-qualified name of the {@link ApplicationLifeCycle} class
     *                          that will act as the delegate for application lifecycle callbacks.
     */
    protected TinkerApplication(int tinkerFlags, String delegateClassName,
                                String loaderClassName, boolean tinkerLoadVerifyFlag) {
        this.tinkerFlags = tinkerFlags;
        this.delegateClassName = delegateClassName;
        this.loaderClassName = loaderClassName;
        this.tinkerLoadVerifyFlag = tinkerLoadVerifyFlag;

    }

    protected TinkerApplication(int tinkerFlags, String delegateClassName) {
        this(tinkerFlags, delegateClassName, TinkerLoader.class.getName(), false);
    }

    private ApplicationLike createDelegate() {
        try {
            // 通过反射创建ApplicationLike对象
            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);
        }
    }

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


    private void onBaseContextAttached(Context base) {
        applicationStartElapsedTime = SystemClock.elapsedRealtime();
        applicationStartMillisTime = System.currentTimeMillis();
        //先调用了tinker进行patch等操作
        loadTinker();
       //再创建ApplicationLike对象
        ensureDelegate();
       //最后再执行ApplicationLike的生命周期
        applicationLike.onBaseContextAttached(base);
        ...
    }

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

    private void loadTinker() {
        //disable tinker, not need to install
        if (tinkerFlags == TINKER_DISABLE) {
            return;
        }
        tinkerResultIntent = new Intent();
        try {
            //反射调用TinkLoader的tryLoad方法
            Class<?> tinkerLoadClass = Class.forName(loaderClassName, false, getClassLoader());

            Method loadMethod = tinkerLoadClass.getMethod(TINKER_LOADER_METHOD, TinkerApplication.class, int.class, boolean.class);
            Constructor<?> constructor = tinkerLoadClass.getConstructor();
            tinkerResultIntent = (Intent) loadMethod.invoke(constructor.newInstance(), this, tinkerFlags, tinkerLoadVerifyFlag);
        } catch (Throwable e) {
            //has exception, put exception error code
            ShareIntentUtil.setIntentReturnCode(tinkerResultIntent, ShareConstants.ERROR_LOAD_PATCH_UNKNOWN_EXCEPTION);
            tinkerResultIntent.putExtra(INTENT_PATCH_EXCEPTION, e);
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        ensureDelegate();
        applicationLike.onCreate();
    }

    @Override
    public void onTerminate() {
        super.onTerminate();
        if (applicationLike != null) {
            applicationLike.onTerminate();
        }
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        if (applicationLike != null) {
            applicationLike.onLowMemory();
        }
    }

    @TargetApi(14)
    @Override
    public void onTrimMemory(int level) {
        super.onTrimMemory(level);
        if (applicationLike != null) {
            applicationLike.onTrimMemory(level);
        }
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        if (applicationLike != null) {
            applicationLike.onConfigurationChanged(newConfig);
        }
    }

  ...
}

可以看到,TinkerApplication是继承自Application的,是真正意义上的Application,在它的构造方法中,我们把需要代理的Application类名传递了进来,然后在createDelegate方法中利用反射,构造了一个ApplicationLike的对象,并且在自己的生命周期中调用了ApplicationLike所对应的方法,所以即使ApplicationLike并非真正的Application,却仍能具有Application的生命周期。并且,在onBaseContextAttached方法中,loadTinker方法是先于ensureDelegate方法被调用的,所以能够保证ApplicationLike的创建发生在补丁包合成插入之后,所以也就达到了Application中的类也能被热修复的目的。

到这里,我们已经摸清了整个代理的过程,还有一个问题,就是我们继承了ApplicationLike后的子类是怎么和TinkerApplication关联起来的呢?

这里你当然仍可以采用继承TinkerApplication的方法,然后将所有需要在Application中操作的代码放到继承自ApplicationLike的子类,并将它的类名通过TinkerApplication构造方法进行传递,但是这样很容易对以后维护代码的人造成一定的误解,Tinker为了方便开发者,提供了编译注解的形式以求尽量屏蔽代理的过程,使用注解生成Application类也是Tinker官方文档所推荐的方式。

使用的方式也很简单,在你的ApplicationLike添加以下注解

@DefaultLifeCycle(
application = ".SampleApplication",                       //application类名
flags = ShareConstants.TINKER_ENABLE_ALL,                 //tinkerFlags
loaderClass = "com.tencent.tinker.loader.TinkerLoader",   //loaderClassName, 我们这里使用默认即可!
loadVerifyFlag = false)                                   //tinkerLoadVerifyFlag
public class YourApplicationLike extends DefaultApplicationLike {
      ...
}

这里我们也看一下处理注解逻辑的AnnotationProcessor类,其中processDefaultLifeCycle便是处理该注解的方法

@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class AnnotationProcessor extends AbstractProcessor {

    private static final String APPLICATION_TEMPLATE_PATH = "/TinkerAnnoApplication.tmpl";

    ...
    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);
            //获得注解中的Application名
            String applicationClassName = ca.application();
            //补全全类名
            if (applicationClassName.startsWith(".")) {
                applicationClassName = lifeCyclePackageName + applicationClassName;
            }
            String applicationPackageName = applicationClassName.substring(0, applicationClassName.lastIndexOf('.'));
            applicationClassName = applicationClassName.substring(applicationClassName.lastIndexOf('.') + 1);
            //获得注解中的loaderClass名
            String loaderClassName = ca.loaderClass();
            //补全loaderClass类名
            if (loaderClassName.startsWith(".")) {
                loaderClassName = lifeCyclePackageName + loaderClassName;
            }

            System.out.println("*");
            //加载模板
            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 {
                //输出为java文件
                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());
            }
        }
    }
}

不是很复杂,主要过程就是读取了一个模板文件,然后将对应的字符串进行替换,然后将其以Java文件写入起来,模板文件其实就是TinkerAnnoApplication.tmpl了,内容如下

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的代理,实现的方式很巧妙,从而使得Tinker也能够对Application中使用到的类进行热修复。

总结

通过阅读源码,我们可以对整个框架的理解和原理更清晰,这里也推荐大家有时间还是要多看看一些优秀的源码,确实对整个编程思想有帮助。那么本文到这里就先告一段落了,如有错误或者不够详细的地方,大家也可以在评论中指出~ 下次将给大家带来Tinker源码中关于patch的部分,敬请期待~ 谢谢大家~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,646评论 4 366
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,979评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,391评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,356评论 0 215
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,740评论 3 293
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,836评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,022评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,764评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,487评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,728评论 2 252
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,200评论 1 263
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,548评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,217评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,134评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,921评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,919评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,766评论 2 274

推荐阅读更多精彩内容