Tinker踩坑

Tinker 接入指南
Tencent/tinker

集成Tinker最主要的兩個部分:一個是接入文檔,另一個是github上的demo,可以將tinker-sample-android單獨下載下來,運行,參考裏邊的配置。集成中遇到了一些問題,記載一下

集成步驟

1.在項目的build.gradle,配置

 dependencies {
        classpath 'com.android.tools.build:gradle:3.2.1'
        classpath ("com.tencent.tinker:tinker-patch-gradle-plugin:${TINKER_VERSION}")
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }

問題1.如果出現下圖的Bug
在這裏插入圖片描述
說明sdk中有方法已經過時,需要將gradle 版本調低一些
2.在app的build.gradle,引入tinker庫

dependencies {
   .......
    //optional, help to generate the final application
    //生成application時使用
    compileOnly("com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}") {changing=true}
    //tinker's main Android lib
    implementation("com.tencent.tinker:tinker-android-lib:${TINKER_VERSION}"){changing=true}

    implementation "com.android.support:multidex:1.0.3"
}

在gradle.properties裏定義TINKER_VERSION=1.9.1,方便管理

3.參考demo和文檔,對build.gradle進行配置

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "drag.mandala.com.tinkerdemo"
        minSdkVersion 22
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        javaCompileOptions {
            annotationProcessorOptions {
                includeCompileClasspath true
            }
        }
    }

    signingConfigs {
        release {
            try {
                storeFile file("test.jks")
                storePassword "aaaaaa"
                keyAlias "test1"
                keyPassword "aaaaaa"
            } catch (ex) {
                throw new InvalidUserDataException(ex.toString())
            }
        }

    }

    buildTypes {
        release {
            //打開混淆,要不然不能生產mapping文件
            minifyEnabled true
            //配置signConfig,否則提示can't the get signConfig for this build
            signingConfig signingConfigs.release
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }




}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'

    //optional, help to generate the final application
    //生成application時使用
    compileOnly("com.tencent.tinker:tinker-android-anno:${TINKER_VERSION}") {changing=true}
    //tinker's main Android lib
    implementation("com.tencent.tinker:tinker-android-lib:${TINKER_VERSION}"){changing=true}

    implementation "com.android.support:multidex:1.0.3"
}


def bakPath = file("${buildDir}/bakApk")
ext{
    tinkerEnable =  true
    //tinkerOldApkPath,tinkerApplyMappingPath,tinkerApplyResourceMappingPath初始值爲${bakPath},
    //打包成功以後,將build/bakApk下生成的文件名放到${bakPath}後,修改 minifyEnabled爲 true,要不然不會生產mapping文件
    tinkerOldApkPath = "${bakPath}/app-release-1126-14-00-54.apk"
    tinkerId = "1.0"
    tinkerApplyMappingPath = "${bakPath}/app-release-1126-14-00-54-mapping.txt"
    tinkerApplyResourceMappingPath = "${bakPath}/app-release-1126-14-00-54-R.txt"
}

def buildWithTinker(){

    return ext.tinkerEnable
}
def getOldApkPath(){

    return ext.tinkerOldApkPath
}

def getApplyMappingPath(){

    return ext.tinkerApplyMappingPath
}

def getApplyResourceMappingPath(){

    return ext.tinkerApplyResourceMappingPath
}

def getTinkerIdValue(){

    return ext.tinkerId
}

if(buildWithTinker()){
    //啓用了tinker
    apply plugin: 'com.tencent.tinker.patch'

    //所有tinker相關的參數配置
    tinkerPatch{

        oldApk = getOldApkPath()//指定old apk文件路徑

        ignoreWarning = false //不忽略tinker的警告,有警告就停止生成patch文件

        useSign = true //強制patch文件使用簽名

        tinkerEnable = buildWithTinker() //指定是否啓用tinker

        buildConfig{
            applyMapping = getApplyMappingPath() //指定old apk打包時所使用的混淆文件

            applyResourceMapping = getApplyResourceMappingPath() // 指定old apk的資源文件

            tinkerId = getTinkerIdValue() // 指定TinkerId

            keepDexApply = false

            isProtectedApp = false

            supportHotplugComponent = false
        }

        dex{
            dexMode = "jar" //只能是'raw'或者'jar'

            pattern = ["classes*.dex",
                       "assets/secondary-dex-?.jar"] // 指定dex文件目錄,第二個是官方例子配置

            loader = ["drag.mandala.com.tinkerdemo.MyTinkerApplication"] //指定加載patch文件時用到的類

        }

        lib{

            pattern = ["lib/*/*.so"]
        }

        res{

            pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"] //指定tinker可以修改的所有資源文件路徑

           // ignoreChange = ["assets/sample.txt"] //在編譯時會忽略該文件的新增、刪除與修改
            ignoreChange = ["assets/sample_meta.txt"]
            largeModSize = 100 //資源修改的默認值  如果大於largeModSize,tinker將使用bsdiff算法。
            // 這可以降低補丁包的大小,但是會增加合成時的複雜度。默認大小爲100kb
        }

        //不必須的配置
        //說明配置信息
        packageConfig{

            configField("patchMessage", "tinker is sample to use")

            configField("platform", "all")

            configField("patchVersion", "1.0")
        }

        /**
         * if you don't use zipArtifact or path, we just use 7za to try
         */
        sevenZip {
            /**
             * optional,default '7za'
             * the 7zip artifact path, it will use the right 7za with your platform
             */
            zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
            /**
             * optional,default '7za'
             * you can specify the 7za path yourself, it will overwrite the zipArtifact value
             */
//        path = "/usr/local/bin/7za"
        }
    }

    List<String> flavors = new ArrayList<>();
    project.android.productFlavors.each { flavor ->
        flavors.add(flavor.name)
    }
    //是否配置多渠道
    boolean hasFlavors = flavors.size() > 0


    /**
     * bak apk and mapping
     */
    android.applicationVariants.all { variant ->
        /**
         * task type, you want to bak
         */
        def taskName = variant.name
        def date = new Date().format("MMdd-HH-mm-ss")
        tasks.all {
            if ("assemble${taskName.capitalize()}".equalsIgnoreCase(it.name)) {

                it.doLast {
                    copy {
                        def fileNamePrefix = "${project.name}-${variant.baseName}"
                        def newFileNamePrefix = hasFlavors ? "${fileNamePrefix}" : "${fileNamePrefix}-${date}"

                        def destPath = hasFlavors ? file("${bakPath}/${project.name}-${date}/${variant.flavorName}") : bakPath

                        if (variant.metaClass.hasProperty(variant, 'packageApplicationProvider')) {
                            def packageAndroidArtifact = variant.packageApplicationProvider.get()
                            if (packageAndroidArtifact != null) {
                                try {
                                    from new File(packageAndroidArtifact.outputDirectory.getAsFile().get(), variant.outputs.first().apkData.outputFileName)
                                } catch (Exception e) {
                                    from new File(packageAndroidArtifact.outputDirectory, variant.outputs.first().apkData.outputFileName)
                                }
                            } else {
                                from variant.outputs.first().mainOutputFile.outputFile
                            }
                        } else {
                            from variant.outputs.first().outputFile
                        }

                        into destPath
                        rename { String fileName ->
                            fileName.replace("${fileNamePrefix}.apk", "${newFileNamePrefix}.apk")
                        }

                        from "${buildDir}/outputs/mapping/${variant.dirName}/mapping.txt"
                        into destPath
                        rename { String fileName ->
                            fileName.replace("mapping.txt", "${newFileNamePrefix}-mapping.txt")
                        }

                        from "${buildDir}/intermediates/symbols/${variant.dirName}/R.txt"
                        from "${buildDir}/intermediates/symbol_list/${variant.dirName}/R.txt"
                        into destPath
                        rename { String fileName ->
                            fileName.replace("R.txt", "${newFileNamePrefix}-R.txt")
                        }
                    }
                }
            }
        }
    }
}

問題2: 如果出現下圖的提示
在這裏插入圖片描述
需要配置:
在這裏插入圖片描述
**問題3:**出現下圖的Bug
在這裏插入圖片描述
在gradle.properties配置android.enableAapt2=true

問題4: 不能生成mapping文件,這個需要打開混淆

 minifyEnabled true

如果提示“can’t the get signConfig for this build”,需要配置signConfig

 signingConfig signingConfigs.release

完整配置


    buildTypes {
        release {
            //打開混淆,要不然不能生產mapping文件
            minifyEnabled true
            //配置signConfig,否則提示can't the get signConfig for this build
            signingConfig signingConfigs.release
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

4.寫一個繼承自DefaultApplicationLike的類

@DefaultLifeCycle(application = ".MyTinkerApplication",
flags = ShareConstants.TINKER_ENABLE_ALL,
loadVerifyFlag = false)
public class CustomTinkerLike extends DefaultApplicationLike
{

    public CustomTinkerLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent)
    {
        super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
    }

    @Override
    public void onBaseContextAttached(Context base)
    {
        super.onBaseContextAttached(base);
        //you must install multiDex whatever tinker is installed!
        MultiDex.install(base);

        TinkerManager.setTinkerApplicationLike(this);

        TinkerManager.initFastCrashProtect();
        //should set before tinker is installed
        TinkerManager.setUpgradeRetryEnable(true);


        //installTinker after load multiDex
        //or you can put com.tencent.tinker.** to main dex
        TinkerManager.installTinker(this);
        Tinker tinker = Tinker.with(getApplication());
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    public void registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks callback) {
        getApplication().registerActivityLifecycleCallbacks(callback);
    }
}

build一下,AndroidManifest.xml裏的<application 的name設置成註解裏的application,

<application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme"
            android:name=".MyTinkerApplication">
            ...

配置權限,否則onReceiveUpgradePatch的時候會閃退

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

5.其他的類可以複製demo裏的,別忘了在AndroidManifest.xml配置SampleResultService

6.配置完成以後,先打包
在這裏插入圖片描述

打包成功以後,查看build文件夾,
在這裏插入圖片描述
將這三個文件名賦值到build.gradle中的對應位置
在這裏插入圖片描述

將app-release-1126-14-00-54.apk安裝到手機上,作爲一個有Bug的app,修改bug,生成patch文件
在這裏插入圖片描述

生成成功以後,繼續看build文件夾
在這裏插入圖片描述

這個放到手機對應的目錄下,目錄地址是onReceiveUpgradePatch方法裏的

TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), Environment.getExternalStorageDirectory().getAbsolutePath() + "/patch_signed_7zip.apk");

點擊按鈕加載patch,殺死進程,重啓,就會是修改後的app

問題5: 如果報com.tencent.tinker.build.util.TinkerPatchException: resource must contain resources.arsc pattern,檢查build.gradle裏的res下的pattern中的"resources.arsc"是否寫錯。還有就是通過AndroidStudio方式生成差異包的時候如果不小心修改了XML也會報這個錯

問題6: onLoadPatchListenerReceiveFail: patch receive fail: /storage/emulated/0/patch_signed_7zip.apk, code: -2
檢查文件路徑是否和代碼裏的一致,查看清單文件中是否有添加SD卡訪問權限,如果是Android7.0要考慮FileProvider(Android7.0不支持直接訪問sd卡)

問題7: Tinker does not support instant run mode, please trigger build by assembleDebug or disable instant run
去掉instance run

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章