Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android動態加載框架DL的架構與基本原理解析

Android動態加載框架DL的架構與基本原理解析

編輯:關於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();
        }
    }

宿主在加載插件apk時會解析該apk的相關信息,比如它的默認啟動的Activity,上述形式將apk運行起來,那麼在插件apk中的activity跳轉則需要通過DLIntent類將目標activity的包名、類名傳遞給DL框架,DL內部會進行解析以及相應的加載邏輯。


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();
    }

    // 代碼省略

}

下面我們來看另外一個重點,也就是插件apk的資源加載。關於apk的資源加載機制也請參考上面給出的文章,我們直接看代碼。

    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;
    }

這樣,等於是插件Activity的生命周期交給了Proxy Activity代理,而插件的Activity的啟動和資源訪問則交給了DLProxyImpl代理。這樣,通過兩個代理就啟動了插件Activity,並且資源訪問問題也得到了解決。

最後我們通過一張圖來看DL的基本結構。

\

DLPluginManager加載、管理插件包,DLIntent是插件之間跳轉的信息載體。Base Plugin Activity是插件Activity的基類,封裝代理操作。Proxy Activity代理插件Activity的生命周期函數,也是插件Activity的外殼。DLProxy負責加載插件Activity以及資源操作。這麼一來,整個動態加載框架就運行起來了,更多的細節有興趣的朋友自己去看源碼吧。


後期特性

1. 支持service、靜態廣播、ContentProvider

bug

1. 插件透明主題的支持

2. 完整activity api的重寫


最後給出DL框架github地址,希望更多的人參與到DL框架的開發中來。


我正在參加博客之星,點擊這裡投我一票吧,謝謝~


  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved