前言
該文章屬於初級整合詳解,側重Tinker的使用,如若想深入瞭解其原理請自行查閱相關文件Tinker相關文件
當前市面的熱補丁方案有很多,其中比較出名的有阿里的 AndFix、美團的 Robust 以及 QZone 的超級補丁方案,先來做一個對比
通過對比我們發現Tinker還是有一定的優勢。 下面我們就來擺一擺Tinker的用法1.先去Tinker平臺註冊一個AppKey,後面會使用到
2.新增 gradle 外掛依賴
gradle 遠端倉庫依賴 jcenter
buildscript {
repositories {
//mavenLocal()
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
//無需再單獨引用tinker的其他庫
classpath "com.tinkerpatch.sdk:tinkerpatch-gradle-plugin:${TINKERPATCH_VERSION}"
}
}
複製程式碼
3.整合 TinkerPatch SDK
新增 TinkerPatch SDK 庫的 denpendencies 依賴
//若使用annotation需要單獨引用,對於tinker的其他庫都無需再引用
annotationProcessor("com.tinkerpatch.tinker:tinker-android-anno:${TINKER_VERSION}") { changing = true }
compileOnly("com.tinkerpatch.tinker:tinker-android-anno:${TINKER_VERSION}") { changing = true }
implementation("com.tinkerpatch.sdk:tinkerpatch-android-sdk:${TINKERPATCH_VERSION}") { changing = true }
複製程式碼
4.新建tinkerpatch.gradle tinkerpatch.gradle
為了操作簡單,可以直接複製上面連結中的tinkerpatch.gradle放在app目錄下 為了簡單方便,我們將 TinkerPatch 相關的配置都放於 tinkerpatch.gradle 中 在app的build.gradle中引入
apply from: 'tinkerpatch.gradle'
複製程式碼
tinkerpatch.gradle中的baseInfo和variantName可以先不用管,後面使用
def bakPath = file("${buildDir}/bakApk/")
def baseInfo = "app-1.0.0-0115-14-59-51"
def variantName = "debug"
複製程式碼
5.配置tinkerpatchSupport引數
1)baseInfo和variantName引數先按照這個放著暫時不管
2)appKey請自行登入tinker官網登入並建立應用獲取即可 To: www.tinkerpatch.com/
3)appVersion版本號一般對應你的versionName就行了(versionName改的話 這裡就改,這裡的appVersion對應tinker官網上傳patch時的版本,切記!)
/** 可以在debug的時候關閉 tinkerPatch **/
/** 當disable tinker的時候需要新增multiDexKeepProguard和proguardFiles,
這些配置檔案本身由tinkerPatch的外掛自動新增,當你disable後需要手動新增
你可以copy本示例中的proguardRules.pro和tinkerMultidexKeep.pro,
需要你手動修改'tinker.sample.android.app'本示例的包名為你自己的包名, com.xxx字首的包名不用修改
**/
tinkerEnable = true
reflectApplication = true
/**
* 是否開啟加固模式,只能在APK將要進行加固時使用,否則會patch失敗。
* 如果只在某個渠道使用了加固,可使用多flavors配置
**/
protectedApp = false
/**
* 實驗功能
* 補丁是否支援新增 Activity (新增Activity的exported屬性必須為false)
**/
supportComponent = true
autoBackupApkPath = "${bakPath}"
appKey = "你第一步申請的appkey"
/** 注意: 若釋出新的全量包, appVersion一定要更新 **/
appVersion = "1.0.0"
def pathPrefix = "${bakPath}/${baseInfo}/${variantName}/"
def name = "${project.name}-${variantName}"
baseApkFile = "${pathPrefix}/${name}.apk"
baseProguardMappingFile = "${pathPrefix}/${name}-mapping.txt"
baseResourceRFile = "${pathPrefix}/${name}-R.txt"
複製程式碼
具體的引數詳解如下
引數 | 預設值 | 描述 |
---|---|---|
tinkerEnable | true | 是否開啟 tinkerpatchSupport 外掛功能。 |
appKey | "" | 在 TinkerPatch 平臺 申請的 appkey, 例如 sample 中的 'f828475486f91936' |
appVersion | "" | 在 TinkerPatch 平臺 輸入的版本號, 例如 sample 中的 '1.0.0'。 注意,我們使用 appVersion 作為 TinkerId, 我們需要保證每個釋出出去的基礎安裝包的 appVersion 都不一樣。 |
reflectApplication | false | 是否反射 Application 實現一鍵接入;一般來說,接入 Tinker 我們需要改造我們的 Application, 若這裡為 true, 即我們無需對應用做任何改造即可接入。 |
autoBackupApkPath | "" | 將每次編譯產生的 apk/mapping.txt/R.txt 歸檔儲存的位置 |
baseApkFile | "" | 基準包的檔案路徑, 對應 tinker 外掛中的 oldApk 引數;編譯補丁包時,必需指定基準版本的 apk,預設值為空,則表示不是進行補丁包的編譯。 |
baseProguardMappingFile | "" | 基準包的 Proguard mapping.txt 檔案路徑, 對應 tinker 外掛 applyMapping 引數;在編譯新的 apk 時候,我們希望通過保持基準 apk 的 proguard 混淆方式,從而減少補丁包的大小。這是強烈推薦的,編譯補丁包時,我們推薦輸入基準 apk 生成的 mapping.txt 檔案。 |
baseResourceRFile | "" | 基準包的資源 R.txt 檔案路徑, 對應 tinker 外掛 applyResourceMapping 引數;在編譯新的apk時候,我們希望通基準 apk 的 R.txt 檔案來保持 Resource Id 的分配,這樣不僅可以減少補丁包的大小,同時也避免由於 Resource Id 改變導致 remote view 異常。 |
protectedApp | false | 是否開啟支援加固,注意:只有在使用加固時才能開啟此開關 |
supportComponent | false | 是否開啟支援在補丁包中動態增加Activity 注意:新增Activity的Exported屬性必須為false |
backupFileNameFormat | '${appName}-${variantName}' | 格式化命名備份檔案 這裡請使用單引號 |
上述步驟 配置完之後 sync編譯即可
6.初始化 TinkerPatch SDK
以上屬性中有一個reflectApplication 屬性,所以初始化 TinkerPatch SDK有兩種方法
1. reflectApplication = true 的情況
若我們使用 reflectApplication 模式,我們無需為接入 Tinker 而改造我們的 Application 類。相當於自己寫一個Application,本人就是使用的這種方式。
public class MyApplication extends Application {
private ApplicationLike tinkerApplicationLike;
@Override
public void onCreate() {
super.onCreate();
initTinkerPatch();
}
/**
* 我們需要確保至少對主程式跟patch程式初始化 TinkerPatch
*/
private void initTinkerPatch() {
if (BuildConfig.TINKER_ENABLE) {
// 我們可以從這裡獲得Tinker載入過程的資訊
// 我們可以從這裡獲得Tinker載入過程的資訊
tinkerApplicationLike = TinkerPatchApplicationLike.getTinkerPatchApplicationLike();
// 初始化TinkerPatch SDK, 更多配置可參照API章節中的,初始化SDK
TinkerPatch.init(tinkerApplicationLike)
.reflectPatchLibrary()
.setPatchRollbackOnScreenOff(true)
.setPatchRestartOnSrceenOff(true)
.setFetchPatchIntervalByHours(3);
// 每隔3個小時(通過setFetchPatchIntervalByHours設定)去訪問後臺時候有更新,通過handler實現輪訓的效果
TinkerPatch.with().fetchPatchUpdateAndPollWithInterval();
}
}
}
複製程式碼
2. reflectApplication = false 的情況
不需要自己寫Application類,新建一個整合DefaultApplicationLike的類即可
public class SampleApplicationLike extends DefaultApplicationLike {
...
@Override
public void onCreate() {
super.onCreate();
// 初始化TinkerPatch SDK, 更多配置可參照API章節中的,初始化 SDK
TinkerPatch.init(this)
.reflectPatchLibrary()
.setPatchRollbackOnScreenOff(true)
.setPatchRestartOnSrceenOff(true)
.setFetchPatchIntervalByHours(3);
// 每隔3個小時(通過setFetchPatchIntervalByHours設定)去訪問後臺時候有更新,通過handler實現輪訓的效果
TinkerPatch.with().fetchPatchUpdateAndPollWithInterval();
}
...
}
複製程式碼
setFetchPatchIntervalByHour(3)// 每隔3個小時(通過setFetchPatchIntervalByHours設定)去訪問後臺時候有更新,通過handler實現輪訓的效果 如果你想快點看見效果,可以將這個數字改小點
7.AndroidManifest.xml中完成配置
將AndroidManifest.xml中的新增上相應的網路和SD的許可權,還要在application中加上 android:name=".MyApplication"
<!--tinker修復所需-->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:name=".MyApplication"
複製程式碼
到現在tinker基本上已經整合完了
8.開始體驗
我們在xml裡隨便寫個view以便區分新舊包。
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="我是舊版"
android:textSize="30dp" />
複製程式碼
寫完之後找到編輯器右側的Gradle下點選build下的assembleDebug進行編譯
完成後在左側build檢視,最下面就是最新生成的apk
生成之後,我們將此debug包安裝到手機上,執行效果為我們剛才在xml裡寫的舊佈局接下來,我們打補丁包(模擬修復bug)
首先到tinkerpatch.gradle裡更改我們們先前介紹的兩個引數: baseInfo:修改為上面生成的對應資料夾名(請修改為自己的) variantName:因為打的debug包,所以傳入debug即可
/**
* TODO: 請按自己的需求修改為適應自己工程的引數
*/
//基包路徑
def bakPath = file("${buildDir}/bakApk/")
//基包資料夾名(打補丁包的時候,需要修改)
def baseInfo = "app-1.0.0-0116-11-22-29"(上圖中的最後一個app-.....)
//版本名稱
def variantName = "debug"
複製程式碼
然後我們在xml中做寫修改
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="我是新版"
android:textSize="30dp" />
複製程式碼
然後打出差異包補丁,繼續找到Gradle下的tinker目錄,點選tinkerPatchDebug進行編譯
編譯完成之後,請到工程目錄下app->outputs->檢視生成的資料夾 tinkerPatch: 接下來,我們將圖中箭頭所指的patch_signed_7zip.apk上傳至tinker官網(釋出補丁): 點選圖中的補丁下發,然後將上面生成的patch_signed_7zip.apk上傳至補丁檔案處,點選提交接下來就是見證奇蹟的時刻
退出應用,再次開啟應用,顯示的就是新的佈局檔案的內容。
然後到tinker官網,我們可以檢視補丁下發進度及當前成功率。