如需轉載請評論或簡信,并注明出處,未經允許不得轉載
目錄
官網
版本更新狀況
Latest commit4247313 8 days ago
目前時間2020年5月27日,可以看出到目前為止Tinker一直在更新
使用場景
Tinker是微信官方的Android熱補丁解決方案,它支持動態下發代碼、So庫以及資源,讓應用能夠在不需要重新安裝的情況下實現更新。當然,你也可以使用Tinker來更新你的插件
下面的表格來源于Tinker官方wiki
Tinker | QZone | AndFix | Robust | |
---|---|---|---|---|
類替換 | yes | yes | no | no |
So替換 | yes | no | no | no |
資源替換 | yes | yes | no | no |
全平臺支持 | yes | yes | yes | yes |
即時生效 | no | no | yes | yes |
性能損耗 | 較小 | 較大 | 較小 | 較小 |
補丁包大小 | 較小 | 較大 | 一般 | 一般 |
開發透明 | yes | yes | no | no |
復雜度 | 較低 | 較低 | 復雜 | 復雜 |
gradle支持 | yes | no | no | no |
Rom體積 | 較大 | 較小 | 較小 | 較小 |
成功率 | 較高 | 較高 | 一般 | 最高 |
Tinker的已知問題
由于原理與系統限制,Tinker有以下已知問題:
- Tinker不支持修改AndroidManifest.xml,Tinker不支持新增四大組件(1.9.0支持新增非export的Activity);
- 由于Google Play的開發者條款限制,不建議在GP渠道動態更新代碼;
- 在Android N上,補丁對應用啟動時間有輕微的影響;
- 不支持部分三星android-21機型,加載補丁時會主動拋出
"TinkerRuntimeException:checkDexInstall failed"
; - 對于資源替換,不支持修改remoteView。例如transition動畫,notification icon以及桌面圖標。
Tinker的TODO
Tinker經過幾次全量上線,也發現了一些熱補丁的問題。有以下的一些優化工作尚未完成:
- Crash 啟動保護;
- Android Q 或更新的系統上降低補丁帶來的開銷。
集成步驟
根據Tinker官網的步驟,我們能夠完成一個Tinker的本地繼承。但是熱更新往往需要一個后臺系統來管理和下發補丁包,這樣一來就增加了使用熱更新的成本。好在騰訊的Bugly提供了Tinker補丁管理系統,而且目前是免費的,所以本文就基于騰訊的Bugly來介紹如何集成Tinker熱更新
在Bugly上創建產品,獲取APP ID
-
進入bugly產品管理,點擊新建產品
-
輸入產品信息
-
完成后回到我的產品,點擊進入
-
點擊產品設置,獲取APP ID,記錄下來
添加插件依賴
工程根目錄下“build.gradle”文件中添加:
buildscript {
repositories {
jcenter()
}
dependencies {
// tinkersupport插件, 其中lastest.release指拉取最新版本,也可以指定明確版本號,例如1.0.4
classpath "com.tencent.bugly:tinker-support:1.1.5"
}
}
注意:自 tinkersupport 1.0.3
版本起無需再配tinker插件的classpath。
版本對應關系:
tinker-support 1.1.3 對應 tinker 1.9.8
tinker-support 1.1.2 對應 tinker 1.9.6
tinker-support 1.1.1 對應 tinker 1.9.1
tinker-support 1.0.9 對應 tinker 1.9.0
tinker-support 1.0.8 對應 tinker 1.7.11
tinker-support 1.0.7 對應 tinker 1.7.9
tinker-support 1.0.4 對應 tinker 1.7.7
tinker-support 1.0.3 對應 tinker 1.7.6
tinker-support 1.0.2 對應 tinker 1.7.5(需配置tinker插件的classpath)
集成SDK
gradle配置
android {
defaultConfig {
ndk {
//設置支持的SO庫架構
abiFilters 'armeabi' //, 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
}
}
}
dependencies {
// 多dex配置
api "com.android.support:multidex:1.0.1"
// bugly遠程倉庫
api 'com.tencent.bugly:crashreport_upgrade:1.3.6'
// 指定tinker依賴版本(注:應用升級1.3.5版本起,不再內置tinker)
api 'com.tencent.tinker:tinker-android-lib:1.9.9'
}
注意:
1.升級SDK已經集成crash上報功能,已經集成Bugly的用戶需要注釋掉原來Bugly的jcenter庫;
2.已經配置過符號表的Bugly用戶保留原有符號表配置;
3.Bugly SDK(2.1.5及以上版本)已經將Java Crash和Native Crash捕獲功能分開,如果想使用NDK庫,需要配置:
compile 'com.tencent.bugly:nativecrashreport:latest.release'
在app module的“build.gradle”文件中添加:
// 依賴插件腳本
apply from: 'tinker-support.gradle'
tinker-support.gradle內容如下所示(示例配置):
注:您需要在同級目錄下創建tinker-support.gradle這個文件
apply plugin: 'com.tencent.bugly.tinker-support'
def bakPath = file("${buildDir}/bakApk/")
/**
* 此處填寫每次構建生成的基準包目錄
*/
def baseApkDir = "app-0208-15-10-00"
/**
* 對于插件各參數的詳細解析請參考
*/
tinkerSupport {
// 開啟tinker-support插件,默認值true
enable = true
// 指定歸檔目錄,默認值當前module的子目錄tinker
autoBackupApkDir = "${bakPath}"
// 是否啟用覆蓋tinkerPatch配置功能,默認值false
// 開啟后tinkerPatch配置不生效,即無需添加tinkerPatch
overrideTinkerPatchConfiguration = true
// 編譯補丁包時,必需指定基線版本的apk,默認值為空
// 如果為空,則表示不是進行補丁包的編譯
// @{link tinkerPatch.oldApk }
baseApk = "${bakPath}/${baseApkDir}/app-release.apk"
// 對應tinker插件applyMapping
baseApkProguardMapping = "${bakPath}/${baseApkDir}/app-release-mapping.txt"
// 對應tinker插件applyResourceMapping
baseApkResourceMapping = "${bakPath}/${baseApkDir}/app-release-R.txt"
// 構建基準包和補丁包都要指定不同的tinkerId,并且必須保證唯一性
tinkerId = "base-1.0.1"
// 構建多渠道補丁時使用
// buildAllFlavorsDir = "${bakPath}/${baseApkDir}"
// 是否啟用加固模式,默認為false.(tinker-spport 1.0.7起支持)
// isProtectedApp = true
// 是否開啟反射Application模式
enableProxyApplication = false
// 是否支持新增非export的Activity(注意:設置為true才能修改AndroidManifest文件)
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"
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"
// path = "/usr/local/bin/7za"
}
buildConfig {
keepDexApply = false
//tinkerId = "1.0.1-base"
//applyMapping = "${bakPath}/${appName}/app-release-mapping.txt" // 可選,設置mapping文件,建議保持舊apk的proguard混淆方式
//applyResourceMapping = "${bakPath}/${appName}/app-release-R.txt" // 可選,設置R.txt文件,通過舊apk文件保持ResId的分配
}
}
更詳細的配置項參考tinker-support配置說明
到這一步完成之后,構建項目,可能會遇到一個問題
can't find tinkerProcessReleaseManifest, you must init tinker plugin first!
官方的問題解答里面說可以關閉Instant Run,但是我用的AS版本是3.6.3,已經沒有Instant Run功能了。最終通過修改gradle tool版本為3.4.0得以解決,具體原因目前還不是特別清楚
classpath 'com.android.tools.build:gradle:3.4.0'
初始化SDK
enableProxyApplication = false
是Tinker推薦的接入方式,一定程度上會增加接入成本,但具有更好的兼容性
集成Bugly升級SDK之后,我們需要按照以下方式自定義ApplicationLike
來實現Application
的代碼
自定義Application
需要將項目中的Applicaton
配置為繼承TinkerApplication
的類(這個Application
需要配置到manifest
文件中):
public class SampleApplication extends TinkerApplication {
public SampleApplication() {
super(ShareConstants.TINKER_ENABLE_ALL, "xxx.xxx.SampleApplicationLike",
"com.tencent.tinker.loader.TinkerLoader", false);
}
}
注意:這個類集成TinkerApplication類,這里面不做任何操作,所有Application的代碼都會放到ApplicationLike繼承類當中
參數解析
參數1:tinkerFlags 表示Tinker支持的類型 dex only、library only or all suuport,default: TINKER_ENABLE_ALL
參數2:delegateClassName Application代理類 這里填寫你自定義的ApplicationLike
參數3:loaderClassName Tinker的加載器,使用默認即可
參數4:tinkerLoadVerifyFlag 加載dex或者lib是否驗證md5,默認為false
自定義ApplicationLike
這里就需要用到之前在Bugly平臺申請的appId
public class SampleApplicationLike extends DefaultApplicationLike {
public static final String TAG = "Tinker.SampleApplicationLike";
public SampleApplicationLike(Application application, int tinkerFlags,
boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime,
long applicationStartMillisTime, Intent tinkerResultIntent) {
super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
}
@Override
public void onCreate() {
super.onCreate();
// 這里實現SDK初始化,appId替換成你的在Bugly平臺申請的appId
// 調試時,將第三個參數改為true
Bugly.init(getApplication(), "265bca5587", false);
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
@Override
public void onBaseContextAttached(Context base) {
super.onBaseContextAttached(base);
// you must install multiDex whatever tinker is installed!
MultiDex.install(base);
// 安裝tinker
// TinkerManager.installTinker(this); 替換成下面Bugly提供的方法
Beta.installTinker(this);
}
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public void registerActivityLifecycleCallback(Application.ActivityLifecycleCallbacks callbacks) {
getApplication().registerActivityLifecycleCallbacks(callbacks);
}
}
注意:tinker需要你開啟MultiDex,你需要在dependencies中進行配置
compile "com.android.support:multidex:1.0.1"
才可以使用MultiDex.install方法; SampleApplicationLike這個類是Application的代理類,以前所有在Application的實現必須要全部拷貝到這里,在onCreate
方法調用SDK的初始化方法,在onBaseContextAttached
中調用Beta.installTinker(this);
。
AndroidManifest.xml配置
在AndroidMainfest.xml中進行以下配置:
1. 權限配置
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
2. Activity配置
<activity
android:name="com.tencent.bugly.beta.ui.BetaActivity"
android:configChanges="keyboardHidden|orientation|screenSize|locale"
android:theme="@android:style/Theme.Translucent" />
3. 配置FileProvider
注意:如果您想兼容Android N或者以上的設備,必須要在AndroidManifest.xml
文件中配置FileProvider
來訪問共享路徑的文件
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
如果你使用的第三方庫也配置了同樣的FileProvider
, 可以通過繼承FileProvider
類來解決合并沖突的問題,示例如下:
<provider
android:name=".utils.BuglyFileProvider"
android:authorities="${applicationId}.fileProvider"
android:exported="false"
android:grantUriPermissions="true"
tools:replace="name,authorities,exported,grantUriPermissions">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"
tools:replace="name,resource"/>
</provider>
這里要注意一下,FileProvider
類是在androidx.core.content
包中的,檢查你的工程是否引入該類庫
混淆配置
為了避免混淆SDK,在Proguard混淆文件中增加以下配置:
-dontwarn com.tencent.bugly.**
-keep public class com.tencent.bugly.**{*;}
# tinker混淆規則
-dontwarn com.tencent.tinker.**
-keep class com.tencent.tinker.** { *; }
APK 簽名
如果是自己寫demo測試Tinker的使用,不要忘記APK是需要簽名的,如果對APK簽名不是很了解的同學可以看我的這邊文章手把手教你APK簽名
Bugly后臺
做完上面的步驟后,Bugly+Tinker的集成已經完成,APK完成后就需要打包上線,Bugly提供了全套的打包上線流程以及強大的后臺能力支持,具體可以參考
https://bugly.qq.com/docs/user-guide/instruction-manual-android-hotfix-demo/