編輯:關於Android編程
前幾天,微信團隊的Android熱修復框架在GitHub上開源了,6天之內,已經2700+star了,我只能說太6了
地址:https://github.com/Tencent/tinker
官方介紹:https://my.oschina.net/shwenzhang/blog/751618
接入指南:https://github.com/Tencent/tinker/wiki/Tinker-%E6%8E%A5%E5%85%A5%E6%8C%87%E5%8D%97
今天拿下來集成使用了一下,發現md上對集成使用的過程介紹的比較精簡(後來發現wiki上面倒是很詳細,需要的同學可以自己去看),這裡記錄一下我集成使用的過程。
一、集成
我這裡直接使用的是他自帶的sample,導入到studio中。
二、初始化配置
首先我們需要在app/bulid.gradle中,設置tinkerId的值,很多人開始編譯就報錯,提示“tinkerId is not set!!!”,就是因為這個值沒有設置。獲取tinkerId走的
def gitSha() { return 'git rev-parse --short HEAD'.execute().text.trim() }
---------------------------------------------------------------------------
補充:關於獲取git提交版本號
git rev-parse --short HEAD這段代碼主要是用來顯示最近一次提交到HEAD上的記錄編號(類似於“b03b0c4”的字符串,每次提交,字符串都不一樣。個人對git命令行了解不多,如果有知道的大神麻煩指教一下)。
所以前面說的,除了環境變量要配置git(可以在命令行輸入 git --version ,顯示出了版本號,便是配置成功),還要把你的項目與git關聯起來,並且保證有一次提交記錄,才能獲取到該字符串。
具體使用可以看我的另一篇文章:關於git命令“git rev-parse --short HEAD”在android studio中使用與配置的個人探究
個人覺得,加入這段代碼,顯得更麻煩了,還不如直接寫死,或者獲取其他的版本號。
---------------------------------------------------------------------------------------------------
之後,我們會看到Manifest.xml中,SampleApplication.java這個類報紅找不到。這個並不影響,因為到時候我們在編譯的時候,tinker會為我們生成SampleApplication.java這個類,而起作用的代碼就是SampleApplicationLike.java類聲明上面的
@DefaultLifeCycle(application = "tinker.sample.android.app.SampleApplication", flags = ShareConstants.TINKER_ENABLE_ALL, loadVerifyFlag = false)這段注解,我們也可以把這段注解注釋了,
自定定義一個SampleApplication類繼承 TinkerApplication類,然後加入無參構造方法,得到的類為:
/** * Created by anzyhui on 2016/9/26. */ public class SampleApplication extends TinkerApplication { public SampleApplication(){ super( //tinkerFlags, which types is supported //dex only, library only, all support ShareConstants.TINKER_ENABLE_ALL, // This is passed as a string so the shell application does not // have a binary dependency on your ApplicationLifeCycle class. "tinker.sample.android.app.SampleApplicationLike"); } }這些都是Tinker這邊定好的規矩,咱們得照著來(第二個參數中的".app"不要忘了,也就是路徑不要搞錯了),其中第一個參數表示支持修復的類型,有以下類型可以選:
public static final int TINKER_DISABLE = 0x00; public static final int TINKER_DEX_MASK = 0x01; public static final int TINKER_NATIVE_LIBRARY_MASK = 0x02; public static final int TINKER_RESOURCE_MASK = 0x04; public static final int TINKER_DEX_AND_LIBRARY = TINKER_DEX_MASK | TINKER_NATIVE_LIBRARY_MASK; public static final int TINKER_ENABLE_ALL = TINKER_DEX_MASK | TINKER_NATIVE_LIBRARY_MASK | TINKER_RESOURCE_MASK;從字面理解就知道什麼意思了。
重新編譯一下,一般也不會有問題,如果有問題的話,看看你是不是之前已經生成過了SampleApplication了,在\build\intermediates\classes\debug\tinker路徑下對應的目錄裡,有的話,刪掉再編譯試下。
三、功能測試
(1)debug版本
先嘗試debug版本
現在我們模擬打包一個出現bug的版本。
再activity_main.xml中加入倆控件:
MainActivity中設置點擊事件,點擊btnBug時,tvMessage顯示"出現bug了"文字。
打包生成apk
這時,在build/bakApk目錄中,會生成一個apk和一個R.txt文件,R.txt的作用後面再講。
apk運行至手機中,
點擊點擊btnBug按鈕,顯示:
好,現在來修復這個bug。
我們在activity_main.xml中刪除該“點擊出現bug”按鈕,增加“點擊修復bug”按鈕:
(PS:也就是修改了res文件)
當然了MainActivity中的代碼中,也修改了點擊事件,刪除了“點擊出現bug”按鈕的點擊事件,增加了“點擊修復bug”按鈕的點擊事件,觸發後,上方文本要顯示“bug已經修復了”。
代碼改好後,我們需要配置一下前面提到了R.txt文件,這裡面保存了每次編譯得到的每個res文件的id值。
具體配置就是,在app/build.gradle的ext部分,添加oldApk(也就是出現bug的那個apk)的信息:
ext { //for some reason, you may want to ignore tinkerBuild, such as instant run debug build? tinkerEnabled = true //you should bak the following files //old apk file to build patch apk tinkerOldApkPath = "${bakPath}/app-debug-0927-11-47-21.apk" //這個是你出現bug的apk完整名稱,app/build/bakApk目錄下 //proguard mapping file to build patch apk tinkerApplyMappingPath = "${bakPath}/" //暫未開啟混淆,不用管 //resource R.txt to build patch apk, must input if there is resource changed tinkerApplyResourcePath = "${bakPath}/app-debug-0927-11-47-21-R.txt" //如果你修復了res文件,需要指定你bug版本的R.txt文件 }然後,在studio的右上角,打開gradle,找到tinkerPatchDebug(我一直用的是debug簽名,這個根據自己實際情況來)這個task,點擊運行。就會在output目錄下生成一個tinkerpatch目錄,
在裡面找到patch_signed_7zip.apk和patch_signed.apk,這就是我們的差分包。
因為代碼中指定的差分包路徑為:
loadPatchButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), Environment.getExternalStorageDirectory().getAbsolutePath() + "/patch_signed_7zip.apk"); } }); 我們就直接使用patch_signed_7zip.apk,放到我們的手機根目錄,再打開app,點擊LOAD PATCH,
提示我們補丁加載成功,重啟後生效。
------------------------------
補充:返回鍵退出後進入,並沒有執行修復。
(當時以為是我手機的原因,就沒太在意),現在有朋友評論說自己也加載成功但沒法修復,是不是跟我一樣按得返回鍵退出。
殺進程後再進入 ,應該就可以修復成功了,如果不成功,把補丁包逆向一下,看看自己修復的部分有沒有在裡面。
------------------------------------------------------
重啟後,注意看界面上的按鈕是不是已經被替換,
按鈕已經替換了,這裡我已經點擊了按鈕,所以上方文本內容也提示修復成功。
(2)release版本+混淆
release版本總體使用方式與的bug版本類似,
每次打出正式包時,我們應該備份好。
在打包前,修改app/build.gradle裡的
/** * task type, you want to bak */ //def taskName = "debug" def taskName = "release"
這樣release打包時才會在bakApk下生成對應的apk,R.txt以及mapping文件。
之後的打包是一樣的。
打差分包(補丁)的時候,我們要做的,同樣是,替換裡面的屬性:
ext { //for some reason, you may want to ignore tinkerBuild, such as instant run debug build? tinkerEnabled = true //you should bak the following files //old apk file to build patch apk tinkerOldApkPath = "${bakPath}/app-release-0929-11-28-36.apk" //proguard mapping file to build patch apk tinkerApplyMappingPath = "${bakPath}/app-release-0929-11-28-36-mapping.txt" //resource R.txt to build patch apk, must input if there is resource changed tinkerApplyResourcePath = "${bakPath}/app-release-0929-11-28-36-R.txt" }開啟了混淆的話,就要設置bug版本release包的mapping文件,以保證兩次映射一致。
同樣,右側gradle,點擊tinkerPatchRelease。
我這次Gradle任務進行了好幾分鐘,當時還以為是出錯了,後來看了一下下圖,
因為我是第一次打release包,所以在下載必要的jar包。
有運行慢的可以點進這裡看看,是不是一樣的情況。
差分包打包成功後,跟之前一樣的操作,我這邊測試成功沒問題呢。
四、總結
之前研究過AndFix,主要是使用native方法,來修改出現bug的方法,雖然支持的系統版本也很廣(2.3~7.0),但是具有一定的局限性,只能修改方法的內部實現,不支持新增方法,新增、修改成員變量等;而基於ClassLoader的各種實現,由於采用在字節碼中在構造方法注入一段代碼,防止被打上CLASS_ISPREVERIFIED標記,在dalvik中比較影響性能,而且支持系統也不全面,所以都不是特別的完美。而且他們都有一個缺點,不能修改資源文件。
這次Tinker的發布,打破了原有的性能問題,功能局限,實現了,支持對library、java類、res文件的修復,並且除了小部分版本的設備(wiki上說是部分三星api19版本),其他基本都可以覆蓋到(yunOS另說)。目前來看 ,一個不足是,需要重啟後熱修復才能生效。
MSM8909+Android5.1.1之BSP開發---電池管理2---BatteryInfo.java 先來借用MTK對電池管理的框架圖 圖1&n
本文主要講解局部加權(線性)回歸。在講解局部加權線性回歸之前,先講解兩個概念:欠擬合、過擬合,由此引出局部加權線性回歸算法。 欠擬合、過擬合如下圖中三個擬合模型
錯誤類型: 04-28 06:10:15.508: E/AndroidRuntime(849): Caused by: java.lang.ClassNotFoun
Java源文件通過Java編譯器生成CLASS文件,再通過dx工具轉換為classes.dex文件。DEX文件從整體上來看是一個索引的結構,類名、方法名、字段名等信息都存