Tinker + Bugly + Jenkins 爬坑之路

来源:https://www.cnblogs.com/cspecialy/archive/2018/06/02/9125861.html
-Advertisement-
Play Games

前陣子 Android 端的線上崩潰比較多,熱修複被提上日程。實現方案是 Tinker,Jenkins 打包,最後補丁包上傳到 Bugly 進行分發。主要在 Jenkins 打包這一塊爬了不少坑,現記錄下來,供大家參考。 1. Tinker + Bugly熱修複實現 首先是本地實現,按照官方文檔,只 ...


前陣子 Android 端的線上崩潰比較多,熱修複被提上日程。實現方案是 Tinker,Jenkins 打包,最後補丁包上傳到 Bugly 進行分發。主要在 Jenkins 打包這一塊爬了不少坑,現記錄下來,供大家參考。

1. Tinker + Bugly熱修複實現

首先是本地實現,按照官方文檔,只要一步一步按照文檔來,這個步驟還是比較容易的,這裡就不再贅述了,不懂的可以先參考官方文檔:Bugly Android熱更新使用指南Bugly Android熱更新詳解。這裡貼一下接入流程:

  • 打基準包安裝並上報聯網(註:填寫唯一的 tinkerId)

  • 對基準包的 bug 修複(可以是 Java 代碼變更,資源的變更)

  • 修改基準包路徑、修改補丁包 tinkerId、mapping 文件路徑(如果開啟了混淆需要配置)、resId 文件路徑

  • 執行 buildTinkerPatchRelease 打 Release 版本補丁包

  • 選擇 app/build/outputs/patch目錄 下的補丁包並上傳(註:不要選擇 tinkerPatch 目錄下的補丁包,不然上傳會有問題

  • 編輯下發補丁規則,點擊立即下發

  • 殺死進程並重啟基準包,請求補丁策略( SDK 會自動下載補丁併合成)

  • 再次重啟基準包,檢驗補丁應用結果

  • 查看頁面,查看激活數據的變化

這裡說一下使用指南中的第三步:初始化 SDK,我這裡使用的是 enableProxyApplication = false 的方式,原本想用 enableProxyApplication = true 的這種比較靈活的方式,但是程式編譯報錯,沒時間去深究報錯的原因,加上直接繼承的方式接入也沒什麼代價,就沒管是為什麼了,知道原因的可以順手告知下。 ┑( ̄Д  ̄)┍

一通擼下來還是比較容易的,完成代碼的接入後,先打個包(基準包),安裝到手機上運行一遍,使程式聯網上報到 Bugly。之後,再按照打基準包的基線版本,修改 tinker-support.gradle 文件中的 baseApkDir 參數,然後就可以打補丁包了。

2. 結合 Jenkins 所遇到的坑

先說明一下我司使用 Jenkins 打包 apk 的背景知識。Jenkins 打包 apk 使用的是 Ant 插件,打包腳本由於公司項目的原因,不方便展示出來,大家如果有疑問的話,可以在評論里說明,本人會私下裡幫助大家解決。

下麵爬坑 /(ㄒoㄒ)/~~

坑1 ☞ 打補丁包時,基準包哪裡找?

由於公司 Jenkins 的打包策略是,在構建之前,先執行 clean 命令,這也就意味著,像本地打包一樣在 app/build/bakApk/app-xxxx-xx-xx-xx 目錄下找到基準包已是不可能。那怎麼辦,沒有基準包怎麼打增量包?苦思良久,愚笨的我最終想到,在項目工程路徑下創建一個文件夾,要打增量包時,將基準包拷貝到該文件夾,然後上傳 SVN。這時,旁邊同學來了句:可以找運維同學,雙方約定一個目錄,打基準包時將基準包由腳本拷貝過去,打補丁包時從約定的目錄取就行((ಥ _ ಥ) 我咋就想不到...)。

然後屁顛屁顛的跑去找運維同學,溝通後發現,Jenkins 每次打包都會在 Jenkins 目錄下的 /jobs/pipeline名稱/builds/構建編號/archive/app/build/outputs/apk/kungeek/release/ 保存一份 apk 文件的副本。路徑中 構件編號 如圖所示:

接下來,打補丁包時將 tinker-support.gradle 文件中的 baseApkDir 參數修改為 /jobs/pipeline名稱/builds/構建編號/archive/app/build/outputs/apk/kungeek/release/ 即可。代碼如下:

/**
 * 此處填寫每次構建生成的基準包目錄,註意變數要自定義
 */
def baseApkDir = "${rootProject.projectDir}/../../jobs/${pipeline名稱}/builds/${baseApkBuildNumber}/archive/app/build/outputs/apk/kungeek/release"

坑2 ☞ Linux 下文件拷貝通配符問題

由於構建基準包的同時生成的 mapping 文件(如果開啟了混淆需要配置)、resId 文件在構建補丁包時也需要用到,所以,在構建基準包時,需要將這兩個文件拷貝到 /jobs/pipeline名稱/builds/構建編號/archive/app/build/outputs/apk/kungeek/release/ 目錄下,拷貝代碼如下:

<!--複製 tinker 生成的文件(apk文件、mapping.txt、R.txt)-->
<copy todir="../../../../jobs/pipeline名稱/builds/${env.BUILD_NUMBER}/archive/app/build/outputs/apk/kungeek/release/" flatten="true">
    <fileset dir="${android.root}/app/build/bakApk/">
        <include name="*/*" />
    </fileset>
</copy>

註1:代碼中相對路徑問題讀者有疑問的話,麻煩再評論去提問。

註2:代碼中構建編碼使用到了 Jenkins 的環境變數,需要先在 Ant 的構建腳本文件的 project 的標簽下添加 <property environment="env"/> 來導入。

這裡遇到的坑是:因為 Tinker 構建的 apk 文件是存放在 app-xxxx-xx-xx-xx 目錄下,所以需要使用通配符來輔助複製文件,運維同學原本是想將通配符加到 fileset 中形成以後完整的路徑,經過一段痛苦的嘗試以及百度後發現,通配符只能在 include 標簽中使用。(ノへ ̄、)

坑3 ☞ 構建補丁包完成後找不到生成的補丁包?

踩過前面一個一個的坑,終於在 Jenkins 上打了基準包之後,/jobs/pipeline名稱/builds/構建編號/archive/app/build/outputs/apk/kungeek/release/ 目錄下有了 基準包 apk 文件mapping 文件resId 文件

接下來,我以為,只需要配置好基準包的構件編號等相關配置參數,再構建補丁包就沒問題了。然後 Jenkins 在構建好補丁包 apk 文件後,展示成果時報出的 apk 文件未找到 給了我當頭一棒,依然失敗。挫敗感油然而生~~~

之後,經運維同學確認,Jenkins 構建期間是有在 app/build/outputs/patch 目錄下生成 patch_signed_7zip.apk 文件的,但是構建完成之後,又沒了。然後我試著看了下構建過程中執行的命令,長這樣的:

sh gradlew clean buildTinkerPatchRelease  --stacktrace
sh gradlew checklist

執行了 buildTinkerPatchRelease 後,還執行的 checklist 任務,難道是執行 checklist 時把 patch 給清空了,之後我嘗試把這個命令註釋掉,再次打補丁包時成功。果然是這個 checklist 惹的事啊,事後發現,打補丁包後,再次執行 gradle task,基本都會清空 patch 目錄,這是個坑,大家記得避免。

坑4 ☞ 一個項目中多個 application 時,打補丁包不成功?

我們知道,在 Android Studio 中,一個 project 可以有多個 module,包括 application 類型的 module,一般情況下,執行 gradlew assembleRelease 任務會將所有的 APP 都打包,這裡打基準包也沒問題,但是打補丁包時就不行了,只能成功一個。

這裡提供分開打包一個方案:在每個 application 的 build.gradle 中配置 productFlavors,且每個 application 的命名都得不一樣,這樣,針對不同的 APP 就會產生不同的構建 task,比如:在 A 的 build.gradle 中配置名為 a_app,則回產生一個名為 buildTinkerPatchA_appRelease 的 task,最終使用此 task 來打補丁包即可。

那麼問題來了,最終打包的形式是什麼呢?是這樣?

sh gradlew buildTinkerPatchA_appRelease buildTinkerPatchA_appRelease

還是這樣?

sh gradlew buildTinkerPatchA_appRelease 
sh gradlew buildTinkerPatchA_appRelease

都不是,這兩種方式其實和不配置 productFlavors 的打包方式是一樣的,那麼如何打包呢?

答案是在 Ant 的打包腳本中,執行多次打包,關鍵代碼如下:

<!--構建APP a-->
<exec dir="." executable="bash" failonerror="false">
    <arg value="generated_apk_hotfix.sh"/>
    <arg value="buildTinkerPatchApp_aRelease"/>
</exec><!--構建APP b-->
<exec dir="." executable="bash" failonerror="false">
    <arg value="generated_apk_hotfix.sh"/>
    <arg value="buildTinkerPatchApp_bRelease"/>
</exec>

構建腳本 generated_apk_hotfix.sh 文件關鍵代碼如下:

#!/bin/sh
command=$1;
​
# 增量包需分開打包,否則會失敗
sh gradlew ${command} --stacktrace

3. 總結

上面說到的坑只有 4 點,但實際上也遇到過挺多小問題的,但那些就不用多說了,很容易解決。

最後,總結一下結合 Jenkins 構建補丁包的思路。

首先,約定好基線版本的基準包 apk 包、mapping 文件、R.txt 文件的存放路徑,打基準包時將這三個文件存入該目錄。如果跟本文一樣存放在 Jenkins 的 pipeline 構建目錄下的話,記得要調整 pipeline 的清理策略,否則等需要打補丁包的時候,發現基線版本 apk 包什麼的被清理掉就尷尬了,我這裡是考慮到重覆利用空間,所以放入此目錄下。

其次,通過約定的路徑,找到基準包、mapping 文件、R.txt 文件,打補丁包。這裡需要確定一個找到基準包的策略,比如,我這裡是通過構建編號來匹配存放基準包的路徑,然後通過固定命名格式(如:app_release_版本號.apk)來匹配基準包以及 mapping 文件和 R.txt 文件,如此下來,我只需要確定基線版本的版本號和構建編號即可。

最後,貼一下我最終的 tinker-support.gradle 文件代碼內容,大家有需要的可以參考:

apply plugin: 'com.tencent.bugly.tinker-support'
​
def bakPath = file("${buildDir}/bakApk/")
​
/** 基準包的 Jenkins 構建編號*/
def baseApkBuildNumber = project.property("baseApkBuildNumber")
/** 基準包的版本號*/
def baseApkVersion = project.property("baseApkVersion")
​
/**
 * 此處填寫每次構建生成的基準包目錄
 */
def baseApkDir = "${rootProject.projectDir}/../../jobs/Android_Trunk/builds/${baseApkBuildNumber}/archive/app/build/outputs/apk/release"/** 基準包的 apk 文件名*/
def baseApkFileName = "app-v${baseApkVersion}"/**
 * 對於插件各參數的詳細解析請參考
 */
tinkerSupport {
​
    // 開啟tinker-support插件,預設值true
    enable = true// tinkerEnable功能開關
    tinkerEnable = true// 指定歸檔目錄,預設值當前module的子目錄tinker
    autoBackupApkDir = "${bakPath}"
​
    autoGenerateTinkerId = true// 打基準包時生成 R.txt、mapping.txt 文件名的首碼
    // rootProject.ext.android_version 指打包時的版本號
    targetFileNamePrefix = "app-v${rootProject.ext.android_version}"// 是否啟用覆蓋tinkerPatch配置功能,預設值false
    // 開啟後tinkerPatch配置不生效,即無需添加tinkerPatch
    overrideTinkerPatchConfiguration = true
    // 編譯補丁包時,必需指定基線版本的apk,預設值為空
    // 如果為空,則表示不是進行補丁包的編譯
    // @{link tinkerPatch.oldApk }
    baseApk = "${baseApkDir}/${baseApkFileName}.apk"// 對應tinker插件applyMapping
    baseApkProguardMapping = "${baseApkDir}/${baseApkFileName}-mapping.txt"// 對應tinker插件applyResourceMapping
    baseApkResourceMapping = "${baseApkDir}/${baseApkFileName}-R.txt"
​
    tinkerId = "base-1.0.1"//    buildAllFlavorsDir = "${bakPath}/${baseApkDir}"
    // 是否開啟加固模式,預設為false
    // isProtectedApp = true
// 是否開啟反射Application模式
    enableProxyApplication = false
​
    supportHotplugComponent = true
​
}
​
/**
 * 一般來說,我們無需對下麵的參數做任何的修改
 * 對於各參數的詳細介紹請參考:
 * https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97
 */
tinkerPatch {
    //oldApk ="${bakPath}/${appName}/app-release.apk"
// tinkerEnable功能開關
    tinkerEnable = true
    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.1.10"
    }
    buildConfig {
        keepDexApply = false
    }
}

然後是維護在 gradle.properties 文件中的兩個變數:

# 打增量包時基準包的 Jenkins 構建編號
baseApkBuildNumber = 1
# 打增量包時基準包的版本號
baseApkVersion = 1.0.0.197094

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 查看有哪些資料庫: 創建,刪除資料庫: 查看有哪些表: 創建,刪除表: 查看表結構: 設置表的主鍵: 設置,刪除表的外鍵: 子表的外鍵關聯必須是父表的主鍵,而且數據類型必須一致。 新增欄位: 刪除欄位: 修改欄位排列位置: 更改表的存儲引擎: 刪除被其他表關聯的父表時,應該先刪除子表的外鍵約束,然後 ...
  • 需要學習的朋友可以通過網盤下載: http://tadown.com/fs/8yi6be9nsehu9e921/內容簡介 · · · · · · 《MySQL必知必會》MySQL是世界上最受歡迎的資料庫管理系統之一。書中從介紹簡單的數據檢索開始,逐步深入一些複雜的內容,包括聯結的使用、子查詢、正則表 ...
  • 前言 “當你不創造東西時,你只會根據自己的感覺而不是能力去看待問題。” – WhyTheLuckyStiff 彙總一些自己在大數據路上走過的彎路,願大家不再掉坑… 1.分散式存儲 傳統化集中式存儲存在已有一段時間。但大數據並非真的適合集中式存儲架構。Hadoop設計用於將計算更接近數據節點,同時採用 ...
  • hadoop安全目錄: kerberos(已發佈) elasticsearch(已發佈)http://blog.51cto.com/chenhao6/2113873 knox oozie ranger apache sentry 簡介: 從運維青銅到運維白銀再到運維黃金,這裡就要牽扯到方向問題也就是 ...
  • BEGINSET NOCOUNT ON;if @_MODE NOT IN ('A','M','D') begin raiserror('參數錯誤!',16,3); return; end; declare @rowcount int,@error int; if @_MODE='A'begin in ...
  • 1、資料庫文件類型: ①數據文件 主要數據文件:尾碼 .mdf ,有且只有一個,預設已創建,包含啟動信息、數據對象 次要數據文件:尾碼 .ndf ,可有任意個,預設無 文件流數據:存儲圖片、音頻等文件 ②事務日誌文件:尾碼 .ldf ,至少一個,預設已創建一個,記錄所有事務的SQL語句,用於恢複數據 ...
  • 10-1 elasticsearch介紹 目前在使用es的大公司: https://www.elastic.co/use-cases Mongodb redis 在 elasticsearch面前就是一個玩笑 哈哈! 10-2 elasticsearch安裝 10-3elasticsearch-he ...
  • CompileSdkVersion:編譯版本,就是運行這個項目需要的SDK,即API Level。 buildToolsVerson:是構建工具的版本,構建工具包括了打包工具aapt、dx等等。 註意: 1、buildtools的目錄位於 android_sdk_path/built tools/X ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...