Android动态加载框架DL的架构与基本原理解析

本文介绍了DL动态加载框架的基本结构与原理,包括如何在不安装的情况下运行apk,并确保Activity生命周期正常调用,以及如何解决Activity内部资源访问问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文来自【 Mr.Simple的博客 】http://blog.youkuaiyun.com/bboyfeiyu/article/details/42611963


前言

最近这一两年,Android App使用插件化技术开发的数量越来越大,其实还是业务地快速膨胀导致,需求越来越多,App越来越臃肿。虽然手机的内存空间不断地的增大,但是太大的安装包给用户也造成了心理压力。于是大家都会想到插件化的开发方式,把App做成一个平台,而不是一个独立的app。平台上可以集成各种各样的功能,功能模块也插件的形式添加进来,这些插件不需要安装,只需要用户按需下载到某个位置,然后使用的时候动态加载进来。

 想法都是好的,但是想实现一个相对比较稳定的动态加载框架还是有点难度的,一些大公司都有成熟的动态加载框架,但是他们并没有开源出来。好在2014年知名优快云博主任玉刚开源了一款名为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函数:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)  
  2. protected void launchTargetActivity() {  
  3.     try {  
  4.         // mClass就是目标插件Activity的类名  
  5.         Class<?> localClass = getClassLoader().loadClass(mClass);  
  6.         Constructor<?> localConstructor = localClass.getConstructor(new Class[] {});  
  7.         // 通过反射构建插件Activity  
  8.         Object instance = localConstructor.newInstance(new Object[] {});  
  9.         mPluginActivity = (DLPlugin) instance;  
  10.         // 将插件Activity注入给Proxy Activity  
  11.         ((DLAttachable) mProxyActivity).attach(mPluginActivity, mPluginManager);  
  12.         // 插件activity也获取到Proxy的引用  
  13.         mPluginActivity.attach(mProxyActivity, mPluginPackage);  
  14.   
  15.         Bundle bundle = new Bundle();  
  16.         bundle.putInt(DLConstants.FROM, DLConstants.FROM_EXTERNAL);  
  17.         // 调用插件Activity的onCreate函数,启动插件Activity  
  18.         mPluginActivity.onCreate(bundle);  
  19.     } catch (Exception e) {  
  20.         e.printStackTrace();  
  21.     }  
  22. }  

宿主在加载插件apk时会解析该apk的相关信息,比如它的默认启动的Activity,上述形式将apk运行起来,那么在插件apk中的activity跳转则需要通过DLIntent类将目标activity的包名、类名传递给DL框架,DL内部会进行解析以及相应的加载逻辑。


DLProxyActivity核心代码: 

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class DLProxyActivity extends Activity implements DLAttachable {  
  2.   
  3.     // 插件Activity  
  4.     protected DLPlugin mRemoteActivity;  
  5.     // DLProxyImpl加载插件Activity和资源等  
  6.     private DLProxyImpl impl = new DLProxyImpl(this);  
  7.   
  8.     @Override  
  9.     protected void onCreate(Bundle savedInstanceState) {  
  10.         super.onCreate(savedInstanceState);  
  11.         // Proxy onCreate的时候调用DLProxyImpl的onCreate,加载插件Activity  
  12.         impl.onCreate(getIntent());  
  13.     }  
  14.   
  15.     // 加载完插件Activity后将插件Activity传递给Proxy Activity  
  16.     @Override  
  17.     public void attach(DLPlugin remoteActivity, DLPluginManager pluginManager) {  
  18.         mRemoteActivity = remoteActivity;  
  19.     }  
  20.   
  21.     // 获取资源,DLProxyImpl加载了插件apk的资源,通过这里代理资源操作,这样插件Activity内部就可以通过R访问资源文件了  
  22.     @Override  
  23.     public Resources getResources() {  
  24.         return impl.getResources() == null ? super.getResources() : impl.getResources();  
  25.     }  
  26.   
  27.     /***********************  以下都是代理插件Activity的生命周期函数  ***********************/  
  28.   
  29.     @Override  
  30.     protected void onStart() {  
  31.         mRemoteActivity.onStart();  
  32.         super.onStart();  
  33.     }  
  34.   
  35.     @Override  
  36.     protected void onRestart() {  
  37.         mRemoteActivity.onRestart();  
  38.         super.onRestart();  
  39.     }  
  40.   
  41.     @Override  
  42.     protected void onResume() {  
  43.         mRemoteActivity.onResume();  
  44.         super.onResume();  
  45.     }  
  46.   
  47.     @Override  
  48.     protected void onPause() {  
  49.         mRemoteActivity.onPause();  
  50.         super.onPause();  
  51.     }  
  52.   
  53.     @Override  
  54.     protected void onStop() {  
  55.         mRemoteActivity.onStop();  
  56.         super.onStop();  
  57.     }  
  58.   
  59.     @Override  
  60.     protected void onDestroy() {  
  61.         mRemoteActivity.onDestroy();  
  62.         super.onDestroy();  
  63.     }  
  64.   
  65.     // 代码省略  
  66.   
  67. }  

下面我们来看另外一个重点,也就是插件apk的资源加载。关于apk的资源加载机制也请参考上面给出的文章,我们直接看代码。

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private DexClassLoader createDexClassLoader(String dexPath) {  
  2.     File dexOutputDir = mContext.getDir("dex", Context.MODE_PRIVATE);  
  3.     dexOutputPath = dexOutputDir.getAbsolutePath();  
  4.     // 创建ClassLoader  
  5.     DexClassLoader loader = new DexClassLoader(dexPath, dexOutputPath, mNativeLibDir,  
  6.             mContext.getClassLoader());  
  7.     return loader;  
  8. }  
  9.   
  10. // 根据插件apk的路径构建AssetManager,将其资源所在的路径添加到AssetManager中,然后通过AssetManager构建一个Resources对象,这个对象就是插件apk的资源对象,插件apk内部访问资源时都是通过这个资源对象  
  11. private AssetManager createAssetManager(String dexPath) {  
  12.     try {  
  13.         AssetManager assetManager = AssetManager.class.newInstance();  
  14.         Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);  
  15.         addAssetPath.invoke(assetManager, dexPath);  
  16.         return assetManager;  
  17.     } catch (Exception e) {  
  18.         e.printStackTrace();  
  19.         return null;  
  20.     }  
  21.   
  22. }  
  23.   
  24. private Resources createResources(AssetManager assetManager) {  
  25.     Resources superRes = mContext.getResources();  
  26.     Resources resources = new Resources(assetManager, superRes.getDisplayMetrics(),  
  27.             superRes.getConfiguration());  
  28.     return resources;  
  29. }  

这样,等于是插件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框架的开发中来。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值