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);
}
这里很关键,主要做了三个工作:
- 反射获取TinkerLoader,反射拿到其tryLoad()并执行。
- 初始化ApplicationLike。
- 调用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. 合成过程
- Tinker收到服务端获取的patch文件。
- 开启TinkerPatchService去执行UpgradePatch的tryPatch()。
- tryPatch()中会依次进行dex文件合成、.so文件合成和资源文件合成。
- 将合成后的新文件放在tinker加载的文件路径中。
b. 加载过程
- TinkerApplication的onBaseContextAttached()中反射调用了TinkerLoader的tryLoad()。
- 之后分别调用loadTinkerJars()和loadTinkerResources()去加载dex文件和资源文件。
与AndFix的native层进行方法替换不同的是,Tinker采用的是Java层的实现。类加载过程中会去一个Element数组中寻找该类信息,Tinker的思路就是在该类被加载之前去将patch修复后的该类包装到Element放到数组的前端,这样在加载的时候,先找到的就是修复后的信息了,从而达到代替的效果。
具体加载合并算法实现部分由于我能力不够就暂时放一放,但是找到了几篇比较好的博客,我也有看,先记下来,以后再仔细研究研究。
网友评论