Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android熱修復——Tinker微信解決方案

Android熱修復——Tinker微信解決方案

編輯:關於Android編程

Android的熱修復

前言:

隨著時代的發展,由於公司的項目需要去求變化平凡計劃總趕不上變化,H5的高靈活性,開發周期短,更新速度快H5以及一些混合開發越來越被看好,然而主要原因之一:這種混合開發的方式容錯率大,更新和修復BUG快.不用發布版本就可以讓用戶不覺的情況下就更新對應的內容或者BUG,我們不能否認混合開發的快捷,正在此前提下熱修復和熱更新技術也得到了非常大的發展,不管熱修復還是熱更新,都是對app的內容或者邏輯變化做出像web頁面更新一樣的體驗.而本文只對熱修復進行探索,不對H5進行深入研究.而今天的主人公的話是微信Tinker.

不久前微信開源了Tinker,github的star數量直飚5000+,我的天,還在等什麼,學習學習.

什麼是熱修復


熱修復補丁(hotfix),又稱為patch,指能夠修復軟件漏洞的一些代碼,是一種快速、低成本修復產品軟件版本缺陷的方式。
前言中描述的”不用發布版本就可以讓用戶不覺的情況下就更新對應的內容或者BUG”可能不算准確,所以我自行百度了一下.
熱修復說白了就是”打補丁”,比如你們公司上線一個app,用戶反應有重大bug,需要緊急修復。如果按照通
常做法,那就是程序猿加班搞定bug,然後測試,重新打包並發布。這樣帶來的問題就是成本高,效率低。於是,熱
修復就應運而生.一般通過事先設定的接口從網上下載無Bug的代碼來替換有Bug的代碼。這樣就省事多了,用
戶體驗也好.

原理

類似與插件開發,關於插件開發原理,看這篇Android插件原理剖析,其中介紹了一下Java中的類加載器和Android中的類加載器. 熱修復就是利用android中的 DexClassLoader 類加載器,動態加載補丁dex,替換有bug的類

已有的熱修復解決方案:

  • https://github.com/dodola/HotFix
  • https://github.com/jasonross/Nuwa
  • https://github.com/bunnyblue/DroidFix

微信Tinker

Tinker的github地址:https://github.com/Tencent/tinke

Tinker原理:微信Android熱補丁實踐演進之路

官方給出的定義:
Tinker is a hot-fix solution library for Android, it supports dex, library and resources update without reinstalling apk.
Tinker是微信官方的Android熱補丁解決方案,它支持動態下發代碼、So庫以及資源,讓應用能夠在不需要重新安裝的情況下實現更新。當然,你也可以使用Tinker來更新你的插件。

這裡原理以及好處.在這裡就BB了,我們開發人員只需要關心怎麼使用,實現就可以了.不過這裡還是貼出來給大家學習..那麼接下來直接實踐作為一個資深的開發人員學習一個新的技術,第一想到就是去官網看看文檔跑跑Demo,當然我也不例外(資深).

導入Sample工程

  • tinkerd地址,下載下來解壓打開導入Android Studio,我們只需要把tinker-sample-android這個目錄導入即可.
  • 導入之後,構建一下,想都不用想肯定出錯,提示“tinkerId is not set!!!”,WTF????然後我們肯定會去看他的接入指南,前面一大堆BBBB…
看到了Sample的使用方法內心激動起來以為可以知道了什麼原因了,再次WTF???沒有直接就是運行的後的說明,不能忍,於是我又去網上找找,算是找到了解決的辦法,但是後面才知道這些問題微信維護開源人員被問了煩了,直接列出了常見問題,我都不知道說什麼了…….
問題解決:這是因為沒有正確的配置IDE的git路徑, 若不是通過clone方式下載tinker,需要本地手動commit一次。這裡你也可以使用其他字符作為tinkerId;
\

我這裡的話直接就把當前的版本號作為id..

補充:關於獲取Git提交版本號?

1git rev-parse –short HEAD
這段代碼主要是用來顯示最近一次提交到HEAD上的記錄編號(類似於“b03b0c4”的字符串,每次提交,字符串都不一樣。個人對git命令行了解不多,如果有知道的大神麻煩指教一下)。
所以前面說的,除了環境變量要配置git(可以在命令行輸入 git –version ,顯示出了版本號,便是配置成功),還要把你的項目與git關聯起來,並且保證有一次提交記錄,才能獲取到該字符串。

具體使用可以看我的另一篇文章:關於git命令“git rev-parse –short HEAD”在android studio中使用與配置的個人探究

個人覺得,加入這段代碼,顯得更麻煩了,還不如直接寫死,或者獲取其他的版本號。

編譯運行原版apk

接下使用assembleDebug命令,再拿到下圖中的app-debug-xxxxx.apk裝在手機上運行

\
<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPjxjb2RlPrvy1d/Wsb3T1MvQ0Ciyu7n90qrPyLnYsdVJbnN0YW50IFJ1bikgLSZndDtmaWxlLSZndDtzZXR0aW5nLSZndDtCdWlsZC5FJmhlbGxpcDsuLSZndDtJbnN0YW50IFJ1biC12tK7uPbIpbX0vs2/ydLU1MvQ0MHLPC9jb2RlPjwvY29kZT48L2NvZGU+PC9jb2RlPjwvY29kZT48L3A+DQo8aDEgaWQ9"配置原版apk路徑">配置原版apk路徑

\
這裡的oldapkpath是上圖編譯運行原版apk中得到的apk路徑和R.txt路徑配置下就ok

if (buildWithTinker()) {
apply plugin: 'com.tencent.tinker.patch'

tinkerPatch {
     * the old apk path, use to diff with the new apk to build
     * add apk from the build/bakApk
     */
    oldApk = "${bakPath}/app-debug-1108-13-43-27.apk"
    ignoreWarning = false
    useSign = true
    buildConfig {

        applyMapping = getApplyMappingPath()

        applyResourceMapping = getApplyResourceMappingPath()

        tinkerId = getTinkerIdValue()
    }

這裡的oldapk也要修改成上面編譯運行原版apk生成apk的路徑

修改源碼 生成新版apk 補丁

  • 運行起來之後,打開代碼MianActvity,修改代碼,打開Log.e(TAG, “i am on onCreate string:” + getResources().getString(R.string.test_resource))的注釋

  • 再運行下面圖中的tinkerPatchDebug,或者在Terminal使用gradlew tinkerPatchDebug ,Terminal->就是

Android studio 一般左下角的那個cmd控制台一樣的東西
\

這樣在app/build/outputs/tinkerPatch/debug/patch_signed_7zip.apk路徑下找到這個差異包,也就是我們俗稱的補丁.

推送補丁

然後把patch_signed_7zip.apk放到手機SD卡中去使用命令
adb push ./app/build/outputs/tinkerPatch/debug/patch_signed_7zip.apk /storage/sdcard0/
這裡放置的路徑與apk中獲取補丁位置一致

運行應用,加載補丁

再次運行apk,點擊LoadPatch時調用TinkerInstaller.onReceiveUpgradePatch(getApplicationContext(), Environment.getExternalStorageDirectory().getAbsolutePath() + "/patch_signed_7zip.apk");方法,加載補丁.
查看控制台日志,打印出i am on onCreate string:I am in the base apk就表示成功了


補充:返回鍵退出後進入,並沒有執行修復。

(當時以為是我手機的原因,就沒太在意),現在有朋友評論說自己也加載成功但沒法修復,是不是跟我一樣按得返回鍵退出。

殺進程後再進入 ,應該就可以修復成功了,如果不成功,把補丁包逆向一下,看看自己修復的部分有沒有在裡面。


集成到自己的項目中

1. 添加gradle依賴

在項目的build.gradle中,添加tinker-patch-gradle-plugin的依賴

buildscript {
    dependencies {
        classpath ('com.tencent.tinker:tinker-patch-gradle-plugin:1.7.3')
    }
}
然後在app的gradle文件app/build.gradle,我們需要添加tinker的庫依賴以及apply tinker的gradle插件.

dependencies {
    //可選,用於生成application類 
    provided('com.tencent.tinker:tinker-android-anno:1.7.3')
    //tinker的核心庫
    compile('com.tencent.tinker:tinker-android-lib:1.7.3') 
}
...
...
//apply tinker插件
apply plugin: 'com.tencent.tinker.patch'

2. 添加生成補丁方法

tinkerPatch {
//有問題的apk的地址,就是要修復BUG的那個apk,這是在電腦上位置
oldApk = "D://1//app-debug-old.apk"
ignoreWarning = false
useSign = true
buildConfig {
    tinkerId = "1.0"
}
packageConfig {
    //寫這個為了修復一個bug,詳見github issue #22
    configField("TINKER_ID", "1.0")
}
dex {
    dexMode = "jar"
    pattern = ["classes*.dex", "assets/secondary-dex-?.jar"]
    loader = ["com.tencent.tinker.loader.*", "com.kairu.rxjava.app.MyApplicationLike"]
}
lib {
    pattern = ["lib/armeabi/*.so", "lib/arm64-v8a/*.so", "lib/armeabi-v7a/*.so", "lib/mips/*.so", "lib/mips64/*.so", "lib/x86/*.so", "lib/x86_64/*.so"]
}
res {
    pattern = ["res/*", "assets/*", "resources.arsc", "AndroidManifest.xml"]
    largeModSize = 100
}
sevenZip {
    zipArtifact = "com.tencent.mm:SevenZip:1.1.10"
}

}

3. 配置Application

程序啟動時會加載默認的Application類,這導致我們補丁包是無法對它做修改了。如何規避?在這裡我們並沒有使用類似InstantRun hook Application的方式,而是通過代碼框架的方式來避免,這也是為了盡量少的去反射,提升框架的兼容性。

這裡我們要實現的是完全將原來的Application類隔離起來,即其他任何類都不能再引用我們自己的Application。我們需要做的其實是以下幾個工作:

將我們自己Application類以及它的繼承類的所有代碼拷貝到自己的ApplicationLike繼承類中,例如SampleApplicationLike。你也可以直接將自己的Application改為繼承ApplicationLike;
Application的attachBaseContext方法實現要單獨移動到onBaseContextAttached中;
對ApplicationLike中,引用application的地方改成getApplication();
對其他引用Application或者它的靜態對象與方法的地方,改成引用ApplicationLike的靜態對象與方法;
更詳細的事例,大家可以參考下面的一些例子以及SampleApplicationLike的做法。

這是我的例子:也可以參考https://github.com/Tencent/tinker/wiki/Tinker-%E8%87%AA%E5%AE%9A%E4%B9%89%E6%89%A9%E5%B1%95

@DefaultLifeCycle(
        application = "com.kairu.rxjava.app.MyApplication",//這的Application是以前項目中的MyApplication
        flags = ShareConstants.TINKER_ENABLE_ALL
)
public class MyApplicationLike extends DefaultApplicationLike {

    private static Application mApplication;
    public static String currentGirl = "http://ww2.sinaimg.cn/large/610dc034jw1f5k1k4azguj20u00u0421.jpg";

    public MyApplicationLike(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent, Resources[] resources, ClassLoader[] classLoader, AssetManager[] assetManager) {
        super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent, resources, classLoader, assetManager);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //這裡把所有的Application換成getApplication() 原因看https://github.com/Tencent/tinker/wiki/Tinker-%E8%87%AA%E5%AE%9A%E4%B9%89%E6%89%A9%E5%B1%95
        mApplication = getApplication();

        //配置是否顯示log
        LogUtil.isDebug = true;

        //配置時候顯示toast
        ToastUtils.isShow = true;

        //配置程序異常退出處理
    }
    @Override
    public void onBaseContextAttached(Context base) {
        super.onBaseContextAttached(base);
        TinkerInstaller.install(this);
        //在初始化的時候調用加載補丁的方法,路徑是實際補丁放的位置
        TinkerInstaller.onReceiveUpgradePatch(this.getApplication(), Environment.getExternalStorageDirectory().getAbsolutePath()+"/patch_signed_7zip.apk");

    }
    public static Context getContext() {
        return mApplication;
    }


    public static Application getIntstance() {
        return mApplication;
    }
}

是不是簡單暴力就完了?當然配置就搞定了,沒有那麼復雜…..

最後我們都配置好了那怎麼得到補丁包呢?

也是一樣

步驟1:編譯運行原版apk

把生成的apk放在自己定義的路徑下

tinkerPatch {
    ...
//有問題的apk的地址,就是要修復BUG的那個apk,這是在電腦上位置
oldApk = "D://1//app-debug-old.apk"
    ...
    }

步驟2:修改源碼 生成新版apk 補丁

這裡修改源碼指的是實際項目中修復BUG更改的代碼…

後續的步驟都一樣就搞定了…………

Tinker的局限

如果出現以下的情況,並且ignoreWarning為false,我們將中斷編譯。因為這些情況可能會導致編譯出來的patch包帶來風險:

1. minSdkVersion小於14,但是dexMode的值為”raw”;
2. 新編譯的安裝包出現新增的四大組件(Activity, BroadcastReceiver…);
3. 定義在dex.loader用於加載補丁的類不在main dex中;
4. 定義在dex.loader用於加載補丁的類出現修改;
5. resources.arsc改變,但沒有使用applyResourceMapping編譯。

還有就是需要結束當前進程才能進行修復....
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved