標籤:
概述
面對App商務邏輯的頻繁變更,如果每一次改變都對App進行一次升級,會降低App的使用者體驗,那麼App進行模組化升級(這裡與增量升級是不同的)是很好的解決方案,讓使用者在完全無感覺的情況下改變App中的商務邏輯。要實現這種模組化升級,動態載入位元組碼(jar/dex)就是實現這個需求的理論基礎。
Android系統載入位元組碼
Android的虛擬機器(Dalvik VM)無法識別普通jar包中的位元組碼,所以需要通過位元組碼轉換工具將jar轉換成dex,jar包中的所有位元組碼都會打進classes.dex,這樣的位元組碼才能被Dalvik虛擬機器識別。
執行個體講解
源碼下載,需要準備兩個工程,一個Android工程(AndroidPractice),Android工程中去載入位元組碼。寧外一個普通的java工程(DexModule),下面看看工程結構。需要注意的是IDynamicLoad這個介面在兩個工程中都需要,且包名一致。
位元組碼載入工程結構
DexModule工程講解
DexModule這個工程中只有兩個類,一個是介面IDynamicLoad,這個介面就是文章開始提到的模組升級的關鍵,只需要提前約定好介面,將介面發布給調用方,具體的實現對調用方完全是透明的,這樣就能做到隨意的更改介面的實現,寧一個是該介面的實作類別DynamicLoad。實現非常簡單就是返回一個字串。
package com.vjson.module;public interface IDynamicLoad { public String dexLoad();}
<span style="font-weight: normal;">package com.vjson.module;public class DynamicLoad implements IDynamicLoad { @Override public String dexLoad() { return "dexload practice"; }}</span>
匯出jar包
在匯出jar包的時候一定要注意,不要匯出IDynamicLoad這個介面檔案,因為Android工程中已經有一個介面檔案,不然載入位元組碼的時候會導致位元組碼衝突。
匯出jar包
位元組碼轉換
用前面提到的位元組碼轉換工具,將jar轉換為Dalvik VM認識的dex。d2j-jar2dex.sh這個命令的-o參數指定輸出檔案。
d2j-jar2dex.sh -o module.jar dynamicLoad.jar
然後將產生的module.jar放到手機的sdcard裡面。
adb push module.jar /mnt/sdcard/
載入dex
載入位元組碼需要用到DexClassLoader這個類,它負責從jar包中提取(解壓縮的一個過程)classes.dex,並且將位元組碼載入到記憶體,接下來就通過loadClass方法載入需要的類,看下面的詳細代碼,注意高亮的行。
package com.vjson.practice;import java.io.File;import android.annotation.TargetApi;import android.app.Activity;import android.os.Build;import android.os.Bundle;import android.os.Environment;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.Toast;import com.vjson.dynamicload.R;import com.vjson.module.IDynamicLoad;import dalvik.system.DexClassLoader;public class MainActivity extends Activity { private Button mBtn; private IDynamicLoad mDynamicLoad; @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) public static final IDynamicLoad loadByteCode() { File jarFile = new File(Environment.getExternalStorageDirectory().toString() + File.separator + "module.jar"); File optmizedPath = BaseApplication.sInstance.getDir("dex", MODE_PRIVATE); DexClassLoader loader = new DexClassLoader(jarFile.getAbsolutePath(), optmizedPath.getAbsolutePath(), null, BaseApplication.sInstance.getClassLoader()); IDynamicLoad dynaicLoad = null; try { Class<?> clazz = loader.loadClass("com.vjson.module.DynamicLoad"); dynaicLoad = (IDynamicLoad) clazz.newInstance(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return dynaicLoad; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mDynamicLoad = loadByteCode(); mBtn = (Button) findViewById(R.id.btn); mBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { String str = mDynamicLoad.dexLoad(); Toast.makeText(getApplicationContext(), str, Toast.LENGTH_SHORT).show(); } }); }}
注意代碼的26行,通過context的getDir(“dex”, MODE_PRIVATE)方法擷取應用程式的私人目錄,這是由於從Android4.1.2開始由於安全原因,防止代碼注入攻擊,必須將位元組碼放到私人目錄下面也就是data/data/應用程式套件組合名/。從jar包中提前出來的classes.dex就放在這個目錄下面。
總結
本文主要介紹了,Android中的位元組碼載入技術,為接下來的文章Android模組化升級提供一個理論基礎,其實最精髓的地方就是定義介面,通過介面調用端和實現端進行通訊。在模組化升級中將會講解jar包的完整性驗證和安全性驗證。
Android動態載入位元組碼