編輯:關於Android編程
項目快速迭代過程中,不可避免的出現BUG,Android線上出現問題,通常需要發版解決。緊急發版,用戶不一定升級,強制升級又不友好,有什麼更好的解決方案呢?這就用到了熱修復技術。
hotfix,後來發展成為RocooFix,
GitHub地址:https://github.com/dodola/HotFix
原理詳細介紹官方文章:安卓App熱補丁動態修復技術介紹
HotFix存在的問題:這種方法無法在已經加載好的類中實現動態替換,只能在類加載之前替換掉。就是說,補丁下載下來後,只能等待用戶重啟應用才能完成補丁效果。
RocooFix支持兩種模式:
靜態修復某種情況下需要重啟應用。
動態修復,無需重啟應用即可生效。
該技術的原理很簡單,其實就是用ClassLoader加載機制,覆蓋掉有問題的方法。所以我們的補丁其實就是有問題的類打成的一個包。
例子中的出現問題的類是dodola.hotfix.BugClass
原始代碼如下:
public class BugClass { public String bug() { return "bug class"; } }
我們假設BugClass
類裡的bug()
方法出現錯誤,需要修復,修復代碼如下:
public class BugClass { public String bug() { return "fixed class"; } }
那麼我們只需要將修復過的類編譯後打包成dex即可
步驟如下:
將補丁類提取出來到一個文件夾裡
將class文件打入一個jar包中jar cvf path.jar *
dx --dex --output=path_dex.jar path.jar
這樣就生成了補丁包path_dex.jar
實現這一部分功能的原因主要是因為出現如下異常
java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation
問題原因在文檔中已經描述的比較清楚。
就是如果以上方法中直接引用到的類(第一層級關系,不會進行遞歸搜索)和clazz都在同一個dex中的話,那麼這個類就會被打上CLASS_ISPREVERIFIED
很明顯,解決的方法就是在類中引用一個其他dex中的類,但是源碼方式的引用會將引用的類打入同一個dex中,所以我們需要找到一種既能編譯通過並且將兩個互相引用的類分離到不同的dex中,於是就有了這個動態的代碼植入方式。
首先我們需要制作引用類的dex包,代碼在hackdex
中,我直接使用了文檔中的類名AntilazyLoad
這樣可以和文章中對應起來,方便一些。
我們將這個庫打包成dex的jar包,方法跟制作補丁一樣。
下面是重點,我們要用javassist
將這個類在編譯打包的過程中插入到目標類中。
為了方便,我將這個過程做成了一個Gradle的Task,代碼在buildSrc
中。
這個項目是使用Groovy開發的,需要配置Groovy SDK才可以編譯成功。
核心代碼如下:
/** * 植入代碼 * @param buildDir 是項目的build class目錄,就是我們需要注入的class所在地 * @param lib 這個是hackdex的目錄,就是AntilazyLoad類的class文件所在地 */ public static void process(String buildDir, String lib) { println(lib) ClassPool classes = ClassPool.getDefault() classes.appendClassPath(buildDir) classes.appendClassPath(lib) //下面的操作比較容易理解,在將需要關聯的類的構造方法中插入引用代碼 CtClass c = classes.getCtClass("dodola.hotfix.BugClass") println("====添加構造方法====") def constructor = c.getConstructors()[0]; constructor.insertBefore("System.out.println(dodola.hackdex.AntilazyLoad.class);") c.writeFile(buildDir) CtClass c1 = classes.getCtClass("dodola.hotfix.LoadBugClass") println("====添加構造方法====") def constructor1 = c1.getConstructors()[0]; constructor1.insertBefore("System.out.println(dodola.hackdex.AntilazyLoad.class);") c1.writeFile(buildDir) growl("ClassDumper", "${c.frozen}") }
下面在代碼編譯完成,打包之前,執行植入代碼的task就可以了。
在 app 項目的 build.gradle 中插入如下代碼
task('processWithJavassist') << { String classPath = file('build/intermediates/classes/debug')//項目編譯class所在目錄 dodola.patch.PatchClass.process(classPath, project(':hackdex').buildDir .absolutePath + '/intermediates/classes/debug')//第二個參數是hackdex的class所在目錄 } android{ ....... applicationVariants.all { variant -> variant.dex.dependsOn << processWithJavassist //在執行dx命令之前將代碼打入到class中 } }
反編譯編譯後的apk可以發現,代碼已經植入進去,而且包裡並不存在dodola.hackdex.AntilazyLoad
這個類
GitHub地址:https://github.com/alibaba/AndFix
初始化
patchManager = new PatchManager(context); patchManager.init(appversion);//current version加載patch
patchManager.loadPatch();添加patch文件
patchManager.addPatch(path);支持熱更新,不需要重新啟動
GitHub地址:https://github.com/alibaba/dexposed
Nuwa的具體實現也是根據QQ空間的熱修復方案來實現的
GitHub地址:https://github.com/jasonross/Nuwa
GitHub地址:https://github.com/bunnyblue/DroidFix
官方介紹:http://bunnyblue.github.io/DroidFix/
DroidFix的實現原理跟QQ空間的熱補丁方案類似。
最近一直在做即時通訊,當然少不了發圖片了, 既然要發圖片,我連忙打開qq,看看qq發圖片是個什麼效果,看起來確實不錯,我就照著qq仿寫了一個,其中選擇圖片時,圖片的右上角
最近在搗鼓android 自定義控件屬性,學到了TypedArray以及attrs。在這其中看了一篇大神博客Android 深入理解Android中的自定義屬性。我就更加
1.打開手機,點擊程序列表中的“設置”按鈕 2.然後在設置界面中,切換至“全部設置”選項卡,點擊&ldqu
最近在學Android 學的不好 然後看到了用.9.png寫對話框的哪裡,但是書上寫的太簡單 感覺做出來和書上的不一樣 然後就去各種百度 感覺網上關於這個東西的資料都是粘