編輯:關於Android編程
類加載器(class loader)是 Java?中的一個很重要的概念。類加載器負責加載 Java 類的字節代碼到 Java 虛擬機中。
Java 虛擬機使用 Java 類的方式如下:Java 源程序(.java 文件)在經過 Java 編譯器編譯之後就被轉換成 Java 字節代碼(.class 文件)。類加載器負責讀取 Java 字節代碼,並轉換成java.lang.Class類的一個實例。每個這樣的實例用來表示一個 Java 類。通過此實例的 newInstance()方法就可以創建出該類的一個對象。實際的情況可能更加復雜,比如 Java 字節代碼可能是通過工具動態生成的,也可能是通過網絡下載的。
與JVM不同,Dalvik的虛擬機不能用ClassCload直接加載.dex,Android從ClassLoader派生出了兩個類:DexClassLoader和PathClassLoader;而這兩個類就是我們加載dex文件的關鍵,這兩者的區別是:
1.DexClassLoader:可以加載jar/apk/dex,可以從SD卡中加載未安裝的apk;
2.PathClassLoader:要傳入系統中apk的存放Path,所以只能加載已經安裝的apk文件。
package wangyang.zun.com.mydexdemo.dynamic; /** * Created by WangYang on 2016/3/11. */ public interface Dynamic { String sayHello(); }接著我們新建一個impl包,並實現Dynamic接口,DynamicImpl.java:
package wangyang.zun.com.mydexdemo.dynamic.impl; import wangyang.zun.com.mydexdemo.dynamic.Dynamic; /** * Created by WangYang on 2016/3/11. */ public class DynamicImpl implements Dynamic { @Override public String sayHello() { return new StringBuilder(getClass().getName()).append(" is loaded by DexClassLoader").toString(); } }很簡單輸出一句話,"DynamicImpl is loaded by DexClassLoader." 具體的工程目錄如下圖: 點擊Build -> make project,這時候會在build\intermediates\classes\debug目錄下生成對應的classes文件。 好了我們要把DynamicImpl這個class轉換成Dalvik可識別的dex文件,分兩步: 1.先導出DynamicImpl這個類為jar包的形式; 2.通過android sdk自帶的dx.jar工具轉換jar包為dex文件。 完成第一步,當時遇到點麻煩,由於eclipse是基於ant並且有可視化工具,可以直接導出指定文件的jar包,但是android studio不行,那怎麼辦呢? 我們可以通過gradle task來打包,打開app目錄下的build.gradle文件,切記不是根目錄的build.gradle文件,加上以下代碼:
//刪除dynamic.jar包任務 task clearJar(type: Delete) { delete 'libs/dynamic.jar' } //打包任務 task makeJar(type:org.gradle.api.tasks.bundling.Jar) { //指定生成的jar名 baseName 'dynamic' //從哪裡打包class文件 from('build/intermediates/classes/debug/wangyang/zun/com/mydexdemo/dynamic/') //打包到jar後的目錄結構 into('wangyang/zun/com/mydexdemo/dynamic/') //去掉不需要打包的目錄和文件 exclude('test/', 'Dynamic.class', 'BuildConfig.class', 'R.class') //去掉R$開頭的文件 exclude{ it.name.startsWith('R$');} } makeJar.dependsOn(clearJar, build)
public class FileUtils { public static void copyFiles(Context context, String fileName, File desFile) { InputStream in = null; OutputStream out = null; try { in = context.getApplicationContext().getAssets().open(fileName); out = new FileOutputStream(desFile.getAbsolutePath()); byte[] bytes = new byte[1024]; int i; while ((i = in.read(bytes)) != -1) out.write(bytes, 0 , i); } catch (IOException e) { e.printStackTrace(); }finally { try { if (in != null) in.close(); if (out != null) out.close(); } catch (IOException e) { e.printStackTrace(); } } } public static boolean hasExternalStorage() { return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); } /** * 獲取緩存路徑 * * @param context * @return 返回緩存文件路徑 */ public static File getCacheDir(Context context) { File cache; if (hasExternalStorage()) { cache = context.getExternalCacheDir(); } else { cache = context.getCacheDir(); } if (!cache.exists()) cache.mkdirs(); return cache; } }打開MainActivity:
public class MainActivity extends AppCompatActivity { private Dynamic dynamic; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //添加一個點擊事件 findViewById(R.id.tx).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { loadDexClass(); } }); } /** * 加載dex文件中的class,並調用其中的sayHello方法 */ private void loadDexClass() { File cacheFile = FileUtils.getCacheDir(getApplicationContext()); String internalPath = cacheFile.getAbsolutePath() + File.separator + "dynamic_dex.jar"; File desFile = new File(internalPath); try { if (!desFile.exists()) { desFile.createNewFile(); FileUtils.copyFiles(this, "dynamic_dex.jar", desFile); } } catch (IOException e) { e.printStackTrace(); } //下面開始加載dex class DexClassLoader dexClassLoader = new DexClassLoader(internalPath, cacheFile.getAbsolutePath(), null, getClassLoader()); try { Class libClazz = dexClassLoader.loadClass("wangyang.zun.com.mydexdemo.dynamic.impl.IDynamic"); dynamic = (Dynamic) libClazz.newInstance(); if (dynamic != null) Toast.makeText(this, dynamic.sayHelloy(), Toast.LENGTH_LONG).show(); } catch (Exception e) { e.printStackTrace(); } } }程序運行的效果圖如下: 至此,我們關於Android Dex動態加載機制的原理講到這裡,接下來我會分析下通過Dex實現熱修復的基本原理。
應用項目需要要屏蔽HOME鍵。項目本身的要求是讓按下HOME鍵後程序不做任何響應,就像按下返回鍵一樣在onBackPressed 方法中直接return啥都
0. 介紹此文將對Github上lguipeng大神所開發的 極簡筆記 v2.0 (點我下載源碼)代碼進行分析學習。通過此文你將學到:應用源碼的研讀方法 MVP架構模式
DPI:每英寸像素數簡單的屏幕分辨率計算方法:DisplayMetrics metrics = new DisplayMetrics();Display display
支持margin,gravity以及水平,垂直排列最近在學習android的view部分,於是動手實現了一個類似ViewPager的可上下或者左右拖動的ViewGroup