自定义Application类

程序启动时会加载默认的Application类,这导致我们补丁包是无法对它做修改了。如何规避?在这里我们并没有使用类似InstantRun hook Application的方式,而是通过代码框架的方式来避免,这也是为了尽量少的去反射,提升框架的兼容性。

这里我们要实现的是完全将原来的Application类隔离起来,即其他任何类都不能再引用我们自己的Application。我们需要做的其实是以下几个工作:

  • 将我们自己Application类以及它的继承类的所有代码拷贝到自己的ApplicationLike继承类中,例如SampleApplicationLike。你也可以直接将自己的Application改为继承ApplicationLike;
  • Application的attachBaseContext方法实现要单独移动到onBaseContextAttached中;
  • 对ApplicationLike中,引用application的地方改成getApplication();
  • 对其他引用Application或者它的静态对象与方法的地方,改成引用ApplicationLike的静态对象与方法;更详细的事例,大家可以参考下面的一些例子以及SampleApplicationLike的做法。

如果你不愿意改造自己的应用,可以尝试TinkerPatch的一键傻瓜式接入,具体的可参考文档TinkerPatch 平台介绍

Application代理类

为了使真正的Application实现可以在补丁包中修改,我们把Appliction类的所有逻辑移动到ApplicationLike代理类中。

  1. -public class YourApplication extends Application {
  2. +public class SampleApplicationLike extends DefaultApplicationLike

在1.7.6版本之前,我们需要手动同时将gradle的dex loader中的Application改为新的YourApplication。在1.7.6版本之后,tinker-build-plugin将会自动写入,我们无须手动填写。需要注意的是,若使用newApk或者命令行编译需要手动填写

  1. dex {
  2. loader = ["com.tencent.tinker.loader.*",
  3. //warning, you must change it with your application
  4. "tinker.sample.android.YourApplication",
  5. }

具体实现可参考SampleApplicationLike, 其中对Application类的调用可以修改成:

  1. public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback) {
  2. application.registerActivityLifecycleCallbacks(callback);
  3. }

若你的应用代理Application的ClassLoader、Resource以及AssetsManger,可以使用以下方法设置。

  1. applicationLike.setResources(res);
  2. applicationLike.setClassLoader(classloader);
  3. applicationLike.setTAssets(assets);

事实上,你也可以在你的Application类加入代理,但是在Application中尽量不要引用自己的类,将真正的实现放在外面。

  1. public class YourrApplication extends Application {
  2. ActivityLifecycleCallbacks activityLifecycleCallbacks;
  3. public void setTinkerActivityLifecycleCallbacks(ActivityLifecycleCallbacks activityLifecycleCallbacks) {
  4. this.activityLifecycleCallbacks = activityLifecycleCallbacks;
  5. }

修改你的Application类

然后将你的Application类继承TinkerApplication.java除了构造方法之外,你最好不要引入其他的类,这将导致它们无法通过补丁修改。

  1. public class SampleApplication extends TinkerApplication {
  2. public SampleApplication() {
  3. super(
  4. //tinkerFlags, tinker支持的类型,dex,library,还是全部都支持!
  5. ShareConstants.TINKER_ENABLE_ALL,
  6. //ApplicationLike的实现类,只能传递字符串
  7. "tinker.sample.android.app.SampleApplicationLike",
  8. //Tinker的加载器,一般来说用默认的即可
  9. "com.tencent.tinker.loader.TinkerLoader",
  10. //tinkerLoadVerifyFlag, 运行加载时是否校验dex与,ib与res的Md5
  11. false);
  12. }
  13. }

具体的数值含义如下:

参数默认值描述
tinkerFlagsTINKER_DISABLEtinker运行时支持的补丁包中的文件类型: 1. ShareConstants.TINKER_DISABLE:不支持任何类型的文件; 2. ShareConstants.TINKER_DEX_ONLY:只支持dex文件; 3. ShareConstants.TINKER_LIBRARY_ONLY:只支持library文件; 4. ShareConstants.TINKER_DEX_AND_LIBRARY:只支持dex与res的修改; 5. ShareConstants.TINKER_ENABLE_ALL:支持任何类型的文件,也是我们通常的设置的模式。
delegateClassName"com.tencent.tinker.loader.app.DefaultApplicationLike"Application代理类的类名,这里只能使用字符串,不能使用class.getName()
loaderClassName"com.tencent.tinker.loader.TinkerLoader"加载Tinker的主类名,对于特殊需求可能需要使用自己的加载类。需要注意的是:这个类以及它使用的类都是不能被补丁修改的,并且我们需要将它们加到dex.loader[]中。一般来说,我们使用默认即可。
tinkerLoadVerifyFlagfalse由于合成过程中我们已经校验了各个文件的Md5,并将它们存放在/data/data/..目录中。默认每次加载时我们并不会去校验tinker文件的Md5,但是你也可通过开启loadVerifyFlag强制每次加载时校验,但是这会带来一定的时间损耗。

Warning: 这里务必不能写成SampleApplicationLike.class.getName(),只能通过传递字符串的方式。为了减少错误的出现,推荐使用Annotation生成Application类

使用Annotation生成Application类

为了隐藏你的Application类,我们更加推荐你使用tinker-android-anno在运行时生成你的Application类。这样保证你无法修改你的Application类,不会因为错误操作导致引入更多无法修改的类。

  1. @DefaultLifeCycle(application = ".SampleApplication", //application类名flags = ShareConstants.TINKER_ENABLE_ALL, //tinkerFlagsloaderClass = "com.tencent.tinker.loader.TinkerLoader", //loaderClassName, 我们这里使用默认即可!loadVerifyFlag = false) //tinkerLoadVerifyFlagpublic class SampleApplicationLike extends DefaultApplicationLike

若采用Annotation生成Application,需要将原来的Application类删掉。到此为止,Tinker初步的接入已真正的完成,你已经可以愉快的使用Tinker来实现补丁功能了。