我们知道Tinker实现热修复的原理是将自己的全量patch包插入到dexElements数组的前段,从而达到热修复的目的。
以下所有对源码的分析均基于Tinker 1.7.7 版本
通常我们都是在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的部分,敬请期待~ 谢谢大家~