一步步手動實現熱修復(一)-dex檔案的生成與載入
*本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家釋出
熱修復技術自從QQ空間團隊搞出來之後便漸漸趨於成熟。
我們這個系列主要介紹如何一步步手動實現基本的熱修復功能,無需使用第三方框架。
在開始學習之前,需要對基本的熱修復技術有些瞭解,以下文章可以幫助到你:
本節課程主要分為3塊:
dex檔案的生成與載入
我們在這部分主要做的流程有:
- 1.編寫基本的Java檔案並編譯為.class檔案。
- 2.將.class檔案轉為.dex檔案。
- 3.將轉好的dex檔案放入建立好的Android工程內並在啟動時將其寫入本地。
- 4.載入解壓後的.dex檔案中的類,並呼叫其方法進行測試。
Note: 在閱讀本節之前最好先了解一下類載入器的雙親委派原則、DexClassLoader的使用以及反射的知識點。
編寫基本的Java檔案並編譯為.class檔案
首先我們在一個工程目錄下開始建立並編寫我們的Java檔案,你可能會選擇各種IDE來做這件事,但我在這裡勸你不要這麼做,因為有坑在等你。等把基本流程搞清楚可以再選擇更進階的方法。這裡我們可以選擇文字編輯器比如EditPlus來對Java檔案進行編輯。
新建一個Java檔案,並命名為:ClassStudent.java,並在java檔案內鍵入以下程式碼:
public class ClassStudent {
private String name;
public ClassStudent() {
}
public void setName(String name) {
this.name = name;
}
public String getName(){
return this.name + ".Mr";
}
}
Note: 這裡要注意,不要對類新增包名,因為在後期對class檔案處理時會遇到問題,具體問題會稍後說明。上面的getName方法在返回時對this.name屬性添加了一段字串,這裡請注意,後面會用到。
在檔案建立好之後,對Java檔案進行編譯:
將.class檔案轉為.dex檔案
好,現在我們使用class檔案生成對應的dex檔案。生成dex檔案所需要的工具為dx,dx工具位於sdk的build-tools資料夾內,如下圖所示:
Tips: 為了方便使用,建議將dx的路徑新增到環境變數中。如果對dx工具不熟悉的,可以在終端中輸入dx –help以獲取幫助。
dx工具的基本用法是:
dx --dex [--output=<file>] [<file>.class | <file>.{zip,jar,apk} | <directory>]
Tips: 剛開始自己摸索的時候,就沒有仔細看命令,導致後面兩個引數的順序顛倒了,搞出了一些讓人疑惑難解的問題,最後又不得不去找dx工具的原始碼除錯,最後才發現自己的問題在哪。如果有對dx工具感興趣的,可以對dx的包進行反編譯或者獲取dx的相關原始碼進行了解。dx.lib檔案位於dx.bat的下級目錄lib資料夾中,可以使用JD-GUI工具對其進行檢視或匯出。如果需要獲取原始碼的,請使用以下命令進行克隆:
我們使用以下命令生成dex檔案:
dx --dex --output=user.dex ClassStudent.class
這裡我為了防止出錯,提前在當前目錄下新建好了user.dex檔案。上述命令依賴編譯.class檔案的JDK版本,如果使用的是JDK8編譯的class會提示以下問題:
PARSE ERROR:
unsupported class file version 52.0
...while parsing ClassStudent.class
1 error; aborting
這裡的52.0意味著class檔案不被支援,需要使用JDK8以下的版本進行編譯,但是dx所需的環境還是需要為JDK8的,這裡我編譯class檔案使用的是JDK7,請注意。
上面我們提到了為什麼先不要在ClassStudent中使用包名,因為在執行dx的時候會報以下異常,這是因為以下第二項條件沒有通過,該程式碼位於com.android.dx.cf.direct.DirectClassFile檔案內:
String thisClassName = thisClass.getClassType().getClassName();
if(!(filePath.endsWith(".class") && filePath.startsWith(thisClassName) && (filePath.length()==(thisClassName.length()+6)))){
throw new ParseException("class name (" + thisClassName + ") does not match path (" + filePath + ")");
}
執行截圖如下所示:
好了,到此為止我們的目錄應該如下:
寫入dex到本地磁碟
接下來將生成好的user.dex檔案放入Android工程的res\raw資料夾下:
在系統啟動時將其寫入到磁碟,這裡不再貼出具體的寫入程式碼,專案的MainActivity中包含了此部分程式碼。
載入dex中的類並測試
在寫入完畢之後使用DexClassLoader對其進行載入。DexClassLoader的構造方法需要4個引數,這裡對這4個引數進行簡要說明:
- String dexPath:dex檔案的絕對路徑。在這裡我將其放入了應用的cache資料夾下。
- String optimizedDirectory:優化後的dex檔案存放路徑。DexClassLoader在構造完畢之後會對原有的dex檔案優化並生成一個新的dex檔案,在這裡我選擇的是…/cache/optimizedDirectory/目錄。此外,API文件對該目錄有嚴格的說明:Do not cache optimized classes on external storage.出於安全考慮,請不要將優化後的dex檔案放入外部儲存器中。
- String libraryPath:dex檔案所需要的庫檔案路徑。這裡沒有依賴,使用空字串代替。
- ClassLoader parent:雙親委派原則中提到的父類載入器。這裡我們使用預設的載入器,通過getClassLoader()方法獲得。
在解釋完畢DexClassLoader的構造引數之後,我們開始對剛剛的dex檔案進行載入:
DexClassLoader dexClassLoader = new DexClassLoader(apkPath, file.getParent() + "/optimizedDirectory/", "", classLoader);
接來下開始load我們剛剛寫入在dex檔案中的ClassStudent類:
Class<?> aClass = dexClassLoader.loadClass("ClassStudent");
然後我們對其進行初始化,並呼叫相關的get/set方法對其進行驗證,在這裡我傳給ClassStudent物件一個字串,然後呼叫它的get方法獲取在方法內合併後的字串:
Object instance = aClass.newInstance();
Method method = aClass.getMethod("setName", String.class);
method.invoke(instance, "Sahadev");
Method getNameMethod = aClass.getMethod("getName");
Object invoke = getNameMethod.invoke(instance););
最後我們實現的程式碼可能是這樣的:
/**
* 載入指定路徑的dex
*
* @param apkPath
*/
private void loadClass(String apkPath) {
ClassLoader classLoader = getClassLoader();
File file = new File(apkPath);
try {
DexClassLoader dexClassLoader = new DexClassLoader(apkPath, file.getParent() + "/optimizedDirectory/", "", classLoader);
Class<?> aClass = dexClassLoader.loadClass("ClassStudent");
mLog.i(TAG, "ClassStudent = " + aClass);
Object instance = aClass.newInstance();
Method method = aClass.getMethod("setName", String.class);
method.invoke(instance, "Sahadev");
Method getNameMethod = aClass.getMethod("getName");
Object invoke = getNameMethod.invoke(instance);
mLog.i(TAG, "invoke result = " + invoke);
} catch (Exception e) {
e.printStackTrace();
}
}
最後附上我們的執行截圖:
如果在實現過程中遇到問題的,請在下方留言。
相關推薦
一步步手動實現熱修復(一)-dex檔案的生成與載入
*本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家釋出 熱修復技術自從QQ空間團隊搞出來之後便漸漸趨於成熟。 我們這個系列主要介紹如何一步步手動實現基本的熱修復功能,無需使用第三方框架。 在開始學習之前,需要對基本的熱修復技術有些瞭解,以下
一步步動手實現高併發的Reactor模型 —— Kafka底層如何充分利用多執行緒優勢去處理網路I/O與業務分發
一、從《Apeche Kafka原始碼剖析》上搬來的概念和圖 Kafka網路採用的是Reactor模式,是一種基於事件驅動的模式。熟悉Java程式設計的讀者應該瞭解Java NIO提供了Reactor模式的API。常見的單執行緒Java NIO程式設計模式如圖所示。 熟悉NIO程式設計都應該知道這個Sele
Rxjava2.x 原始碼分析,以及手動實現Rxjava(一)
這兩年Rxjava火的一塌糊塗,不會點Rxjava+Okhttp+Retrofit+MVP+Dagger2架構都不好意思說自己混Android的。Rxjava 到底是什麼和Rxjava到底怎麼用,這裡就不講了,網上太多了,具體可以參考 這位大佬 和扔物線的。 Rxjava
React Native用CodePush實現熱更新(一)
本文介紹用微軟預設的CodePush Cloud和將code-push-server放在本地伺服器上,以local作為storageType實現熱更新。1. 安裝 CodePush CLI:npm in
一步步動手實現簡單的執行緒池 —— 生動有趣解析 Java 執行緒池原始碼
零、引子 某天小奈與小夥伴肥宅埋的日常技(cai)術(ji)研(hu)討(zhuo)中聊起了執行緒池。 自詡十分熟悉併發程式設計
【SSH之旅】一步步學習Hibernate框架(一):關於持久化
stc localhost 對象 schema hbm.xml java let pass [] 在不引用不論什麽框架下,我們會通過平庸的代碼不停的對數據庫進行操作,產生了非常多冗余的可是又有規律的底層代碼,這樣頻繁的操作數據庫和大量的底層代碼的反復
Android 手動實現熱更新
前言 在上篇Android ClassLoader淺析中我們分析了安卓ClassLoader和熱更新的原理,這篇我們在上篇熱更新分析的基礎上寫個簡單的demo實踐一下。 概述 我們先回顧下熱更新的原理 PathClassLoader是安卓中預設的類載入器,載入類是通過fi
一步步完善rootfs:2.建立配置檔案
建立配置檔案 核心啟動到最後啟動的第一個使用者程序是init程序,它根據檔案系統下的配置檔案決定啟動哪些程式,init程序是後續所有程序的發起者。 /etc 目錄用於存放系統中的配置檔案,基本上所有的配置檔案都可以在這裡找到,這些檔案一般都以XXX.conf的形式命名,通過編輯這些檔案可
iOS實現熱修復的幾種方案
最近,在調研熱修復技術,也稱作熱更新技術。由於蘋果稽核週期有時候比較長,這是公司無法忍受的,所以熱修復技術應運而生。經過查閱多方面的資料,進行如下總結,希望對大家有所幫助。現在比較流行的熱修復技術:一、使用JSPatch進行熱修復。 JSPatch能做到通過JS呼叫
spring boot 實現熱部署,部署java檔案和靜態資源
自己學習了spring boot發現很方便使用,加上熱部署功能,不需要改個樣式就重啟服務,浪費時間了.修改完檔案之後,spring boot 自動給你更新資源,很方便開發人員除錯. 接下來讓我們一步步來實現這個功能. 首先我們需要在gradle 裡面新增依賴 runt
200行Go代碼實現自己的區塊鏈——區塊生成與網絡通信
type hash lazy avi present lte () cti 裏的 在第一篇文章[1]中,我們向大家展示了如何通過精煉的Go代碼實現一個簡單的區塊鏈。如何計算每個塊的 Hash 值,如何驗證塊數據,如何讓塊鏈接起來等等,但是所有這些都是跑在一個節點上的。文章
C++中的位移操作以實現檔案的壓縮(實現哈夫曼對檔案壓縮與解壓時做的一個小測試)
因為以前基本上沒用過位移操作,所以這裡做了一個小測試,加深了一下對位移的理解 相關概念: 因為C++中對檔案的操作常用的就是按位元組來進行讀取。下面對檔案的讀寫進行舉例(這是我常用的方式,大家也可以用其它方法讀取): 首先包含相關標頭檔案:
一步一步教你實現阿里巴巴的Sophix熱修復(一)
1.0 整合準備 gradle遠端倉庫依賴, 開啟專案找到app的build.gradle檔案,新增如下配置: 新增maven倉庫地址: repositories { maven { url "http://maven.ali
android:使用small一步步實現外掛化與熱更新
由於外掛化開發與熱更新最近貌似越來越火,新開的專案準備也使用外掛化進行開發!其中遇到不少坑,在這裡寫了一個小的例子,記錄一下開發流程,有助於自己,同時希望能夠幫助大家理解,並且對於自身專案接入外掛化有所幫助! 外掛化 效果: 外掛化開發的含義:
一步步實現redis+sentinel雙機熱備
前言 前些天一直在忙線上環境部署的事情,初步想的是,nginx(keepalive雙機熱備)+3(tomcat)+2redis(雙機熱備),但是後來由於阿里雲伺服器經典網路不提供虛擬IP,無法使用keepalive,nginx雙機熱備只能暫時先放棄,退而求其次,採用ngin
一步步深入學習webpack(入門困惑express和dev-server區別及分別使用dev-server和webpack-hot-middleware實現的熱載入區別)
最近需要對webpack詳細學習後,給大家分享學習。於是不得不對每一個點進行學習,結果發現webpack涉及到的知識內容好多,自己學習也是一知半解,很多時候腦細胞都死得一片一片的。 注:本文是參考網上多方資料學習後記錄的,如有雷同,請聯絡我。 學習資料:入門
一步步實現 Redis 搜索引擎
行集 準備 exp sta 發的 ast 註意 自己 內容 來源:jasonGeng88 github.com/jasonGeng88/blog/blob/master/201706/redis-search.md 如有好文章投稿,請點擊 → 這裏了解詳情 場景 大家如
WPF一步步實現完全無邊框自定義Window(附源碼)
nbsp interop -c 思路 pan cit 最終 auto pre 在我們設計一個軟件的時候,有很多時候我們需要按照美工的設計來重新設計整個版面,這當然包括主窗體,因為WPF為我們提供了強大的模板的特性,這就為我們自定義各種空間提供了可能性,這篇博客主要用來
Android熱修復 Dex注入實現靜默消滅bug
當app上線後發現緊急bug,如果重新發布版本週期比較長,並且對使用者體驗不好,此時熱修復就派上用場了。熱修復就是為緊急bug而生,能夠快速修復bug,並且使用者無感知。針對熱修復,阿里系先後推出AndFix、HotFix、SophFix,騰訊系也推出QQ空間超級補丁
一步步實現windows版ijkplayer系列文章之五——使用automake生成makefile
一步步實現windows版ijkplayer系列文章之一——Windows10平臺編譯ffmpeg 4.0.2,生成ffplay 一步步實現windows版ijkplayer系列文章之二——Ijkplayer播放器原始碼分析之音視訊輸出——視訊篇 一步步實現windows版ijkplayer系列文章之三——I