說到熱修復技術,我們不得不先談一下什麼是冷修復。
冷修復
當我們發現上線的應用APK存在Bug,我們通過發佈新的應用APK,去替換舊的應用APK,以達到解決Bug的目的,但是這樣做存在很大的缺點,需要用戶二次下載APK,浪費用戶流量,費時、費力、用戶體驗差。
熱修復
當我們發現上線的應用APK存在Bug,我們在用戶使用過程中就把Bug修復了,優點是其過程中用戶不需要把應用程序停止、卸載、重新安裝、重啓,大大改善了用戶體驗。
熱修復原理解析
通常作爲一款應用,最容易出現Bug的地方,是java代碼。我們知道Oracle的套路,java源文件是被編譯成.class文件,用ClassLoader加載.class;而安卓使用Dalvik/ART虛擬機,由於版權問題,谷歌把.class編譯成了dex文件,並通過ClassLoader加載dex。我們的熱修復方案,其實就是基於我之前博客中講到的Android dex多分包方案實現的。想學習瞭解Android Dex多分包技術的童鞋,請點擊鏈接查看:徹底掌握Android多分包技術MultiDex-用Ant和Gradle分別構建(一)
爲了便於大家形象具體的理解熱修復技術的流程,我給大家畫一幅原理圖。
待修復項目搭建
明白了熱修復的流程以後,爲了方便給大家演示熱修復的流程,我們首先新建一個含有Bug的項目,該項目有一個頁面,頁面中包含兩個按鈕,一個按鈕點擊後會執行錯誤未修復的代碼,另一個按鈕點擊後執行熱修復操作。
MainActivity代碼如下:
public class MainActivity extends Activity {
Button btnOpen, btnModify;
NullTest nt = new NullTest();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnOpen = (Button) findViewById(R.id.btn_open);
btnModify = (Button) findViewById(R.id.btn_modify);
btnOpen.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
nt.printAbcLength(MainActivity.this);// 執行計算
}
});
btnModify.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
castielFixMethod();// 調用熱修復方法
}
});
}
}
出錯的NullTest計算工具類:
public class NullTest {
int a = 8;
int b = 0;// 故意設置爲0
public void printAbcLength(Context context) {
// 很明用8除0,一定會導致java.lang.ArithmeticException: / by zero異常
Toast.makeText(context, "count result:" + (a/b), Toast.LENGTH_LONG).show();
}
}
佈局文件代碼:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="猴子搬來的救兵 http://blog.csdn.net/mynameishuangshuai" />
<ImageView
android:id="@+id/imageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/textView1"
android:layout_margin="10dp"
android:src="@drawable/old" />
<Button
android:id="@+id/btn_open"
android:layout_width="200dp"
android:layout_height="40dp"
android:layout_below="@+id/imageView1"
android:layout_margin="10dp"
android:text="執行操作" />
<Button
android:id="@+id/btn_modify"
android:layout_width="200dp"
android:layout_height="40dp"
android:layout_below="@+id/btn_open"
android:layout_margin="10dp"
android:text="修復Bug" />
</RelativeLayout>
OK,項目源碼開發到此爲止,接下來,我們通過使用ant命令,對該項目進行多分包構建,這次我們一共構建了兩個dex包,特地把出錯的NullTest類放到classes2.dex中去,爲的就是方便後面的熱修復。
<!-- 構建多分包dex文件 -->
<target
name="multi-dex"
depends="compile" >
<echo message="Generate multi-dex..." />
<exec
executable="${tools.dx}"
failonerror="true" >
<arg value="--dex" />
<arg value="--multi-dex" />
<arg value="--set-max-idx-number=10000" />
<arg value="--main-dex-list" />
<!-- 主包包含class文件列表 -->
<arg value="${main-dex-rule}" />
<arg value="--minimal-main-dex" />
<arg value="--output=${bin}" />
<arg value="${bin}" />
<!-- <arg value="${libs}" /> -->
</exec>
</target>
主包配置文件清單:
com/castiel/demo/MainActivity.class
完成以上所有操作後,我們將構建出來的APK安裝到手機上,然後測試,點擊執行操作按鈕,發現項目崩潰並閃退。
開發熱修復補丁dex文件
1.發現並修改Bug
public class NullTest {
int a = 8;
int b = 1;// 這裏我們將出錯的0改爲1
public void printAbcLength(Context context) {
Toast.makeText(context, "count result:" + (a/b), Toast.LENGTH_LONG).show();
}
}
2.生成補丁dex文件
修改錯誤代碼後,我們clean一下項目,在項目的bin目錄中找到生成的新的NullTest.class文件,連同該文件的包目錄一併拷貝出來(注意其他的類文件通通去掉),這裏我拷貝到桌面上的castiel文件夾中,同時在該文件夾中新建一個castieloutput文件夾,用於稍後存放編譯的dex文件。
然後在cmd命令行中,利用SDK的dx工具編譯生成新的dex文件
成功後,我們將生成的dex文件反編譯,可以看到新的修復補丁文件已經將0改爲1
到這裏本篇博客就結束了,下一篇博客我們要做的事情就是用我們 classes2.dex(修復好的包)去動態替換classes2.dex(有Bug的包),實現熱修復操作。