1. 程式人生 > >Tinker熱修復框架接入

Tinker熱修復框架接入

Tinker熱修復框架接入

Android現在開發App基本都開始接入熱修復框架,為的就是能夠修復一些線上緊急Bug。熱門的熱修復框架以及對比,網上介紹的也很多,個人而言就用過騰訊的tinker以及阿里的sophix。

騰訊tinkerTinker簡介,根據官方文件接入tinker,然後測試熱修復補丁,總是莫名地失敗(或許是自己技術太渣)。無奈就不去折騰它,而選擇了第三方的tinkerpatch這個sdk,並測試補丁ok。

注:參照網上的tinker接入部落格,以及官方文件,自己總是搞不定,說是自己技術渣吧,也可能。反正我參照阿里的文件接入sophix就很順利。

閒言少續,言歸正傳,以下為接入tinkerpatch的大致步驟

  • 在專案根build.gradle加入依賴配置
      buildscript {
      	repositories {
      		...
      		maven { url 'https://dl.bintray.com/wemobiledev/maven' }
      		maven { url 'https://dl.bintray.com/tinker/maven' }
      	}
      	dependencies {
      		...
      		// TinkerPatch 外掛
      		classpath "com.tinkerpatch.sdk:tinkerpatch-gradle-plugin:1.2.8"
      	}
      }
  • 在app的build.gradle中,新增依賴
      apply from: '../tinkerpatch.gradle'
      
      
      dependencies{
          // 若使用annotation需要單獨引用,對於tinker的其他庫都無需再引用
      	implementation 'com.tinkerpatch.sdk:tinkerpatch-android-sdk:1.2.8'
      	annotationProcessor 'com.tinkerpatch.tinker:tinker-android-anno:1.9.8'
      	//dex分包,用於tinker
          implementation "com.android.support:multidex:1.0.3"
          ...
      }
  • 根據上面的配置,就需要建立換一個tinkerpatch.gradle檔案,路徑與app的builde.gradle同級。一般來說,需要注意兩個位置
    1. appKey就是你在tinkerpatch平臺上的key
    2. sevenZip的版本和路徑配置,對應到你本地的路徑。(注意,這裡可能你會下載不到,maven倉庫配置一下阿里的jcenter比較好)
    3. 7zip檔案的下載,7za這個好多人沒有,需要https://www.7-zip.org/download.html這裡面的download目錄下
      7-Zip Extra: standalone console version, 7z DLL, Plugin for Far Manager這一欄。然後解壓到你對應路徑,配置到下面就好。
    4. com.tencent.mm:SevenZip:1.2.12有時候載入不出來,你就在app的buidle.gradle中新增這個依賴implementation com.tencent.mm:SevenZip:1.2.12來下載,就好。
    5. 這裡用到一個變數 appversionName需要你配置在gradle中的一個變數,就是app的版本號。可以在根目錄的gradle.properties中配置,如:
      appVersionCode=16
      appVersionName=2.0.2
      注意,有時候AS設定中去掉compile設定項下的–offline引數比較好
      import java.util.regex.Matcher
      import java.util.regex.Pattern
      
      apply plugin: 'tinkerpatch-support'
      
      /**
       * TODO: 請按自己的需求修改為適應自己工程的引數
       */
      def bakPath = file("${buildDir}/bakApk/")
      
      tinkerpatchSupport {
          /** 可以在debug的時候關閉 tinkerPatch **/
          tinkerEnable = true
      
          /** 是否使用一鍵接入功能  **/
          reflectApplication = true
      
          /** 是否開啟加固模式,只有在使用加固時才能開啟此開關 **/
          protectedApp = false
      
          /** 補丁是否支援新增 Activity (exported必須為false)**/
          supportComponent = false
      
          autoBackupApkPath = "${bakPath}"
      
          /** 在tinkerpatch.com得到的appKey **/
          appKey = "20155552655555522"
          /** 注意: 若釋出新的全量包, appVersion一定要更新 **/
          appVersion = appVersionName
      
          baseApkFile = "${bakPath}/app-${appVersionName}.apk"
          baseProguardMappingFile = "${bakPath}/app-${appVersionName}-mapping.txt"
          baseResourceRFile = "${bakPath}/app-${appVersionName}-R.txt"
      
          /**
           * (可選)重新命名備份檔案的格式化字串,預設為'${appName}-${variantName}'
           *
           * Available vars:
           * 1. projectName
           * 2. appName
           * 3. packageName
           * 4. buildType
           * 5. versionName
           * 6. versionCode
           * 7. buildTime
           * 8. fileSHA1
           * 9. flavorName
           * 10. variantName
           *
           * default value: '${appName}-${variantName}'
           * Note: plz use single-quotation wrapping this format string
           */
          backupFileNameFormat = '${appName}-${variantName}'
      
          /**
           *  若有編譯多flavors需求, 可以參照: https://github.com/TinkerPatch/tinkerpatch-flavors-sample
           *  注意: 除非你不同的flavor程式碼是不一樣的,不然建議採用zip comment或者檔案方式生成渠道資訊(相關工具:walle 或者 packer-ng)
           **/
      }
      
      /**
       * 用於使用者在程式碼中判斷tinkerPatch是否被使能
       */
      android {
          defaultConfig {
              buildConfigField "boolean", "TINKER_ENABLE", "${tinkerpatchSupport.tinkerEnable}"
          }
      }
      
      /**
       * 一般來說,我們無需對下面的引數做任何的修改
       * 對於各引數的詳細介紹請參考:
       * https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97
       */
      tinkerPatch {
          ignoreWarning = false
          useSign = true
          dex {
              dexMode = "jar"
              pattern = ["classes*.dex"]
              loader = []
          }
          lib {
              pattern = ["lib/*/*.so"]
          }
      
          res {
              pattern = ["res/*", "r/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
              ignoreChange = []
              largeModSize = 100
          }
      
          packageConfig {
          }
          sevenZip {
              zipArtifact = "com.tencent.mm:SevenZip:1.2.12"
              path = "D:\\Android\\7z\\x64\\7za.exe"
          }
          buildConfig {
              keepDexApply = false
          }
      }
      
      /**
       * 如果只想在Release中開啟tinker,可以把tinkerEnable賦值為這個函式的return
       * @return 是否為release
       */
      def isRelease() {
          Gradle gradle = getGradle()
          String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
      
          Pattern pattern
          if (tskReqStr.contains("assemble")) {
              println tskReqStr
              pattern = Pattern.compile("assemble(\\w*)(Release|Debug)")
          } else {
              pattern = Pattern.compile("generate(\\w*)(Release|Debug)")
          }
          Matcher matcher = pattern.matcher(tskReqStr)
      
          if (matcher.find()) {
              String task = matcher.group(0).toLowerCase()
              println("[BuildType] Current task: " + task)
              return task.contains("release")
          } else {
              println "[BuildType] NO MATCH FOUND"
              return true
          }
      }

其他基本不做修改,或者可以根據自身專案配置。

  • 在自定義的application中,配置並初始化tinkerpatch:
    	private ApplicationLike tinkerApplicationLike;//Tinker
      
      	@Override
      	public void onCreate() {
      		super.onCreate();
      		//init tinkerpatch
      		initTinkerPatch();
              ...
      	}
      
      	@Override
      	public void attachBaseContext(Context base) {
      		super.attachBaseContext(base);
      		//you must install multiDex whatever tinker is installed!
      		MultiDex.install(base);
      	}
      
      	/**
      	 * 我們需要確保至少對主程序跟patch程序初始化 TinkerPatch
      	 */
      	@SuppressLint("LongLogTag")
      	private void initTinkerPatch() {
      		// 我們可以從這裡獲得Tinker載入過程的資訊
      		if (BuildConfig.TINKER_ENABLE) {
      			tinkerApplicationLike = TinkerPatchApplicationLike.getTinkerPatchApplicationLike();
      			// 初始化TinkerPatch SDK
      			TinkerPatch.init(tinkerApplicationLike)
      					.reflectPatchLibrary()
      					.setPatchRollbackOnScreenOff(true)
      					.setPatchRestartOnSrceenOff(true)
      					.setFetchPatchIntervalByHours(3);
      			// fetchPatchUpdateAndPollWithInterval 與 fetchPatchUpdate(false)
      			// 不同的是,會通過handler的方式去輪詢
      			TinkerPatch.with().fetchPatchUpdateAndPollWithInterval();
      		}
      	}

這裡需要注意的是,如果app使用了多程序,就儘可能只初始化一次。多次初始化有沒有問題,我沒試過。

  • 然後在啟動的activity或者你需要的地方自動呼叫在activity的需要的地方,呼叫TinkerPatch.with().fetchPatchUpdate(true);就會聯網請求tinkerpatch平臺上你所釋出的對應版本的補丁。
  • 以上配置ok的話,每次打包就會在project/app/build/bakapk/下面生成如app-2.0.2-1012-11-25-31樣式的資料夾,裡面有debug資料夾,下有app-debug.apk以及app-debug-R.txt。
    如果你執行的release的build,就是release資料夾下
    1. 在AS的右側Gradle欄目下,找到app-build下執行assembleRelease就能生成release的包。
    2. 然後將上述的app-release.apk以及app-release-R.txt移動到bakapk目錄下,並更改名稱如app-2.0.2.apk以及app-2.0.2-R.txt。(這實在tinkerpatch.gradle中配置的)。
    3. 如果開啟了混淆,則也需要生成基礎包的時候把mapping.txt檔案也放到如上的bakapk目錄下。
    4. 執行AS的Gradle下app–tinker–tinkerPatchRelease的task,就會生成補丁包。在app–build–outputs–apk–tinkerPatch–release下有patch-signed-7z.apk的補丁包。
    5. 上傳之tinkerpatch平臺,你的專案下,建立專案,建立補丁,然後選擇釋出補丁。
    6. 此時app啟動,會聯網下載補丁,再次退出啟動的時候,就會應用補丁。有的可能需要啟動三次(冷啟動)。

注意的是,這裡使用的演示是release構建,如果測試debug,你只需要build 對應debug包,然後tinkerpatch也是debug就行。

還有一點說明,好像tinkerpatch這個平臺免費版的下發補丁次數有限制,如果是大型專案,還是自己研究如何使用tinker官方接入方式。或者可以使用阿里的sophix,接入也是很方便。打補丁也方便。