前言
希望通過本文能夠幫助之前沒有接觸過 Tinker 的同學,快速了解利用 Tinker 生成及使用補丁包的過程。
Tinker 版本:1.8.1
Tinker 官方文檔:https://github.com/Tencent/tinker/wiki
gradle 接入
我沒有 clone 官方的例子,也沒有在現有項目上直接接入,而是創建了一個新的項目。
在項目的 build.gradle 中,添加tinker-patch-gradle-plugin
依賴:
buildscript {
dependencies {
classpath ('com.tencent.tinker:tinker-patch-gradle-plugin:1.8.1')
}
}
然后在 app 的 gradle 文件 app/build.gradle,添加 tinker 的庫依賴以及 apply tinker 的 gradle 插件:
dependencies {
provided('com.tencent.tinker:tinker-android-anno:1.8.1')
compile('com.tencent.tinker:tinker-android-lib:1.8.1')
}
...
apply plugin: 'com.tencent.tinker.patch'
在這一步不用著急編譯,下面還有 gradle 的詳細配置。
生成 Application
tinker 建議編寫一個 DefaultApplicationLike 的子類,并使用@DefaultLifeCycle
注解生成 Application.
@DefaultLifeCycle(application = ".MyApplication", //要生成的application名稱
flags = ShareConstants.TINKER_ENABLE_ALL)
public class MyApplicationLike extends DefaultApplicationLike {
public MyApplicationLike(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);
TinkerInstaller.install(this);
}
}
注意 application 名稱為.MyApplication
,我創建的 MyApplicationLike 所在的包為tech.gujin.tinkersample.application
,生成的 MyApplication 也在這個包下。或者將包名寫全:application = "tech.gujin.tinkersample.application.MyApplication"
,生成的文件都是一樣的。
之后注冊 Application 到 AndroidManifest 中:
<application
android:name="tech.gujin.tinkersample.application.MyApplication"
...
</application>
如果報錯現在不用處理,編譯后會自動生成的。
gradle 配置
我直接拷貝了官方例子,修改了其中獲取 tinkerId 的方法,bakPath 的路徑,移除了多渠道部分。
def bakPath = file("./tinker-old/") //可自行定義文件路徑
ext {
tinkerId = "tinker_id_" + android.defaultConfig.versionName + "_" + android.defaultConfig.versionCode
tinkerEnabled = true
tinkerOldApkPath = "${bakPath}/app-release-1.0-1.apk" //可自行修改文件名
tinkerApplyMappingPath = "${bakPath}/app-release-1.0-1-mapping.txt"
tinkerApplyResourcePath = "${bakPath}/app-release-1.0-1-R.txt"
}
因為要生成補丁,tinker 需要上一個版本的安裝包用來比較差異。
我創建了tinker-old
文件夾,放入上一個版本名為app-release-1.0-1.apk
的安裝包,并將路徑設置給 tinkerOldApkPath.
為了減少補丁包的大小,還可以繼續設置 tinkerApplyMappingPath 和 tinkerApplyResourcePath.
其余地方并無太大變化,更多可以看修改后的gradle。
首次接入沒有舊的安裝包也不用擔心,如果指定文件不存在 tinker 會自動忽略。
使用前的準備
創建一個很簡單的 Activity:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((TextView) findViewById(R.id.tv_msg)).setText("此版本存在BUG");
File file = new File(getExternalCacheDir(), "/patch_signed_7zip.apk");
if (file.exists()) {
TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), file.getAbsolutePath());
}
}
}
這里省去了訪問服務器判斷有沒有補丁包和下載的邏輯,簡化為如果/storage/emulated/0/Android/data/tech.gujin.tinkersample/cache
目錄下有patch_signed_7zip.apk
補丁包就進行更新。
然后編譯一個 release 包,安裝到手機上:
制作補丁包
修改 TextView 的文字,然后升級下 versionCode 和 versionName,就當是 1.1 版本修復了 1.0 版本的 Bug.
剛才的打包過程會自動將 apk,mapping 和 R 文件復制到tinker-old
文件夾中,根據文件名配置下 tinkerOldApkPath、tinkerApplyMappingPath 和 tinkerApplyResourcePath.
然后在 Terminal 輸入 gradlew tinkerPatchRelease
,或者在 Gradle projects 中找到相應的 project 運行即可。
等運行結束后,補丁包生成在/build/outputs/tinkerPatch
目錄下,使用patch_signed_7zip.apk
,它是簽名后并使用 7zip 壓縮的補丁包。
到此為止,補丁包就制作好了。
使用補丁
本例中簡化了流程,我直接將補丁放到 /storage/emulated/0/Android/data/tech.gujin.tinkersample/cache
目錄。
殺死進程重新打開應用,可以看到打印出 log:
I/Tinker.DefaultTinkerResultService: DefaultTinkerResultService received a result:
PatchResult:
isSuccess:true
rawPatchFilePath:/storage/emulated/0/Android/data/tech.gujin.tinkersample/cache/patch_signed_7zip.apk
costTime:530
patchVersion:a5c8417e691fd9cffe83c11cd0d37eff
然后應用直接退出了,這是因為 tinker 提供的 DefaultTinkerResultService 中,補丁升級成功后會殺死當前進程,可以繼承 DefaultTinkerResultService 實現自己的回調。
再次打開應用可以看到補丁已經生效了:
至此,tinker 生成及更新補丁包的過程就已經介紹完畢了。
代碼已托管至 Github:GuJin/TinkerSample
謝謝大家。