Tencent / tinker

Tinker is a hot-fix solution library for Android, it supports dex, library and resources update without reinstall apk.
Other
17.13k stars 3.33k forks source link

tinker-patch-plugin自动添加的multidex规则太模糊 #1449

Closed wzjing closed 4 years ago

wzjing commented 4 years ago
异常类型:编译异常

tinker版本:如:1.9.14.7

gradle版本:如:3.4.2

编译参数:android.enableR8=false

gradle插件:com.tencent.tinker:tinker-patch-gradle-plugin:1.9.14.7

系统:如:Mac

插件自动生的multidex keep规则中包含这一条:

-keep class com.tencent.tinker.loader.app.TinkerInlineFenceAction {
    *;
}

这个类里又直接调用了 ApplicationLike.onBaseContextAttached()和ApplicationLike.onCreate(),由于依赖规则的传递,导致所有在ApplicationLIke中添加的启动代码以及其依赖都被添加到了multidexKeepList中。正常情况下,不接入tinker,把这些启动代码直接放在Application的onCreate也只产生了800-900个keep的class,但是用这一条tinker的规则就导致keep的class骤增到了115000+个,众所周知,mainDex只能keep65535个class,请问,这条规则是否可以细化?ApplicationLike内的类是不是不用全部keep?

youxiachai commented 4 years ago

兄弟,你去错地方了....

wzjing commented 4 years ago

@youxiachai 不太懂,啥意思?这个地方的代码不能提issue吗?TinkerProguardConfigTask.groovy

youxiachai commented 4 years ago

@wzjing tinker 和tinker-patch 不是一个项目啊....

wzjing commented 4 years ago

我指的是这个项目里自带的插件,不是tinkerpatch那个付费平台的插件@youxiachai

icemanstudy commented 4 years ago

@wzjing

3个月前遇到了.实际上应该是tinker里面这个类的keep存在问题.

public final class TinkerApplicationInlineFence extends Handler {
    private final ApplicationLike mAppLike;

tinker的gradle插件会插入如下keep规则

-keep class com.tencent.tinker.entry.TinkerApplicationInlineFence {
    *;
}

这条规则会导致真正的ApplicationLike实现类(多数情况下就是我们实际app的Application类),引用的所有类都被放到maindex... 改成-keep class com.tencent.tinker.entry.TinkerApplicationInlineFence可以让maindex中减少很多.

附上我们在build.gradle中的解决代码.

android.applicationVariants.all { variant ->
    // 删除Tinker添加的DexKeepProguard规则,防止打包时出现mainDex methods>65535
    // 在Tinker 1.9.14.5上测试通过,后续Tinker版本可能会修复此问题, 升级Tinker时注意维护
    Task fixTinkerDexKeepProguard = task("fixTinkerDexKeepProguardFor${variant.name.capitalize()}")
    fixTinkerDexKeepProguard.doLast{
        def tinkerMultidexKeepTask =   project.tasks.findByName("tinkerProcess${variant.name.capitalize()}MultidexKeep")
        def multiDexKeepProguardFile =  file(tinkerMultidexKeepTask.multiDexKeepProguard)
        def lines = []
        def skipNextLine = false
        def processed = false
        multiDexKeepProguardFile.text.eachLine {line->
            if(skipNextLine){
                skipNextLine = false
                processed = true
            }else if(line.contains("-keep class com.tencent.tinker.entry.TinkerApplicationInlineFence")){
                //删除此条之后的*
                lines.add(line)
                skipNextLine = true
            }else {
                lines.add(line)
            }
        }
        FileWriter fr = new FileWriter(multiDexKeepProguardFile,false)
        try {
            for (String line : lines) {
                fr.write(line)
                fr.write("\n")
            }
        } finally {
            fr.close()
        }
        if(!processed){
            project.logger.log(LogLevel.ERROR,"未能成功清除Tinker添加错误的MultidexKeep规则,可能出现65536问题")
        }
    }
}
wzjing commented 4 years ago

@icemanstudy 是个不错的办法,但是直接删掉这条规则是否会有兼容问题呢?目前我是在ApplicationLike里反射调用实际的启动代码,达到切断依赖的目的(但启动时长增加了100ms,也不太能接受)

临时解决方案基本都有了:

总结

主要原因

是这条自动生成的multidex规则导致的:

-keep class com.tencent.tinker.loader.app.TinkerInlineFenceAction {
    *;
}

解决方法

  1. 在编译期通过gradle去掉这条规则,见 @icemanstudy 的回答
  2. 可以通过反射切断multidex keep guard的依赖查找
    class YourApplicationLike extends DefaultApplicationLike {
    @Override
    void onCreate() {
    // 反射调用实际的启动代码,这样就不会被keep到multidexkeeplist里了
    Class<MyDelegate> delegateClazz = Class.forName("com.example.MyDelegate");
    MyDelegate delegate = delegate.getConstructor().newInstance();
    Method onCreateMethod = delegateClazz.getDeclaredMethod("onCreate")
    onCreateMethod.invoke(delegate)
    }
    }

希望Tinker官方能在后续版本给个官方解决方案吧🤘