上次我们分析了一下热修复的原理并着手实践了一下,这次将带大家一起探究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的部分,敬请期待~ 谢谢大家~
网友评论