自定义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代理类中。
- -public class YourApplication extends Application {
- +public class SampleApplicationLike extends DefaultApplicationLike
在1.7.6版本之前,我们需要手动同时将gradle的dex loader中的Application改为新的YourApplication。在1.7.6版本之后,tinker-build-plugin将会自动写入,我们无须手动填写。需要注意的是,若使用newApk或者命令行编译需要手动填写
- dex {
- loader = ["com.tencent.tinker.loader.*",
- //warning, you must change it with your application
- "tinker.sample.android.YourApplication",
- }
具体实现可参考SampleApplicationLike, 其中对Application类的调用可以修改成:
- public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback) {
- application.registerActivityLifecycleCallbacks(callback);
- }
若你的应用代理Application的ClassLoader、Resource以及AssetsManger,可以使用以下方法设置。
- applicationLike.setResources(res);
- applicationLike.setClassLoader(classloader);
- applicationLike.setTAssets(assets);
事实上,你也可以在你的Application类加入代理,但是在Application中尽量不要引用自己的类,将真正的实现放在外面。
- public class YourrApplication extends Application {
- ActivityLifecycleCallbacks activityLifecycleCallbacks;
- public void setTinkerActivityLifecycleCallbacks(ActivityLifecycleCallbacks activityLifecycleCallbacks) {
- this.activityLifecycleCallbacks = activityLifecycleCallbacks;
- }
修改你的Application类
然后将你的Application类继承TinkerApplication.java。除了构造方法之外,你最好不要引入其他的类,这将导致它们无法通过补丁修改。
- public class SampleApplication extends TinkerApplication {
- public SampleApplication() {
- super(
- //tinkerFlags, tinker支持的类型,dex,library,还是全部都支持!
- ShareConstants.TINKER_ENABLE_ALL,
- //ApplicationLike的实现类,只能传递字符串
- "tinker.sample.android.app.SampleApplicationLike",
- //Tinker的加载器,一般来说用默认的即可
- "com.tencent.tinker.loader.TinkerLoader",
- //tinkerLoadVerifyFlag, 运行加载时是否校验dex与,ib与res的Md5
- false);
- }
- }
具体的数值含义如下:
参数 | 默认值 | 描述 |
---|---|---|
tinkerFlags | TINKER_DISABLE | tinker运行时支持的补丁包中的文件类型: 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[]中 。一般来说,我们使用默认即可。 |
tinkerLoadVerifyFlag | false | 由于合成过程中我们已经校验了各个文件的Md5,并将它们存放在/data/data/..目录中。默认每次加载时我们并不会去校验tinker文件的Md5,但是你也可通过开启loadVerifyFlag强制每次加载时校验,但是这会带来一定的时间损耗。 |
Warning: 这里务必不能写成SampleApplicationLike.class.getName(),只能通过传递字符串的方式。为了减少错误的出现,推荐使用Annotation生成Application类
使用Annotation生成Application类
为了隐藏你的Application类,我们更加推荐你使用tinker-android-anno
在运行时生成你的Application类。这样保证你无法修改你的Application类,不会因为错误操作导致引入更多无法修改的类。
- @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来实现补丁功能了。