編輯:關於Android編程
轉載請注明出處,本文來自【 Mr.Simple的博客 】。
我正在參加博客之星,點擊這裡投我一票吧,謝謝~最近這一兩年,Android App使用插件化技術開發的數量越來越大,其實還是業務地快速膨脹導致,需求越來越多,App越來越臃腫。雖然手機的內存空間不斷地的增大,但是太大的安裝包給用戶也造成了心理壓力。於是大家都會想到插件化的開發方式,把App做成一個平台,而不是一個獨立的app。平台上可以集成各種各樣的功能,功能模塊也插件的形式添加進來,這些插件不需要安裝,只需要用戶按需下載到某個位置,然後使用的時候動態加載進來。
想法都是好的,但是想實現一個相對比較穩定的動態加載框架還是有點難度的,一些大公司都有成熟的動態加載框架,但是他們並沒有開源出來。好在2014年知名CSDN博主任玉剛開源了一款名為DL的Android動態加載框架,該框架簡單、開源、兼容性都較為好,如果有需要使用插件化開發的朋友可以嘗試該框架,另外對該開源項目感興趣的朋友也可以貢獻自己的代碼,地址會在文章最後給出。
對於DL的基本情況和使用,本人就不再贅述,作者本人和參與開發的朋友已經有一些較好的文章,詳情請參考,APK動態加載框架(DL)解析、DL動態加載框架技術文檔、Android 使用動態加載框架DL進行插件化開發。在這裡我就簡單介紹一下DL的基本結構與原理,希望能夠一些需要的朋友提供一些有用的信息。
對於Android的動態加載框架來說最重要和最麻煩的點可能基本上有兩個,第一是apk如何在不安裝的情況下運行起來,並且Activity基本的聲明周期能夠正常調用;第二就是Activity內部能夠以R的形式訪問資源文件。關於加載未安裝的apk和Android的資源加載機制請參考如下兩篇文章,Android動態加載jar、apk的實現、Android源碼分析-資源加載機制。我們針對這兩個問題依次給出DL的解決方案。
加載未安裝apk相對比較簡單,就是通過DexClassLoader將apk文件加載到虛擬機中,具體可以參考上文給出的文章。這裡我們主要說一下調用Activity生命周期函數的實現。在DL中,有兩個Proxy類,分別為DLProxyActivity、DLProxyFragmentActivity,這兩個類型分別繼承自Activity和FragmentActivity,他們分別代理集成自DLBasePluginActivity和DLBasePluginFragmentActivity的插件Activity類,DLBasePluginActivity和DLBasePluginFragmentActivity又分別繼承自Activity和FragmentActivity,我去!這個時候是不是有點亂了?且聽我慢慢道來~
按照DL的開發規范,你插件的Activity需要繼承自DLBasePluginActivity或者DLBasePluginFragmentActivity,這兩個類中封裝了一些基本的調用邏輯。這裡我們先暫時不用過多理會,重點是看DLProxyActivity、DLProxyFragmentActivity。DL的機制是這樣的,真正在DL通過Intent啟動的Activity只能是DLProxyActivity、DLProxyFragmentActivity,這兩個Activity是在宿主apk中注冊了的,因此能夠直接通過Intent來啟動。而在兩個Proxy實際上只是一個軀殼,他們會自己的生命周期函數中調用插件Activity對應的生命周期函數。這是就引入了一個關鍵的類,DLProxyImpl,這個類負責解析插件apk的資源、ClassLoader、通過反射加載插件Activity,DLProxyImpl加載了插件Activity之後又會調用Proxy Activity的attach方法將插件Activity實例傳遞給Proxy Activity,這樣Proxy Activity就得到了插件Activity的實例,然後就能在自己的聲明周期函數中調用插件Activity對應的函數。
DLProxyImpl的launchTargetActivity函數:
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) protected void launchTargetActivity() { try { // mClass就是目標插件Activity的類名 Class> localClass = getClassLoader().loadClass(mClass); Constructor> localConstructor = localClass.getConstructor(new Class[] {}); // 通過反射構建插件Activity Object instance = localConstructor.newInstance(new Object[] {}); mPluginActivity = (DLPlugin) instance; // 將插件Activity注入給Proxy Activity ((DLAttachable) mProxyActivity).attach(mPluginActivity, mPluginManager); // 插件activity也獲取到Proxy的引用 mPluginActivity.attach(mProxyActivity, mPluginPackage); Bundle bundle = new Bundle(); bundle.putInt(DLConstants.FROM, DLConstants.FROM_EXTERNAL); // 調用插件Activity的onCreate函數,啟動插件Activity mPluginActivity.onCreate(bundle); } catch (Exception e) { e.printStackTrace(); } }
DLProxyActivity核心代碼:
public class DLProxyActivity extends Activity implements DLAttachable { // 插件Activity protected DLPlugin mRemoteActivity; // DLProxyImpl加載插件Activity和資源等 private DLProxyImpl impl = new DLProxyImpl(this); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Proxy onCreate的時候調用DLProxyImpl的onCreate,加載插件Activity impl.onCreate(getIntent()); } // 加載完插件Activity後將插件Activity傳遞給Proxy Activity @Override public void attach(DLPlugin remoteActivity, DLPluginManager pluginManager) { mRemoteActivity = remoteActivity; } // 獲取資源,DLProxyImpl加載了插件apk的資源,通過這裡代理資源操作,這樣插件Activity內部就可以通過R訪問資源文件了 @Override public Resources getResources() { return impl.getResources() == null ? super.getResources() : impl.getResources(); } /*********************** 以下都是代理插件Activity的生命周期函數 ***********************/ @Override protected void onStart() { mRemoteActivity.onStart(); super.onStart(); } @Override protected void onRestart() { mRemoteActivity.onRestart(); super.onRestart(); } @Override protected void onResume() { mRemoteActivity.onResume(); super.onResume(); } @Override protected void onPause() { mRemoteActivity.onPause(); super.onPause(); } @Override protected void onStop() { mRemoteActivity.onStop(); super.onStop(); } @Override protected void onDestroy() { mRemoteActivity.onDestroy(); super.onDestroy(); } // 代碼省略 }
private DexClassLoader createDexClassLoader(String dexPath) { File dexOutputDir = mContext.getDir("dex", Context.MODE_PRIVATE); dexOutputPath = dexOutputDir.getAbsolutePath(); // 創建ClassLoader DexClassLoader loader = new DexClassLoader(dexPath, dexOutputPath, mNativeLibDir, mContext.getClassLoader()); return loader; } // 根據插件apk的路徑構建AssetManager,將其資源所在的路徑添加到AssetManager中,然後通過AssetManager構建一個Resources對象,這個對象就是插件apk的資源對象,插件apk內部訪問資源時都是通過這個資源對象 private AssetManager createAssetManager(String dexPath) { try { AssetManager assetManager = AssetManager.class.newInstance(); Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class); addAssetPath.invoke(assetManager, dexPath); return assetManager; } catch (Exception e) { e.printStackTrace(); return null; } } private Resources createResources(AssetManager assetManager) { Resources superRes = mContext.getResources(); Resources resources = new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration()); return resources; }
最後我們通過一張圖來看DL的基本結構。
DLPluginManager加載、管理插件包,DLIntent是插件之間跳轉的信息載體。Base Plugin Activity是插件Activity的基類,封裝代理操作。Proxy Activity代理插件Activity的生命周期函數,也是插件Activity的外殼。DLProxy負責加載插件Activity以及資源操作。這麼一來,整個動態加載框架就運行起來了,更多的細節有興趣的朋友自己去看源碼吧。
1. 支持service、靜態廣播、ContentProvider
1. 插件透明主題的支持
2. 完整activity api的重寫
最後給出DL框架github地址,希望更多的人參與到DL框架的開發中來。
我正在參加博客之星,點擊這裡投我一票吧,謝謝~
從今天起傻蛋打算做一個系列文章,對最新的Android4.0系統中的Launcher,也就是Android4.0原生的桌面程序,進行一個深入淺出的分析,從而引領Andro
這幾天還是在做那個項目 有一個部分是需要有一個類似微信朋友圈那樣的功能 開始自己實現是用RecycleView嵌套RecycleView 然後已經把別的弄好了 動態圖片那
上文我們地完成了『啟動沒有在AndroidManifest.xml中顯式聲明的Activity』的任務;通過HookAMS和攔截ActivityThread中H類對於組件
ListView是每個app中都要使用的,所以今天我來總結下ListView的使用和一些簡單的優化。先看下運行效果:一、創建數據庫為了模擬數據,這裡將數據保存數據庫中,順