Tencent / tinker

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

Improve product flavor support #1086

Open noproxy opened 5 years ago

noproxy commented 5 years ago

In the sample android project, a trick and invasive way is provided to build tinker with multiple flavors. The given methods is error-prone and feature-missing. It can be easily affect by other plugin and also break the UP-TO-DATE check of the Gradle (Oops, it seems that tinker's tasks simply set 'upToDateWhen = false'. ).

The below is a example dsl to keep max compatible with old api.

// defaults
tinkerPatch {
    oldApk = "xxxx"
    buildConfig {
    }
    dex {
        ...
    }
    lib {
        ...
    }
}

// overrdie the defaults
tinkerPatchFlavors {
    xiaomi { // has same type as tinkerPatch
         oldApk = "xxxx-xiaomi"
         buildConfig {
             applyMapping = "maping-xiaomi.txt"
             applyResourceMapping = "maping-xiaomi.txt"
             tinkerId = "1.1.0-xiaomi"
         }
    }
    normal { // has same type as tinkerPatch
         oldApk = "xxxx-normal"
         buildConfig {
             applyMapping = "maping-normal.txt"
             applyResourceMapping = "maping-normal.txt"
             tinkerId = "1.1.0-normal"
         }
         // override somethings others
    }
}

If tinker has a plan for 2.0 where breaking changes are tolerated, new dsl will be better that we can separate some non-overridable options from origin extension.

tinkerPatch {
    // non-overridable
    ignoreWarnings = false
    tinkerEnable = true

    defaultConfig {
        oldApk = "xxxx"
        buildConfig {
            applyMapping = "maping-xiaomi.txt"
            applyResourceMapping = "maping-xiaomi.txt"
            tinkerId = "1.1.0-xiaomi"
        }
    }
    flavors {
        xiaomi { // has same type as tinkerPatch
             oldApk = "xxxx-xiaomi"
             buildConfig {
                 applyMapping = "maping-xiaomi.txt"
                 applyResourceMapping = "maping-xiaomi.txt"
                 tinkerId = "1.1.0-xiaomi"
             }
             dex {
                loader = []
             }
        }
        normal { // has same type as tinkerPatch
             oldApk = "xxxx-normal"
             buildConfig {
                 applyMapping = "maping-normal.txt"
                 applyResourceMapping = "maping-normal.txt"
                 tinkerId = "1.1.0-normal"
             }
             // override somethings others
        }
    }
}

In the internal plugin, we should create tasks by the derived configuration.

final TinkerPatchExtension defaultConfig = project.tinkerPatch
// do some common with defaultConfig

variants.all(variant -> {
   // create derived configuration
   final TinkerPatchExtension variantConfig =
            // the value in flavors config can override the default one.
            defaultConfig + project.tinkerPatchFlavors.findByName(variant.name)

   // create tasks and configure somethings by variantConfig
   project.getTasks().create("tinkerPatch${variant.getName().capitalize()}", TinkerPatchTask.class , t -> {
        t.setOldApk( variantConfig.getOldApk() );
   })
}
tys282000 commented 5 years ago

Thanks for your advice. The demo project was made a long time ago when gradle's increamental building mechanism was not mature. So we decided to completely block the increamental building of 'tinkerPatch' task at the begining. Besides, you may notice that the logic of 'tinkerPatch' task can be briefly described as 'pre-check' and 'diff', which needs to be re-triggered if any changes were made to base apk or current built apk. As a result, making 'tinkerPatch' task support up-to-date check could be redundant.

tys282000 commented 5 years ago

... It can be easily affect by other plugin ...

That's what I always worry about, too. To solve this and other compiling problems completely, Tinker configuration DSL will be refactored in version 2.0. Your advice really makes helps, thanks again.