从Tinker的官方文档上可以看到,接入Tinker是在Application中,所以切入点就从Application开始;
public class TinkerApplication extends Application {
private ApplicationLike tinkerApplicationLike;
@Override
public void onCreate() {
super.onCreate();
if (BuildConfig.TINKER_ENABLE) {
// 我们可以从这里获得Tinker加载过程的信息
tinkerApplicationLike = TinkerPatchApplicationLike.getTinkerPatchApplicationLike();
// 初始化TinkerPatch SDK, 更多配置可参照API章节中的,初始化SDK
TinkerPatch.init(tinkerApplicationLike)
.reflectPatchLibrary()
.setPatchRollbackOnScreenOff(true)
.setPatchRestartOnSrceenOff(true);
// 每隔3个小时去访问后台时候有更新,通过handler实现轮训的效果
new FetchPatchHandler().fetchPatchWithInterval(3);
}
}
}
上面是我们自己的Application,发现使用了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;
}
public Application getApplication() {
return application;
}
public final Intent getTinkerResultIntent() {
return tinkerResultIntent;
}
public final int getTinkerFlags() {
return tinkerFlags;
}
public final boolean getTinkerLoadVerifyFlag() {
return tinkerLoadVerifyFlag;
}
public long getApplicationStartElapsedTime() {
return applicationStartElapsedTime;
}
public long getApplicationStartMillisTime() {
return applicationStartMillisTime;
}
@Override
public void onCreate() {
}
@Override
public void onLowMemory() {
}
@Override
public void onTrimMemory(int level) {
}
@Override
public void onTerminate() {
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
}
@Override
public void onBaseContextAttached(Context base) {
}
//some get methods that may be overwrite
public Resources getResources(Resources resources) {
return resources;
}
public ClassLoader getClassLoader(ClassLoader classLoader) {
return classLoader;
}
public AssetManager getAssets(AssetManager assetManager) {
return assetManager;
}
public Object getSystemService(String name, Object service) {
return service;
}
public Context getBaseContext(Context base) {
return base;
}
}
可以看出ApplicationLike只是一个抽象类并实现了ApplicationLifeCycle这个接口,并且里面保存了Application的引用。可以想到Tinker是使用的代理的方式,将Application隔离起来,在Tinker中,真正的Application实际上是TinkerApplication,TinkerApplication的源码如下:
public abstract class TinkerApplication extends Application {
//oh, we can use ShareConstants, because they are loader class and static final!
private static final int TINKER_DISABLE = ShareConstants.TINKER_DISABLE;
private static final String INTENT_PATCH_EXCEPTION = ShareIntentUtil.INTENT_PATCH_EXCEPTION;
private static final String TINKER_LOADER_METHOD = "tryLoad";
...
private ApplicationLike applicationLike = null;
private long applicationStartElapsedTime;
private long applicationStartMillisTime;
/**
* 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 {
// 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);
}
}
private synchronized void ensureDelegate() {
if (applicationLike == null) {
applicationLike = createDelegate();
}
}
/**
* Hook for sub-classes to run logic after the {@link Application#attachBaseContext} has been
* called but before the delegate is created. Implementors should be very careful what they do
* here since {@link android.app.Application#onCreate} will not have yet been called.
*/
private void onBaseContextAttached(Context base) {
applicationStartElapsedTime = SystemClock.elapsedRealtime();
applicationStartMillisTime = System.currentTimeMillis();
loadTinker();
ensureDelegate();
applicationLike.onBaseContextAttached(base);
//reset save mode
if (useSafeMode) {
String processName = ShareTinkerInternals.getProcessName(this);
String preferName = ShareConstants.TINKER_OWN_PREFERENCE_CONFIG + processName;
SharedPreferences sp = getSharedPreferences(preferName, Context.MODE_PRIVATE);
sp.edit().putInt(ShareConstants.TINKER_SAFE_MODE_COUNT, 0).commit();
}
}
@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 {
//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);
} 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();
}
...
}
既然是Application,所以生命周期方法attachBaseContext() ->onBaseContextAttached() -> loadTinker() ,最后是调用了loadTinker方法,在该方法内部,反射调用了TinkerLoader的tryLoad方法。
TinkerLoader的tryLoad如下:
/**
* only main process can handle patch version change or incomplete
*/
@Override
public Intent tryLoad(TinkerApplication app) {
Intent resultIntent = new Intent();
long begin = SystemClock.elapsedRealtime();
tryLoadPatchFilesInternal(app, resultIntent);
long cost = SystemClock.elapsedRealtime() - begin;
ShareIntentUtil.setIntentPatchCostTime(resultIntent, cost);
return resultIntent;
}
tryLoad中又调用了tryLoadPatchFilesInternal方法;
private void tryLoadPatchFilesInternal(TinkerApplication app, Intent resultIntent) {
...
//check patch directory whether exist
...
//check patch info file whether exist
...
//now we can load patch jar
if (isEnabledForDex) {
boolean loadTinkerJars = TinkerDexLoader.loadTinkerJars(app, patchVersionDirectory, oatDex, resultIntent, isSystemOTA);
if (isSystemOTA) {
// update fingerprint after load success
patchInfo.fingerPrint = Build.FINGERPRINT;
patchInfo.oatDir = loadTinkerJars ? ShareConstants.INTERPRET_DEX_OPTIMIZE_PATH : ShareConstants.DEFAULT_DEX_OPTIMIZE_PATH;
// reset to false
oatModeChanged = false;
if (!SharePatchInfo.rewritePatchInfoFileWithLock(patchInfoFile, patchInfo, patchInfoLockFile)) {
ShareIntentUtil.setIntentReturnCode(resultIntent, ShareConstants.ERROR_LOAD_PATCH_REWRITE_PATCH_INFO_FAIL);
Log.w(TAG, "tryLoadPatchFiles:onReWritePatchInfoCorrupted");
return;
}
// update oat dir
resultIntent.putExtra(ShareIntentUtil.INTENT_PATCH_OAT_DIR, patchInfo.oatDir);
}
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;
}
}
}
看上面的源码可以发现,代码注释写的还是很清楚的 (这点很值得我们学习) ,首先是做了一系列的安全性校验,然后开始加载jar,res;接下来主要看一下loadTinkerJars这个方法;
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public static boolean loadTinkerJars(Application application, boolean tinkerLoadVerifyFlag,
String directory, Intent intentResult, boolean isSystemOTA) {
PathClassLoader classLoader = (PathClassLoader) TinkerDexLoader.class.getClassLoader();
String dexPath = directory + "/" + DEX_PATH + "/";
File optimizeDir = new File(directory + "/" + DEX_OPTIMIZE_PATH);
ArrayList<File> legalFiles = new ArrayList<>();
final boolean isArtPlatForm = ShareTinkerInternals.isVmArt();
for (ShareDexDiffPatchInfo info : dexList) {
//for dalvik, ignore art support dex
if (isJustArtSupportDex(info)) {
continue;
}
String path = dexPath + info.realName;
File file = new File(path);
legalFiles.add(file);
}
// just for art
if (isSystemOTA) {
parallelOTAResult = true;
parallelOTAThrowable = null;
Log.w(TAG, "systemOTA, try parallel oat dexes!!!!!");
TinkerParallelDexOptimizer.optimizeAll(
legalFiles, optimizeDir,
new TinkerParallelDexOptimizer.ResultCallback() {
}
);
SystemClassLoaderAdder.installDexes(application, classLoader, optimizeDir, legalFiles);
return true;
}
TinkerDexLoader.loadTinkerJars传入四个参数,分别为application,tinkerLoadVerifyFlag(注解上声明的值,传入为false),patchVersionDirectory当前version的patch文件夹,intent,当前patch是否仅适用于art。最后调用了installDexes方法:
@SuppressLint("NewApi")
public static void installDexes(Application application, PathClassLoader loader, File dexOptDir, List<File> files)
throws Throwable {
if (!files.isEmpty()) {
ClassLoader classLoader = loader;
if (Build.VERSION.SDK_INT >= 24) {
classLoader = AndroidNClassLoader.inject(loader, application);
}
//because in dalvik, if inner class is not the same classloader with it wrapper class.
//it won't fail at dex2opt
if (Build.VERSION.SDK_INT >= 23) {
V23.install(classLoader, files, dexOptDir);
} else if (Build.VERSION.SDK_INT >= 19) {
V19.install(classLoader, files, dexOptDir);
} else if (Build.VERSION.SDK_INT >= 14) {
V14.install(classLoader, files, dexOptDir);
} else {
V4.install(classLoader, files, dexOptDir);
}
//install done
sPatchDexCount = files.size();
Log.i(TAG, "after loaded classloader: " + classLoader + ", dex size:" + sPatchDexCount);
if (!checkDexInstall(classLoader)) {
//reset patch dex
SystemClassLoaderAdder.uninstallPatchDex(classLoader);
throw new TinkerRuntimeException(ShareConstants.CHECK_DEX_INSTALL_FAIL);
}
}
}
private static final class V23 {
private static void install(ClassLoader loader, List<File> additionalClassPathEntries,
File optimizedDirectory)
throws IllegalArgumentException, IllegalAccessException,
NoSuchFieldException, InvocationTargetException, NoSuchMethodException, IOException {
/* The patched class loader is expected to be a descendant of
* dalvik.system.BaseDexClassLoader. We modify its
* dalvik.system.DexPathList pathList field to append additional DEX
* file entries.
*/
Field pathListField = ShareReflectUtil.findField(loader, "pathList");
Object dexPathList = pathListField.get(loader);
ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
ShareReflectUtil.expandFieldArray(dexPathList, "dexElements", makePathElements(dexPathList,
new ArrayList<File>(additionalClassPathEntries), optimizedDirectory,
suppressedExceptions));
if (suppressedExceptions.size() > 0) {
for (IOException e : suppressedExceptions) {
Log.w(TAG, "Exception in makePathElement", e);
throw e;
}
}
}
1.使用反射拿到PathClassLoader对象中的pathList对象;
2.找到pathList中的makeDexElements方法,传入patch相关参数,获取Element[]数组;
3.获取pathList对象中的原始Element[]数组;
4.合并两个数组,并且将patch相关的dex放在数组前面,并设置给pathList;
Tinker的补丁加载和Qzone的方案基本上是一致的,不同的是Tinker是利用DexDiff算法对比差异生成Patch补丁包,然后将Patch包里面的Dex与基础包的dexElements进行合并,合成新的dex,然后整体替换。而Qzone是使用插桩的方式避免类被打上CLASS_ISPREVERIFYED标志。