引入 Tinker 以後如何在 Debug 模式下開啓 Instant Run

在《Tinker + Bugly + Jenkins 爬坑之路》一文中講了在接入 Tinker 以後,Jenkins 中的一些坑,由此,熱修復算告一段落,可是,在直接 Run 模式運行時,程序會報出以下錯誤:java

Tinker does not support instant run mode, please trigger build by assembleDebug or disable instant run in 'File->Settings...'.

好吧,使用 TInker 時不能開啓 Instant Run  ̄□ ̄||android

GitHub 上也有一個一樣的 issue,引入Tinker以後如何在Debug模式下開啓Instant Run ,這裏我將個人方法講述一下,給你們一個參考。git

1. 使用變量標記是否使用 Tinker

projectbuild.gradle 文件的 ext 中定義變量 tinkerEnabled 用來標記是否使用 TInker,代碼以下所示:github

ext {
    /** * 是否啓用tinker參與編譯 * 開發時,根據須要修改值來開啓 * Jenkins 構建時,會替換該值 */
    tinkerEnabled = rootProject.properties["tinkerEnable"]
    if (null == tinkerEnabled) {
        tinkerEnabled = "false"
    }
}

看過《Tinker + Bugly + Jenkins 爬坑之路》的同窗應該知道,我司的項目是使用 Jenkins 打包的,因此我這裏先經過 rootProject.properties["tinkerEnable"] 從 Gradle 命令中取 tinkerEnabled 參數的值,而後在構建腳本的打包命令行中加入該參數:bash

sh gradlew assembleRelease -PtinkerEnable=true --stacktrace

這樣,就確保了 Jenkins 構建時 tinkerEnable 的值爲 true。在開發過程當中,本地運行或者構建 apk 就能夠經過修改 tinkerEnabled = "false" 來決定是否使用 Tinker 構建。app

2. 經過標記值決定是否使用 TInker 構建

接下來在 modulebuild.gradle 文件中,經過 tinkerEnabled 值來判斷是否引入 tinker-support.gradle 構建項目,代碼以下:ide

// 依賴插件腳本-tinker
if (Boolean.parseBoolean(rootProject.ext.tinkerEnabled)) {
    apply from: rootProject.file('gradle/tinker-support.gradle')
}

3. Java/Kotlin 代碼中經過標記值決定是否初始化 Tinker

Java/Kotlin 代碼中,是沒法直接使用 gradle 文件中的變量值的,那麼在 Java/Kotlin 代碼中,怎麼經過上面定義的標記量來決定是否初始化 Tinker 呢?總不能在 Java/Kotlin 代碼中也定義一個全局變量來控制吧,那樣本地開發時一改就得改兩個地方,不但麻煩並且可能出錯,另外,Jenkins 打包時也沒法肯定 Java/Kotlin 會初始化 Tinker。gradle

那,怎麼辦呢?咱們來個「曲線救國」的方法。經過自定義 BuildConfig 屬性來解決,首先,在 modulebuild.gradle 文件中,將 tinkerEnabled 的值傳遞到 BuildConfig 的自定義屬性中,代碼以下:ui

android {
    compileSdkVersion rootProject.ext.compileSdkVersion
    buildToolsVersion rootProject.ext.buildToolsVersion
    defaultConfig {
        /** =============自定義 BuildConfig 屬性========================*/
        buildConfigField "boolean", "BuildConfig", rootProject.ext.tinkerEnabled
        /** =============自定義 BuildConfig 屬性========================*/
    }
}

而後,在自定義的 application 類中添加根據 BuildConfig.BuildConfig 判斷是否初始化 Tinker 的代碼:spa

package com.cy.sample

import android.app.Application
import android.content.Context
import android.widget.Toast
import com.tencent.bugly.Bugly
import com.tencent.bugly.beta.Beta
import com.tencent.bugly.beta.interfaces.BetaPatchListener
import com.tencent.bugly.beta.tinker.TinkerManager.getApplication
import java.util.*

/** * 類描述。 * * @author cspecialy * @version v1.0.0 */
class MyApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        if (BuildConfig.TINKER_ENABLE) {
            initTinker()
        }
    }

    /** * 初始化 Tinker */
    private fun initTinker() {
        // 設置是否開啓熱更新能力,默認爲true
        Beta.enableHotfix = true
        // 設置是否自動下載補丁,默認爲true
        Beta.canAutoDownloadPatch = true
        // 設置是否自動合成補丁,默認爲true
        Beta.canAutoPatch = true
        // 設置是否提示用戶重啓,默認爲false
        Beta.canNotifyUserRestart = true
        // 補丁回調接口
        Beta.betaPatchListener = object : BetaPatchListener {
            override fun onPatchReceived(patchFile: String) {
                Toast.makeText(getApplication(), "補丁下載地址$patchFile", Toast.LENGTH_SHORT).show()
            }

            override fun onDownloadReceived(savedLength: Long, totalLength: Long) {
                Toast.makeText(getApplication(),
                        String.format(Locale.getDefault(), "%s %d%%",
                                Beta.strNotificationDownloading,
                                (if (totalLength == 0L) 0 else savedLength * 100 / totalLength).toInt()),
                        Toast.LENGTH_SHORT).show()
            }

            override fun onDownloadSuccess(msg: String) {
                Toast.makeText(getApplication(), "補丁下載成功", Toast.LENGTH_SHORT).show()
            }

            override fun onDownloadFailure(msg: String) {
                Toast.makeText(getApplication(), "補丁下載失敗", Toast.LENGTH_SHORT).show()

            }

            override fun onApplySuccess(msg: String) {
                Toast.makeText(getApplication(), "補丁應用成功", Toast.LENGTH_SHORT).show()
            }

            override fun onApplyFailure(msg: String) {
                Toast.makeText(getApplication(), "補丁應用失敗", Toast.LENGTH_SHORT).show()
            }

            override fun onPatchRollback() {

            }
        }

        // 設置開發設備,默認爲false,上傳補丁若是下發範圍指定爲「開發設備」,須要調用此接口來標識開發設備
        Bugly.setIsDevelopmentDevice(getApplication(), true)
        // 多渠道需求塞入
        // String channel = WalleChannelReader.getChannel(getApplication());
        // Bugly.setAppChannel(getApplication(), channel);
        // 這裏實現SDK初始化,appId替換成你的在Bugly平臺申請的appId
        Bugly.init(getApplication(), "2a1dc56c3a", true)
    }

    override fun attachBaseContext(base: Context) {
        super.attachBaseContext(base)
        // you must install multiDex whatever tinker is installed!
        MultiDex.install(base)

        if (BuildConfig.TINKER_ENABLE) {
            // 安裝tinker
            Beta.installTinker()
        }
    }
}

以上代碼相信你們也注意到了,是的,我這裏 TInker 是使用 enableProxyApplication = true 開啓反射代理的方式,你們若是使用 enableProxyApplication = false 方式的話,方向也同樣,我這裏就不贅述了,你們因地制宜哈~~~ O(∩_∩)O哈哈~

接下來,開發時只須要將 tinkerEnabled 變量的值設置爲 false,就能夠愉快的使用 Instant Run 了。

相關文章
相關標籤/搜索