Android動態加載Apk-插件化技術(動態代理方案)html
一.概述java
爲何要使用插件化?在開發中,一個項目只會越作越大。初始版本多是單一功能,後續可能加上各類風馬牛不相及的功能。因此我認爲插件化可使得業務分離的更完全,一人負責哪幾個模塊,問題也能快速定位。可是也會帶來問題:插件和插件之間的交互的複雜性更高、底層支持庫由於多個插件須要使用相同的代碼可能會變得很大。因此插件化看似解耦了程序員的職責,實際上對於代碼質量的要求更高。android
要想實現插件化,最快的方法就是找一個第三方框架接入。可是要想真正理解,須要真正本身寫一個.下面本文就帶你們寫一個動態加載插件化的框架程序員
二. 什麼是插件化微信
1. 主App(宿主App)加載插件apk的實現app
2. 每一個業務組件模塊造成一個獨立的Apk, 而後經過主App動態加載部署業務組件模塊Apk的一種方案
框架
三.效果演示圖&應用場景ide
1.效果演示圖:性能
2.實際開發中,好比微信和支付寶的以下頁面就是典型的插件化應用場景優化
三.插件化的優勢好處
1. 業務組件解耦,可以實現業務組件模塊的熱插拔
2. 更改產品迭代模式,可分爲主App和次Apk(動態加載業務組件模塊)
3. 改善產品更新過程,能夠在不影響用戶的狀況下實現業務組件模塊更新以及重要Bug修復
4. 減輕主App的內存和CPU佔用,提升應用的性能.
四.插件化的思想
動態加載Apk的主要思想是:主App是被系統(PMS)安裝,被系統(AMS)調用,整個過程都是由系統提供的,而插件Apk並不是一個真正的Apk,只是一個打包成Apk的一個組件模塊,由於它並不是被系統安裝調用.簡言之,須要講插件Apk當作一個」非Apk」文件,只是一個結構比較複雜的壓縮打包成Apk格式的文件.調用插件即用某種特殊技術手段打開文件並執行其相關代碼.
五.插件化的步驟-分析主App
1.主APp打包完成解壓後,會有dex,images,xml,asset等類型文件
2.Dex靠PathClassLoader加載運行
3.圖片以及xml等資源依靠Resources&AssetManager加載管理
六.插件化的實現流程
六. 插件化的代碼實現步驟
1.建立DexClassLoader加載插件化Apk相關代碼,核心代碼以下:
/**' * 建立DexClassLoader */ private DexClassLoader createDexClassLoader(String apkPath) { File file = mContext.getDir("dex",Context.MODE_PRIVATE); return new DexClassLoader(apkPath,file.getAbsolutePath(),null,mContext.getClassLoader()); }
2.建立Resources&AssetManager來加載插件化Apk的資源
/** * 獲取到插件中的Resource */ private Resources createResources(AssetManager am) { Resources resources = mContext.getResources(); return new Resources(am,resources.getDisplayMetrics(),resources.getConfiguration()); } /** * 獲取插件的AssetManager */ private AssetManager createAssetManager(String apkPath) { try { AssetManager am = AssetManager.class.newInstance(); Method method = AssetManager.class.getDeclaredMethod("addAssetPath",String.class); method.invoke(am,apkPath); return am; } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } return null; }
3.管理插件Apk裏的組件(如Activity)的生命週期
package com.czm.pluginlib; import android.app.Activity; import android.content.Intent; import android.os.Bundle; /** * Created by caizhiming on 2018/3/3. */ public interface IPlugin { int FROM_INTERNAL = 0;//內部跳轉 int FROM_EXTERNAL = 1;//外部跳轉 void attach(Activity activity); void onCreate(Bundle bundle); void onStart(); void onRestart(); void onActivityResult(int requestCode, int resultCode, Intent data); void onResume(); void onPause(); void onStop(); void onDestroy(); }
4.經過代理模式實現對插件Apk裏面組件的管理
@Override protected void onCreate( Bundle savedInstanceState) { super.onCreate(savedInstanceState); mClassName = getIntent().getStringExtra("className"); mPluginApk = XCPluginManager.getInstance().getPluginApk(); launchPluginActivity(); } private void launchPluginActivity() { if(mPluginApk == null){ throw new RuntimeException("請先加載插件Apk"); } try { //clazz 就是Activity的實例對象,可是該對象沒有生命週期,沒有上下文環境 Class<?> clazz = mPluginApk.mDexClassLoader.loadClass(mClassName); Object object = clazz.newInstance(); if(object instanceof IPlugin) { mIPlugin = (IPlugin) object; mIPlugin.attach(this); Bundle bundle = new Bundle(); bundle.putInt("FROM",IPlugin.FROM_EXTERNAL); mIPlugin.onCreate(bundle); } } catch (Exception e) { e.printStackTrace(); } } @Override public Resources getResources() { if(mPluginApk != null) { return mPluginApk.mResources; } else { return super.getResources(); } } @Override public AssetManager getAssets() { if(mPluginApk != null) { return mPluginApk.mAssetManager; }else { return super.getAssets(); } } @Override public ClassLoader getClassLoader() { if(mPluginApk != null) { return mPluginApk.mDexClassLoader; }else { return super.getClassLoader(); } }
以上就是實現插件化的主要過程步驟,具體細節優化讀者能夠本身擴展優化補充.
七.項目代碼目錄結構圖