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放到數組的前端,這樣在加載的時候,先找到的就是修復后的信息了,從而達到代替的效果。
具體加載合并算法實現部分由于我能力不夠就暫時放一放,但是找到了幾篇比較好的博客,我也有看,先記下來,以后再仔細研究研究。