您好,登錄后才能下訂單哦!
面對App業(yè)務(wù)邏輯的頻繁變更,如果每一次改變都對App進(jìn)行一次升級,會降低App的用戶體驗(yàn),那么App進(jìn)行模塊化升級(這里與增量升級是不同的)是很好的解決方案,讓用戶在完全無感覺的情況下改變App中的業(yè)務(wù)邏輯。要實(shí)現(xiàn)這種模塊化升級,動態(tài)加載字節(jié)碼(jar/dex)就是實(shí)現(xiàn)這個需求的理論基礎(chǔ)。
Android的虛擬機(jī)(Dalvik VM)無法識別普通jar包中的字節(jié)碼,所以需要通過字節(jié)碼轉(zhuǎn)換工具將jar轉(zhuǎn)換成dex,jar包中的所有字節(jié)碼都會打進(jìn)classes.dex,這樣的字節(jié)碼才能被Dalvik虛擬機(jī)識別。
×××,需要準(zhǔn)備兩個工程,一個Android工程(AndroidPractice),Android工程中去加載字節(jié)碼。寧外一個普通的java工程(DexModule),下面看看工程結(jié)構(gòu)。需要注意的是IDynamicLoad這個接口在兩個工程中都需要,且包名一致。
DexModule這個工程中只有兩個類,一個是接口IDynamicLoad,這個接口就是文章開始提到的模塊升級的關(guān)鍵,只需要提前約定好接口,將接口發(fā)布給調(diào)用方,具體的實(shí)現(xiàn)對調(diào)用方完全是透明的,這樣就能做到隨意的更改接口的實(shí)現(xiàn),寧一個是該接口的實(shí)現(xiàn)類DynamicLoad。實(shí)現(xiàn)非常簡單就是返回一個字符串。
package com.vjson.module; public interface IDynamicLoad { public String dexLoad(); }
<span >package com.vjson.module; public class DynamicLoad implements IDynamicLoad { @Override public String dexLoad() { return "dexload practice"; } }</span>
在導(dǎo)出jar包的時候一定要注意,不要導(dǎo)出IDynamicLoad這個接口文件,因?yàn)锳ndroid工程中已經(jīng)有一個接口文件,不然加載字節(jié)碼的時候會導(dǎo)致字節(jié)碼沖突。
用前面提到的字節(jié)碼轉(zhuǎn)換工具,將jar轉(zhuǎn)換為Dalvik VM認(rèn)識的dex。d2j-jar2dex.sh這個命令的-o參數(shù)指定輸出文件。
d2j-jar2dex.sh -o module.jar dynamicLoad.jar
然后將生成的module.jar放到手機(jī)的sdcard里面。
adb push module.jar /mnt/sdcard/
加載字節(jié)碼需要用到DexClassLoader這個類,它負(fù)責(zé)從jar包中提?。ń鈮嚎s的一個過程)classes.dex,并且將字節(jié)碼加載到內(nèi)存,接下來就通過loadClass方法加載需要的類,看下面的詳細(xì)代碼,注意高亮的行。
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)方法獲取應(yīng)用程序的私有目錄,這是由于從Android4.1.2開始由于安全原因,防止代碼注入***,必須將字節(jié)碼放到私有目錄下面也就是data/data/應(yīng)用程序包名/。從jar包中提前出來的classes.dex就放在這個目錄下面。
本文主要介紹了,Android中的字節(jié)碼加載技術(shù),為接下來的文章Android模塊化升級提供一個理論基礎(chǔ),其實(shí)最精髓的地方就是定義接口,通過接口調(diào)用端和實(shí)現(xiàn)端進(jìn)行通信。在模塊化升級中將會講解jar包的完整性驗(yàn)證和安全性驗(yàn)證。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。