Tinker的整合和多渠道打包
Tinker是什麼
Tinker是微信官方的Android熱補丁解決方案,它支援動態下發程式碼、So庫以及資源,讓應用能夠在不需要重新安裝的情況下實現更新。當然,你也可以使用Tinker來更新你的外掛。
它主要包括以下幾個部分:
1.gradle編譯外掛: tinker-patch-gradle-plugin
2.核心sdk庫: tinker-android-lib
3.非gradle編譯使用者的命令列版本: tinker-patch-cli.jar
為什麼使用Tinker`
當前市面的熱補丁方案有很多,其中比較出名的有阿里的AndFix、美團的Robust以及QZone的超級補丁方案。但它們都存在無法解決的問題,這也是正是我們推出Tinker的原因。
總的來說:
AndFix作為native解決方案,首先面臨的是穩定性與相容性問題,更重要的是它無法實現類替換,它是需要大量額外的開發成本的;
Robust相容性與成功率較高,但是它與AndFix一樣,無法新增變數與類只能用做的bugFix方案;
Qzone方案可以做到釋出產品功能,但是它主要問題是插樁帶來Dalvik的效能問題,以及為了解決Art下記憶體地址問題而導致補丁包急速增大的。
特別是在Android N之後,由於混合編譯的inline策略修改,對於市面上的各種方案都不太容易解決。而Tinker熱補丁方案不僅支援類、So以及資源的替換,它還是2.X-7.X的全平臺支援。利用Tinker我們不僅可以用做bugfix,甚至可以替代功能的釋出。Tinker已執行在微信的數億Android裝置上,那麼為什麼你不使用Tinker呢?
Tinker的已知問題
由於原理與系統限制,Tinker有以下已知問題:
1.Tinker不支援修改AndroidManifest.xml,Tinker不支援新增四大元件;
2.由於Google Play的開發者條款限制,不建議在GP渠道動態更新程式碼;
3.在Android N上,補丁對應用啟動時間有輕微的影響;
4.不支援部分三星android-21機型,載入補丁時會主動丟擲 "TinkerRuntimeException:checkDexInstall failed";
5.由於各個廠商的加固實現並不一致,在1.7.6以及之後的版本,tinker不再支援加固的動態更新;
6.對於資源替換,不支援修改remoteView。例如transition動畫,notification icon以及桌面圖示。
Tinker的demo
1.先去Github上下載Tinker原始碼,裡面有tinker-sample-Android.
下好demo後,可能大家會迫不及待(其實就是我)跑起來,然後就回報這個錯Error:Execution failed for task ':app:tinkerProcessDebugManifest'.
> tinkerId is not set!!!
wtf!然後趕緊百度,去查詢wiki,哦哦,原來問題出在這裡
tinker的官方文件這麼寫,當時我是懵圈的。
tinkerId is not set:這是因為沒有正確的配置IDE的git路徑, 若不是通過clone方式下載tinker,需要本地手動commit一次。這裡你也可以使用其他字元作為tinkerId;
不過簡單,網上肯定給出了答案:
String gitRec =”自己定義一個字串”,不過建議這樣寫:(用你專案的versionName,也可以配置git路徑)
好了,這個問題搞定了,那我們再來跑一次,現在是見證奇蹟的時。。。wtf,什麼鬼啊。。。我要不要面子的啊。
Error:A problem occurred configuringproject ':app'.
> Failed to notify project evaluationlistener.
> Tinker does not support instant run mode, please trigger build byassembleTestDebug or disable instant run in 'File->Settings...'.
> Task with name 'tinkerPatchTestRelease' not found in project':app'.
奔潰中。。
這個錯誤是說tinket不支援install run 模式 ,請手動build assembleDebug 或者把 install run 模式禁用掉。
哇,終於成功了。
2驗證熱修復功能
我在MainActivity的佈局中加了一個Button:
<Button
android:id="@+id/btn_cool"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/killSelf"
android:text="我很帥吧?"/>
findViewById(R.id.btn_cool).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Toast.makeText(view.getContext(), "不存在的", Toast.LENGTH_LONG).show(); } });
點選按鈕,彈出toast。嗯,很明顯這個是很嚴重的bug(純娛樂,這裡只是為了做演示)
假設這個是已經發布的版本,使用者體驗非常差,老闆要你在不更新app下,修復這個bug,那改怎麼辦?來用tinker搞定
首先,我們需要生存一個基礎包(也就是old apk)
在Android Studio右側Gradle的Gradle projects下點選assembieDebug,等待編譯debug包(如圖)
編包成功後,會在app/build/bakApk檔案下生成基礎包和R檔案(如圖)
app-debug-0324-10-50-29.apk就相當於當前使用者使用的APK(old apk),後面生成patch apk都是基於old apk和new apk的不同(diff)生成的。
app-debug-0324-10-50-29-R.txt當前APK的R檔案,也是用於生成patch apk的
然後需要在app的gradle下配置基礎包和R檔案(如圖),把我們需要的tinkerOldApkPath設定成上面介紹的app-debug-0324-10-50-29.apk,tinkerApplyResourcePath設定成app-debug-0324-10-50-29-R.txt
而tinkerApplyMappingPath指的是mapping 檔案,也就是你專案的混淆檔案。如果你專案開了混淆,那就把生存的mapping檔案配置上,這樣不僅能減少你補丁包的大小,也能加快你補丁包打到主程式的速度
tinkerBuildFlavorDirectory指的是多渠道打包,這裡先不做解釋,下面有詳細說明。這兩個地方可以先忽略,不去配置。
到這裡,基本的配置就介紹完了,現在我們需要去修復我們的bug,把toast中的文字改成“嗯,帥炸了!”(程式碼就不貼了)
要想熱修復成功,首先要有patch apk,這個patch apk是根據old apk和new apk通過diff演算法得出的,老版本(old apk)有bug,然後我們我們通過程式碼把這個bug修復了(new apk),通過Tinker diff演算法就得出了patch apk,然後oldapk(也就是使用者正在使用的版本)從遠端伺服器下載這個patch apk然後通過Tinker的onReceiveUpgradePatch方法實現熱修復,如下所示:
TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), Environment.getExternalStorageDirectory().getAbsolutePath() + "/patch_signed_7zip.apk"); } });
既然知道修復的方式,那就來生成下patch包吧,如下圖所示,雙擊tinkerPatchDebug,等待編譯成功。
build成功後會在app/outpus/tinkerPatch/debug/下生成patch_signed_7zip.apk檔案,如圖所示:
然後我們只要把patch_signed_7zip.apk檔案放到手機的根目錄下,然後點選按鈕LOAD PATCH,不出意外的話,過一會兒就會彈Toast(patchsuccess,please restart process
),然後殺死程序,重新進入app,點選Button(我很帥吧?),彈出Toast:
到這裡,通過Tinker實現了熱修復功能
Tinker的接入
gradle接入
gradle是推薦的接入方式,在gradle外掛tinker-patch-gradle-plugin
中我們幫你完成proguard、multiDex以及Manifest處理等工作。
新增gradle依賴
在專案的build.gradle中,新增tinker-patch-gradle-plugin
的依賴
buildscript {
dependencies {
classpath ('com.tencent.tinker:tinker-patch-gradle-plugin:1.7.7')
}
}
然後在app的gradle檔案app/build.gradle,我們需要新增tinker的庫依賴以及apply tinker的gradle外掛.
dependencies {
//可選,用於生成application類
provided('com.tencent.tinker:tinker-android-anno:1.7.7')
//tinker的核心庫
compile('com.tencent.tinker:tinker-android-lib:1.7.7')
}
...
...
//apply tinker外掛
apply plugin: 'com.tencent.tinker.patch'
Pactch生成
Patch生成有兩種方式:
*基於命令列的方式。
*gradle編譯的方式。
1.gradle編譯生成patch
微信Tinker的gradle配置可以參照demo,具體引數請先瀏覽Tinker接入指南,點選檢視,同時這裡附上一份相對完整的gradle配置。
Api引入
自定義Application類
程式啟動時會載入預設的Application類,這導致我們補丁包是無法對它做修改了。如何規避?在這裡我們並沒有使用類似InstantRun hook Application的方式,而是通過程式碼框架的方式來避免,這也是為了儘量少的去反射,提升框架的相容性。
這裡我們要實現的是完全將原來的Application類隔離起來,即其他任何類都不能再引用我們自己的Application。我們需要做的其實是以下幾個工作:
將我們自己Application類以及它的繼承類的所有程式碼拷貝到自己的ApplicationLike繼承類中,例如SampleApplicationLike。你也可以直接將自己的Application改為繼承ApplicationLike;
Application的attachBaseContext方法實現要單獨移動到onBaseContextAttached中;
對ApplicationLike中,引用application的地方改成getApplication();
對其他引用Application或者它的靜態物件與方法的地方,改成引用ApplicationLike的靜態物件與方法;
這個是官方文件說的。具體的做法可以參照如下幾步:
第一步:
可以把demo中一些需要的類拷貝到自己的工程中,放在一個資料夾中如圖:
第二步:修改SampleApplicationLike中生成Application的名字或者包名。
build後會在build/generated/souce/apt/檔案下生成你定義的Application,如圖:
第三步:如果我們專案中已經存在了自己定義的Application了,該怎麼辦呢?
直接讓你自己定義的Application繼承自Tinker生成的O2oInApplication即可,如果找不到這個類,請rebuild下。
第四步:
我們需要在AndroidManifest.xml清單檔案中配置Tinker生成的Application(O2oInApplication)
同時需要配置Tinker Service:
寫完後如果報紅,此時只需要Build下即可解決報紅。Application配置就到此結束。接下來生成patch檔案。因為patch檔案是寫入到SDCrad的,所以我們需要在AndroidManifest中新增如下許可權(注:6.0及已上系統請動態設定許可權或者手動在設定中為專案設定):
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
具體生成patch步驟和demo中演示是一樣的,這裡不重複演示。
多渠道打包
官方文件也對這個進行了說明:如下
關於渠道包的問題,若使用flavor編譯渠道包,會導致不同的渠道包由於BuildConfig變化導致classes.dex差異。這裡建議的方式有:
將渠道資訊寫在AndroidManifest.xml或檔案中,例如channel.ini;
將渠道資訊寫在apk檔案的zip comment中,這種是建議方式,例如可以使用項目或者可使用V2 Scheme的walle;
若不同渠道存在功能上的差異,建議將差異部分放於單獨的dex或採用相同代碼不同配置方式實現;
事實上,tinker也支援多flavor直接編譯多個補丁包,具體可參考。
這裡主要演示tinker支援的多flavor直接編譯多個補丁包
通過flavor編譯,這個時候我們可以看到bakApk路徑是一個按照flavor名稱區分的目錄;
我這裡只是做了一個渠道,大家可以測試多個。
將編譯目錄路徑填寫到gradle中tinkerBuildFlavorDirectory
,其他的幾個欄位不需要填寫,這裡會自動根據路徑拼接;
3.執行tinkerPatchAllFlavorDebug
或者tinkerPatchAllFlavorRelease
即可得到所有flavor的補丁包。
最後會生成多了補丁包,根據你渠道名稱新建資料夾,放在其中
到這裡多渠道打包就完成了。雖然簡單,但是大家可以瞭解下多渠道打包的過程,以及我打包中遇到的問題
我在打多渠道補丁包的時候出現了這樣的一個問題
導致這個錯誤的我在release中重新定義了apk打包後的名稱:
所以在bakApk和outputs/apk下apk的名稱會變成我定義的包命TosO2O_v2.5.0.apk
在多渠道編包的時候會執行下面的程式碼
這裡會判斷是否存在多渠道,然後根據不同渠道的名稱在bakApk下輸出不同的渠道的R檔案和apk包,其中apk的名稱會根據outputs檔案下apk的名稱進行替換。所以我們可以在bakApk下看到Tos020_v2.5.0.apk的。重點來了,既然替換了,為什麼還是會報錯呢?好了,讓我們看下面的程式碼
執行tinkerPatchAllFlavorDebug
或者tinkerPatchAllFlavorRelease
時候查詢檔案其實是走了這兩段程式碼。其中紅框中的程式碼就是那到你要編譯補丁包的old-Apk,R檔案,mapping檔案。但是這裡的這行程式碼:
project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/${project.name}-${flavorName}-release.apk"
規定了old-Apk的名稱和路徑,和我們自己定義的apk名稱不一樣,所以報了
的錯誤。到這裡我們瞭解了多渠道編包的流程,也就找到了問題所在,只要我們把上面那行程式碼修改成
project.tinkerPatch.oldApk = "${originOldPath}/${flavorName}/TosO2O_v2.5.0.apk"
其實也就是報apk名稱改成我們自己的。就ok。
重新編譯,問題解決。
這裡主要闡述tinker的整合和編譯,下篇文章將對tinker的原始碼進行淺析
相關推薦
Tinker的整合和多渠道打包
Tinker是什麼 Tinker是微信官方的Android熱補丁解決方案,它支援動態下發程式碼、So庫以及資源,讓應用能夠在不需要重新安裝的情況下實現更新。當然,你也可以使用Tinker來更新你的外掛。 它主要包括以下幾個部分: 1.gradle編譯外掛: tinke
Tinker整合,多渠道打包
整合見官網 注意事項: 1.修改自定義application 有3個地方 ,換成自己的包名即可 2.修改打補丁apk,R,mapping檔案,和生成補丁檔案的位置 3.生成補丁,開啟Terminal ,執行命令 ./gradlew tinkerP
app打包、混淆、加固和多渠道打包
1:打包步驟: 1:桌面建立一個資料夾,名字叫keystore 2:點選build下面的 ,如下: 3:會出現如下介面: 4:下一步: 5:如果有keystore,請點選 choose existing,選擇以前的keystore,
騰訊樂固的加固和多渠道打包客戶端配置
1、概述 近日專案新版本上線,各大市場都正常,但是騰訊的應用寶稽核提示需要用樂固加固,因為之前一直用360加固並進行多渠道打包,但是提示如果不用樂固可能會稽核不通過,不給曝光率啥的,所以單
Android studio 多環境打包和多渠道打包
1.多環境打包 在android開發中經常遇到要打不同環境的包,所謂不同環境就是介面連線的伺服器域名不同,如開發環境為devIP,測試環境為buildIP,uat環境為uatIP,release環境為releaseIP 。以前的做法是同過一個static變
android開發之Jenkins+Gradle實現android開發持續整合、多渠道打包
需求: 我今天在專案上加了一個功能,那麼一個好的專案開發流程必然得跑單元測試,意思是:“改一次程式碼,需要手動跑一次單元測試,來檢驗程式碼在當前情況下是否能執行成功!”,但每次都需要手動跑的話就太累了,因為一個新增專案功能,我一天下來可能會改十幾次程
借騰訊開源 VasDolly,談談 Android 簽名和多渠道打包的原理!
一、前言 Hi,大家好,我是承香墨影! 當我們需要釋出一款 App 到應用市場的時候,一般需要我們針對不同的市場生產不同的渠道包,它們使用的是同一套程式碼,只是會包含一些各自的渠道資訊,用於我們做資料分析。 前幾天,企鵝電競團隊開源了自己的 And
as中apk簽名和多渠道打包
1.什麼是簽名: 數字簽名就是為你的程式打上一種標記,來作為你自己的標識,當別人看到簽名的時候會知道它是與你相關的 2.為什麼要簽名 防止盜版,app市場可以檢測簽名未簽名的apk無法安裝,也沒法釋出到應用市場包名相同,簽名不同的apk無法升級覆蓋 3.怎麼簽名 bu
騰訊Tinker 熱修復 Andriod studio 3.0 配置和整合(二)多渠道打包和補丁釋出
騰訊Tinker 熱修復 Andriod studio 3.0 多渠道打包和釋出補丁方式推薦 本文說明 在之前我已經分享了Tinker 熱修復的 Andriod studio3.0 初次配置和整合,時隔這麼久來寫一下我對Thinker多渠道打包的理解和記錄,希望對大家有幫助。這篇文
bugly整合Tinker熱修復,多渠道打包 簽名
自己記錄一下,對其他人有幫助更好 https://mp.weixin.qq.com/s/1kRTqyF4JC5lLwpxUae9NA (感謝分享) 在這裡面使用的是博主說的第二種方式 快速打渠道包工具(Gradle) walle 然後在git上找到 walle的專案
Android美團多渠道打包Walle整合
一、為什麼使用美團多渠道打包的方式? 打包更加快速 傳統的通過productFlavors渠道包的方式,渠道10個以內還可以接受,如果100個渠道包,每個包需要打5Min,就是將近10個小時的打包,而採用美團Walle多渠道打包的方式只需要打一個包的時間。 配置更加靈活 可以在APK渠道包中通過配
openinstall:多渠道打包和統計的解決方法
現在市場上至少有十餘家主流安卓應用市場,這意味著App上架前Android工程師至少需要打十幾個包,在較少渠道的時候還能應付得過來,但在面對上千個渠道的時候,還需要再打上千個包,效率自然非常的低,甚至還有被抓包的可能,存在一定的資料誤差風險。此外,客戶端還需要把收集來的渠道碼通過介面傳送給服
Android熱更新Tinker + 多渠道打包 + 加固的流程詳解
一、Tinker熱修復 關於熱修復的作用,不用多說了,一句話概括就是通過讓使用者無感的方式來修復線上應用的bug。這裡介紹的是微信Tinker。 下面的接入方式都是參考自Tinker官方文件來。我這裡主要是把我接入的步驟(通過AndroidStudio +
Android Studio多渠道打包和程式碼混淆教程
http://coolshell.info/blog/2015/03/android-studio-prefrence.html 什麼是Gradle Gradle是一種依賴管理工具,基於Groovy語言,面向Java應用為主,它拋棄了基於XML的各種繁瑣配置,取而
[Android Studio 權威教程]多渠道打包和一鍵完成(全部產品)打包並簽名
好久沒有更新blog了,今天給大家帶來的是AS 的多渠道打包,並且我們還要驗證是否實現了多渠道打包的功能,最後在讓大家爽一下實現一鍵打包所有的渠道包並且給apk簽名 多渠道打包 方法1 第一步:新增渠道表示標籤
Android Studio使用Gradle實現自動打包,簽名,自定義apk檔名,多渠道打包,整合系統簽名證書【附效果圖附原始碼】
接觸Android Stuidio有一陣子了,之前用的時候有很多小問題,不過現在的版本感覺已經很好用了,所以準備徹底從Eclipse轉戰Android Stuidio,這段時間把以前經常使用的公用庫都從Eclipse移植過來了,今天研究了一下在Andro
熱修復框架Tinker最完整講解(02)——加入Walle多渠道打包
前言 上一篇文章 熱修復框架Tinker最完整講解(01)——整合之路 已經介紹我們的專案渠道包有20個,並且我們多渠道打包是採用productFlavors實現的。但是這種多渠道打包會造成20個渠道包的熱更新就需要20個補丁,這樣肯定是不合理的。那怎
Android實戰——Tinker的整合和使用
前言 對於熱修復我相信很多小夥伴都已經知道它們普遍的操作套路,Tinker主要是依賴自己的gradlePlugin生成拆分包,所以其拆分包的生成就由Gradle來完成,當然也可以通過命令列的方式,這裡就不對命令列做講解,Tinker接入指南 專案結構
Tinker熱修復 及walle多渠道打包流程
普通打包 1 Constants.isWalleChannel 設定成false 2 不要註釋掉 <meta-data android:name="UMENG_CHANNEL" android:value="${
Tinker整合步驟和整合中所需要的問題
整合Tinker所需要的問題: 1,複製demo中的build.gradle 修改完成以後出現 Error:(9, 0) Could not get unknown property 'TINKER_VERSION' for object of type