Android 插件化框架 DynamicLoadApk 源码分析

C语言进阶实战:从指针内存到文件操作的系统化学习指南 在编程学习过程中,理解计算机内存管理和数据持久化是核心基础。C语言作为系统级编程的基石,其指针和内存操作原理是理解程序运行机制的关键。通过动态内存分配(malloc/free)和结构体组织数据,开发者能构建高效、灵活的数据结构,这是实现复杂算法和系统软件的技术价值所在。在实际应用场景中,文件操作(fread/fwrite)使得数据得以持久化存储,而字符串处理则是日常开发中的高频需求。本文以《C Primer Plus》下半部分为蓝本,聚焦于**内存管理**和**文件操作**等实战难点,通过环境配置、代码案例 阅读详情

DynamicLoadApk 应该算是 Android 插件化诸多框架中资历比较老的一个了。它的项目地址在:dynamic-load-apk。该项目运行之后的效果是,使用 Gradle 编译出插件包和宿主包,都是以 APK 的形式。安装宿主包之后,通过 ADB 将插件包 push 到手机中。启动宿主包时,它会自动进行扫描将插件加载到应用中。点击插件之后,进入到插件的应用界面。

印象中最初接触的插件都是以单独安装的形式存在的,比如我可以做一个基础的应用,然后在该应用的基础上开发插件。用户可以对插件进行选择,然后下载并安装,以让自己的应用具有更丰富的功能。插件化也算是一种比较实用的技术,毕竟我们使用 Chrome 和 AS 的时候不是一样要加载插件。只是比较反感的是去修改底层的代码,容易给系统带来不稳定因素不说,技术到了一些人手里,你知道他用来干什么。插件化挺好,但真的要去推广这项技术,还是看好 Google 官方去进行规范。

技术要服务于产品,好的产品不一定要高超的技术,技术并不是最重要的,重要的是你究竟想要表达什么。这就像国内很多人只注重数理化,不注重人文学科。相比于国内的技术精英,我还是比较赞同 Google 站在整个生态的角度去考虑技术演进。前些日子社区里对插件化的讨论:移动开发的罗曼蒂克消亡史。好吧,我自己的理解是,这从来就不是什么罗曼蒂克。

DynamicLoadApk 插件化的实现方式还是挺有意思的,它使用纯 Java 实现,没有涉及 Native 层的代码,下面我理了下 DynamicLoadApk 的 Demo 程序的整个执行过程。后续的文章我们就围绕这张图进行,

DynamicLoadApk 的插件化执行过程

首先是扫描文件路径并加载 APK,这里需要解析 APK 文件的信息,它是本质上是通过 PMS 实现的;

    public DLPluginPackage loadApk(final String dexPath, boolean hasSoLib) {
        mFrom = DLConstants.FROM_EXTERNAL;

        // 通过 PMS 获取包信息,这里获取了 Activity 和 Service 的信息
        PackageInfo packageInfo = mContext.getPackageManager().getPackageArchiveInfo(dexPath,
                PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES);
        if (packageInfo == null) {
            return null;
        }

        DLPluginPackage pluginPackage = preparePluginEnv(packageInfo, dexPath);
        if (hasSoLib) {
            copySoLib(dexPath);
        }

        return pluginPackage;
    }

然后,它通过调用 preparePluginEnv() 方法来创建 AssetManager, DexClassLoader 和 Resource 等。我们的插件类加载各种资源和类的时候使用的就是这哥仨:

    private DLPluginPackage preparePluginEnv(PackageInfo packageInfo, String dexPath) {

        DLPluginPackage pluginPackage = mPackagesHolder.get(packageInfo.packageName);
        if (pluginPackage != null) {
            return pluginPackage;
        }
        DexClassLoader dexClassLoader = createDexClassLoader(dexPath);
        AssetManager assetManager = createAssetManager(dexPath);
        Resources resources = createResources(assetManager);
        // create pluginPackage
        pluginPackage = new DLPluginPackage(dexClassLoader, resources, packageInfo);
        mPackagesHolder.put(packageInfo.packageName, pluginPackage);
        return pluginPackage;
    }

    private DexClassLoader createDexClassLoader(String dexPath) {
        File dexOutputDir = mContext.getDir("dex", Context.MODE_PRIVATE);
        dexOutputPath = dexOutputDir.getAbsolutePath();
        DexClassLoader loader = new DexClassLoader(dexPath, dexOutputPath, mNativeLibDir, mContext.getClassLoader());
        return loader;
    }

    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,

    PluginItem item = mPluginItems.get(position);
    DLPluginManager pluginManager = DLPluginManager.getInstance(this);
    pluginManager.startPluginActivity(this, new DLIntent(item.packageInfo.packageName, item.launcherActivityName));

这里会把要启动的包名和启动类名包装到 DLIntent 中。DLIntent 是 Intent 的子类。启动插件的进一步的逻辑在 DLPluginManager 的 startPluginActivity() 方法中。按照上文的描述,这里主要做了以下四件事情:

  1. 判断要启动的 Activity 是否是插件 Activity:因为要启动的类也可能不是插件类,所以我们需要分成两种情况来进行处理,普通的 Activity 直接调用 Context.startActivity() 插件 Activity 需要调用代理 Activity 来执行。
  2. 判断包名,获取插件相关信息:这里就算是一个安全的校验吧,主要是从之前解析的 APK 信息中进行校验。
  3. 使用插件的 DexClassLoader 加载启动类:先要使用类加载器加载插件的 Activity 到内存中,插件 Activity 的信息会作为 Intent 的参数一起传递给代理 Activity。
  4. 使用 DLIntent.setClass() 启动代理类:要启动的代理类可能是 DLProxyFragmentActivity 和 DLProxyActivity,所以这里我们先使用 getProxyActivityClass() 得到代理类。该方法中使用了 Class 的 isAssignableFrom() 方法来判断某个实例是否是指定类型的。比如 DLBasePluginActivity.class.isAssignableFrom(clazz) 表示 clazz 是否是 DLBasePluginActivity 类型的。
    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    public int startPluginActivityForResult(Context context, DLIntent dlIntent, int requestCode) {
        // 1.判断要启动的 Activity 是否是插件 Activity
        if (mFrom == DLConstants.FROM_INTERNAL) {
            dlIntent.setClassName(context, dlIntent.getPluginClass());
            performStartActivityForResult(context, dlIntent, requestCode);
            return DLPluginManager.START_RESULT_SUCCESS;
        }

        // 2.判断包名,获取插件相关信息
        String packageName = dlIntent.getPluginPackage();
        if (TextUtils.isEmpty(packageName)) {
            throw new NullPointerException("disallow null packageName.");
        }

        DLPluginPackage pluginPackage = mPackagesHolder.get(packageName);
        if (pluginPackage == null) {
            return START_RESULT_NO_PKG;
        }

        // 3.使用插件的 DexClassLoader 加载启动类
        final String className = getPluginActivityFullPath(dlIntent, pluginPackage);
        Class<?> clazz = loadPluginClass(pluginPackage.classLoader, className);
        if (clazz == null) {
            return START_RESULT_NO_CLASS;
        }

        Class<? extends Activity> activityClass = getProxyActivityClass(clazz);
        if (activityClass == null) {
            return START_RESULT_TYPE_ERROR;
        }

        // 4.使用 DLIntent.setClass() 启动代理类,并传入插件类和包信息
        dlIntent.putExtra(DLConstants.EXTRA_CLASS, className);
        dlIntent.putExtra(DLConstants.EXTRA_PACKAGE, packageName);
        dlIntent.setClass(mContext, activityClass);
        performStartActivityForResult(context, dlIntent, requestCode);
        return START_RESULT_SUCCESS;
    }

    private Class<? extends Activity> getProxyActivityClass(Class<?> clazz) {
        Class<? extends Activity> activityClass = null;
        if (DLBasePluginActivity.class.isAssignableFrom(clazz)) {
            activityClass = DLProxyActivity.class;
        } else if (DLBasePluginFragmentActivity.class.isAssignableFrom(clazz)) {
            activityClass = DLProxyFragmentActivity.class;
        }

        return activityClass;
    }

    private void performStartActivityForResult(Context context, DLIntent dlIntent, int requestCode) {
        if (context instanceof Activity) {
            ((Activity) context).startActivityForResult(dlIntent, requestCode);
        } else {
            context.startActivity(dlIntent);
        }
    }

这里需要注意下,我们的插件 Activity 是需要继承 DLBasePluginActivity 或者 DLProxyFragmentActivity。这两个类中重写了 Activity 的许多生命周期方法。在代理 Activity 启动之后,代理 Activity 会被传递到前面两个基类中。比如,当插件类想要获取 AssetsManager 的时候,会调用到这两个基类的 getAssetsManager(),然后基类通过代理类得到之前我们创建的 AssetsManager.

按照上述流程,代理类被正常启动。启动之后它会创建 DLProxyImpl 实例,并在 onCreate() 方法中调用 DLProxyImpl 的 onCreate() 方法:

    public void onCreate(Intent intent) {
        intent.setExtrasClassLoader(DLConfigs.sPluginClassloader);

        mPackageName = intent.getStringExtra(DLConstants.EXTRA_PACKAGE);
        mClass = intent.getStringExtra(DLConstants.EXTRA_CLASS);

        mPluginManager = DLPluginManager.getInstance(mProxyActivity);
        mPluginPackage = mPluginManager.getPackage(mPackageName);
        mAssetManager = mPluginPackage.assetManager;
        mResources = mPluginPackage.resources;

        initializeActivityInfo();
        handleActivityInfo();
        launchTargetActivity();
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    protected void launchTargetActivity() {
        try {
            Class<?> localClass = getClassLoader().loadClass(mClass);
            Constructor<?> localConstructor = localClass.getConstructor(new Class[] {});
            Object instance = localConstructor.newInstance(new Object[] {});
            mPluginActivity = (DLPlugin) instance;
            ((DLAttachable) mProxyActivity).attach(mPluginActivity, mPluginManager);
            mPluginActivity.attach(mProxyActivity, mPluginPackage);

            Bundle bundle = new Bundle();
            bundle.putInt(DLConstants.FROM, DLConstants.FROM_EXTERNAL);
            mPluginActivity.onCreate(bundle);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

这里的主要逻辑在上述两个方法中。第一个方法中会根据包名从 DLPluginManager 中获取包的类加载器,然后使用该加载其加载器加载插件类,反射触发其构造方法,获取实例。然后调用代理 Activity 的 attach() 方法将该插件类复制给代理类。然后当 AMS 回调代理类的各个生命周期的时候,代理类调用插件类的各个生命周期。(这里会使用类加载器再次加载插件类,其实这是没必要的,我们可以直接使用 Intent 将插件类的 Class 通过序列化的方式传递过来,然后直接触发其构造方法即可,无需再次执行类加载逻辑。)

好了,以上就是 DynamicLoadApk 的原理,其实本质就是:插件类作为一个普通的类被调用,它不归 AMS 负责。当我们启动插件的时候,实际启动的是代理类,当 AMS 回调代理类的生命周期的时候,代理类再调用插件类的各个生命周期方法。只是,对资源和类加载的部分需要注意下,因为我们需要进行自定义配置来把它们的路径指向我们的插件包。

高级Android插件化强化实战,大厂插件化框架项目解析 一、认识插件化 插件化技术最初源于免安装运行 Apk的想法,这个免安装的 Apk 就可以理解为插件,而支持插件的 app 我们一般叫 宿主。 在 Android 系统中,应用是以 Apk 的形式存在的,应用都需要安装才能使用。但实际上 Android 系统安装应用的方式相当简单,其实就是把应用 Apk 拷贝到系统不同的目录下、然后把 so 解压出来而已。 常见的应用安装目录有: /system/app:系统应用 /system/priv-app:系统应用 /data/app:用户应用 那可能大家会想问, 阅读详情

相关推荐

电源完整性之Cadence Sigrity Power DC_IR_Drop仿真

之前和大家分享过电源完整性之仿真设计原理链接: link.今天接着上一篇文章总结一下电源直流压降的的仿真操作流程及一些simulation中的设置参数,用到的时候Cadence Sigrity 中的Power DC组件,下面开始享受仿真的快乐吧: 1.仿真模式选择与文件导入 选择Cadence Sigrity下的Power DC组件,然后先择licence,最后就可以打开我们之前转化好的.SPD文件。操作如下图: 2.设置层叠信息和过孔焊盘参数 层叠信息:每层的介质厚度、材料、介电常数、介质损耗角等,如

刘小同学的博客 1万+

属性文法和语法制导翻译

前面通过词法分析,语法分析,DFA最后接受了一个输入实际上是理解了某一句编程语句,编译器的角色是将高级程序语言编译(翻译)为汇编代码,通过词法、语法分析编译器可以理解高级程序语言了,那么如何实现输出汇编代码和创建变量等等这一系列动作?属性文法,通过为产生式配备属性的计算规则,通过计算和传递属性处理语义,实现这一系列动作。

qq_23096319的博客 688

抖音直播参数逆向实战:手把手教你用Python获取ttwid和x-bogus(附完整代码)

本文深入解析抖音直播接口的反爬机制,重点剖析核心防护参数ttwid和x-bogus的生成原理与作用。通过Python实战,手把手教你逆向获取ttwid、webid等关键参数,并构建稳定的数据采集方案,为开发者提供企业级数据获取能力。

weixin_29196891的博客 1043

推荐文章:深度探索DynamicAPK——打造高效灵活的Android应用动态加载方案

在移动开发领域,提高应用性能与迭代效率是每个开发者不断追求的目标。今天,我们来揭秘一款由携程技术团队打造的强大工具——**DynamicAPK**,它为Android开发者提供了一种革新性的多APK/DEX动态加载解决方案。DynamicAPK不仅简化了应用的构建过程,还支持热修复与按需加载功能,这无疑是一次对传统App构建模式的挑战与优化。 ## 一、项目介绍 DynamicAPK是一个高度...

gitblog_00284的博客 548

树莓派与stm32通信超详细解答过程

笔者之前发过关于stm32与openmv通信的教程,在学习了几天opencv后,想实现树莓派4B与stm32进行通信。于是上网查询资料,最后实现了两者之间的通信,相信这对大多数电赛人都很有帮助,opencv的识别效果是远高于openmv以及K210的。在此我参考了以下资料辅助我学习,大家可以参考。基于树莓派4B与STM32的UART串口通信实验(代码开源)_树莓派4b串口波特率-优快云博客。

Halcyon0804的博客 5465

Mamba与iTransformer融合:时间序列预测中的曼巴注意力机制创新实践

本文探讨了Mamba与iTransformer融合在时间序列预测中的创新实践,重点介绍了曼巴注意力机制的设计与优势。通过双路径信息流设计,该方案在保持O(n)复杂度的同时,显著提升了预测精度和训练效率。实验证明,混合架构在电力负荷、气象数据等多变量长序列场景中表现优异,预测误差降低至0.083,内存占用仅4.2GB。

weixin_29292535的博客 219

DynamicAPK基本概念

从这篇blog开始,我准备写一个关于Android动态加载的博客,主要Android插件开发的原理,前面几篇主要以携程开源的DynamicAPK 的源码作为基础来进行分析,后面可能会对该库进行一些改进,并提交到我自己fork的repo中,欢迎感兴趣的同学一起来交流。这篇主要先介绍一下DynamicAPK中的一些基本的概念,以及DynamicAPK中的插件的存储。关于动态加载的基础知识,欢迎参考我之

大头鬼Bruce 9970

RTDETR多模态融合实战:基于注意力机制的红外与可见光目标检测优化

本文深入探讨了基于RTDETR模型的红外与可见光多模态目标检测优化方案。针对复杂光照下的检测难题,文章提出并实战演示了如何利用注意力机制(如SimAM与交叉注意力)实现RGB与IR特征的智能动态融合,从而构建更鲁棒的“全天候”检测系统,有效提升安防、自动驾驶等场景在夜间及恶劣天气下的检测精度与可靠性。

weixin_29083373的博客 187

Dynamic loading

 From Wikipedia, the free encyclopediaJump to: navigation, searchDynamic loading is a mechanism by which a computer program can, at runtime, load a library (or other binary) into memory, retri

Codelife 1442

android studio 2022打开了v1 签名但是生成的apk没有v1签名问题

我使用了Android Studio Flamingo | 2022.2.1 Patch 2版本的IDE编译了一个apk,但是apksigner查看apk的签名信息时,发现只有v2签名,没有v1签名。试着改小一下minSdkVersion 为23重新编译apk,再检测一下签名版本v1居然打开了!参考网上搜索到的方法,手动在build.gradle里面添加。但是编译出来的apk还是只有v2的签名。里面提到了v1签名还和。

hedalei的博客 464

PCL SAC-IA 初始配准算法【2025最新版】

采样一致性初始配准算法,简称SAC_IA。博客长期更新,本文最新更新时间为:2025年1月1日。新增参数设置建议

点云侠的博客 37万+

Android插件化技术让你的应用升级不再是难题!!

Android插件化是一种先进的软件开发技术,它允许开发者将一个大型应用程序分解成多个较小、独立的模块(即插件),这些插件可以在运行时动态加载到主应用(宿主应用)中。这样做,应用程序不再需要将所有功能都打包在一个庞大的APK文件中,而是可以按需加载不同的功能模块。插件化的实现通常涉及复杂的类加载机制、资源管理、以及模块间的通信技术,使得应用能够更加灵活地扩展和更新功能,而不必每次更新都重新安装整个应用。总之,学习Android插件化是一个从理论到实践、从基础到深入的过程,需要耐心和持续的努力。

大模型训练 728

深度学习论文: AnomalyDINO: Boosting Patch-based Few-shot Anomaly Detection with DINOv2

通过将DINOv2模型适应到一次性和少量异常检测中,并专注于工业应用少量异常检测。实验表明,这种方法不仅与现有技术相匹敌,甚至可以在许多设置中超越它们。我们提出的仅视觉方法AnomalyDINO基于补丁相似性,能够实现图像级别的异常预测和像素级别的异常分割。该方法在方法论上很简单,无需训练,因此不需要任何额外的数据进行微调或元学习。

mingo_敏 1902

Android插件化进阶宝典,Android高级插件化强化实战!

最后对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!这里附上上述的技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。相信它会给大家带来很多收获:上述【高清技术脑图】以及【配套的架构技术PDF】可以关注我免费获取Android学习PDF+架构视频+面试文档+源码笔记。

碧海朝天素 963

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

DynamicLoadApk 源码解析 http://codekk.com/open-source-project-analysis

本文为 Android 开源项目源码解析 中 DynamicLoadApk 部分 项目地址:DynamicLoadApk,分析的版本:144571b,Demo 地址:DynamicLoadApk Demo 分析者:FFish,分析状态:完成,校对者:Trinea,校对状态:初审完成 1. 功能介绍 1.1 简介 DynamicLoadApk 是一个开源的 Androi

Min的专栏 997

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

DroidPlugin源码分析

DroidPlugin插件学习资料整理 Android插件化在我们开发的过程中越来越使用的频繁,市面上使用比较多的和团队支持的比较好的主要有一下几个: 携程插件化框架 ACDD插件化框架 360插件化机制 Android-Plugin-Framework DL APK动态加载框架 DynamicLoadApk 迁移成本很重:需要使用『that』而

cangck_x的博客 453

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

别人源码的解析

点击打开链接 包括: android-Ultra-Pull-To-Refresh 源码解析 一个强大的 Andriod 下拉刷新框架。主要特点:(1) 继承于 ViewGroup,Content 不仅可以是常用的 ListView、GridView,还可以是其他任何 View。(2) 简洁完善的 Header 抽象,方便进行拓展,构建符合需求的头部。UltraPTR

congxianji的博客 334

Android APK插件化:DynamicAPK技术如何改变游戏规则

在移动应用开发领域,尤其是Android平台,应用的体积和更新速度一直是开发者和用户关注的焦点。随着应用功能的不断增加,APK文件的大小也在逐渐膨胀,这不仅增加了用户的下载成本,也影响了应用的更新效率。DynamicAPK技术的出现,为这一问题提供了一种创新的解决方案——通过插件化的方式,将APK的某些部分动态加载,从而实现应用的灵活更新和扩展。本文将探讨DynamicAPK技术如何改变Android应用开发的游戏规则。

技术无疆 1806

携程DynamicAPK插件化框架源码分析

携程DynamicAPK插件化框架源码分析Author:莫川插件核心思想1.aapt的改造分别对不同的插件项目分配不同的packageId,然后对各个插件的资源进行编译,生成R文件,然后与宿主项目的R文件进行id的合并。 要求:由于最终会将所有的资源文件id进行合并,因此,所有的资源名称均不能相同。2.运行ClassLoader加载各Bundle和MultiDex的思路是一样的,所有的插件都被加载到

郑海波(莫川)的优快云博客 5842

android 插件化方案对比及原理

一、技术背景 Android的插件化技术,目前已经比较成熟,微信、淘宝、携程、360手机助手中都应用到了插件化。插件化技术的特点是无需单独安装apk,即可运行,即插即用,无需升级宿主应用,减少app的更新频率, 除此之外他还可以降低模块耦合,按需加载,节省流量等特点。 二、已有框架技术对比 表1从是否支持四大组件、是否须在主manifest预注册等多个维度对主流开源框架进行对比,从而筛选出比较符合项目的框架有VirtualAPK、RePlugin。 表1主流开源框...

TyearLin的专栏 3878

终极指南:如何在5分钟内搭建您的私有AI知识库

您是否厌倦了将敏感文档上传到云端AI服务?是否希望拥有一个完全私有的、能理解您所有文档的智能助手?今天,我们将向您介绍AnythingLLM——一款全栈应用程序,能够将各类文档转换为大语言模型可使用的上下文,帮助您构建本地部署的私有知识库。无论是商业LLM还是开源方案,AnythingLLM都能灵活适配,同时支持多用户权限管理,满足不同场景下的智能交互需求。 ## 第一部分:快速入门指引 ##

gitblog_01134的博客 415

android 插件化技术的演进之路

android 插件化技术的演进之路

idaretobe的专栏 527

Dagger2集成之环境配置及简单验证

1.1 配置Dagger2依赖关系 目前android应用的代码管理大多采用Gradle来做,但是在集成Dagger2的过程中,Gradle的版本与Dagger2的依赖方式存在一定的相关性,在网上一些介绍集成Dagger2的资料中,大多采用“com.neenbedankt.gradle.plugins:android-apt:1.4”来作为Dagger2的辅助依赖工具,而实际上对稍高一些的Gradle来说,要继续采用apt的方式对接Dagger会显得比较困难,而且对Gradle来说,也不建议再继续使用ap

甘谷天蚕网络技术有限公司 1830

正确运行携程DynamicAPK

首先保证你已经配置好JAVA环境,检查系统变量里面是否含有JAVA_HOME 然后从github下载项目 $ git clone https://github.com/CtripMobile/DynamicAPK.git 用android studio打开项目,会报如下错误 修改build.gradle,把 buildscript { repositori

ujnnet的博客 1592

携程dynamicAPK框架研究(一)

为什么80%的码农都做不了架构师?>>> ...

weixin_34050427的博客 253

DynamicAPK:Android插件化与动态加载的终极解决方案

你是否还在为以下问题而烦恼? - **编译时间过长**:随着项目规模扩大,每次编译都需要等待数分钟甚至更久 - **启动性能瓶颈**:MultiDex方案导致应用启动缓慢,用户面临黑屏等待 - **热修复困难**:线上Bug修复需要重新发布整个应用,流程繁琐 - **团队协作效率低**:多个团队并行开发时,代码冲突频繁,集成困难 - **功能模块无法按需加载**:即使用户不需要某些功能,也必须打...

gitblog_00263的博客 394
上一篇: 在 Android 中使用 JNI 的总结
下一篇: com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection...
开发者如是说
博客等级 码龄10年 67粉丝 163原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值