編輯:關於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框架的開發中來。
我正在參加博客之星,點擊這裡投我一票吧,謝謝~
Android 4.0 Launcher源碼分析系列(一)
從今天起傻蛋打算做一個系列文章,對最新的Android4.0系統中的Launcher,也就是Android4.0原生的桌面程序,進行一個深入淺出的分析,從而引領Andro
Androi:ListView+GridView實現仿微信微博朋友圈無焦點沖突
這幾天還是在做那個項目 有一個部分是需要有一個類似微信朋友圈那樣的功能 開始自己實現是用RecycleView嵌套RecycleView 然後已經把別的弄好了 動態圖片那
Android 插件化原理解析——插件加載機制
上文我們地完成了『啟動沒有在AndroidManifest.xml中顯式聲明的Activity』的任務;通過HookAMS和攔截ActivityThread中H類對於組件
【Android UI】ListView的使用和簡單優化
ListView是每個app中都要使用的,所以今天我來總結下ListView的使用和一些簡單的優化。先看下運行效果:一、創建數據庫為了模擬數據,這裡將數據保存數據庫中,順