Android插件化学习之路(一)之动态加载综述

2024-06-24 05:38

本文主要是介绍Android插件化学习之路(一)之动态加载综述,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前段时间,公司项目完成了插件化的开发,自己也因此学习了很多Android插件化的知识,于是想把这些内容记录下来,本次带来Android插件化的第一篇:动态加载综述

Android插件化学习之路(一)之动态加载综述
Android插件化学习之路(二)之ClassLoader完全解析
Android插件化学习之路(三)之调用外部.dex文件中的代码
Android插件化学习之路(四)之使用插件中的R资源
Android插件化学习之路(五)之代理Activity
Android插件化学习之路(六)之动态创建Activity
Android插件化学习之路(七)之DL插件开发该注意的坑
Android插件化学习之路(八)之DynamicLoadApk 源码解析(上)
Android插件化学习之路(九)之DynamicLoadApk 源码解析(下)
背景知识

1.什么是动态加载?

动态加载技术应由以下几个部分组成:

  1. 应用在运行的时候通过加载一些本地不存在的可执行文件实现一些特定的功能;
  2. 这些可执行文件是可以替换的;
  3. 更换静态资源(比如换启动图、换主题、或者用服务器参数开关控制广告的隐藏现实等)不属于动态加载;
  4. Android中动态加载的核心思想是动态调用外部的 dex文件,极端的情况下,Android APK自身带有的Dex文件只是一个程序的入口(或者说空壳),所有的功能都通过从服务器下载最新的Dex文件完成;

2.动态加载的类型

Android项目中,动态加载技术按照加载的可执行文件的不同大致可以分为两种:

  1. 动态加载so库;
  2. 动态加载dex/jar/apk文件(现在动态加载普遍说的是这种);

第一种,Android中NDK中其实就使用了动态加载,动态加载.so库并通过JNI调用其封装好的方法。后者一般是由C/C++编译而成,运行在Native层,效率会比执行在虚拟机层的Java代码高很多,所以Android中经常通过动态加载.so库来完成一些对性能比较有需求的工作(比如T9搜索、或者Bitmap的解码、图片高斯模糊处理等)。此外,由于so库是由C/C++编译而来的,只能被反编译成汇编代码,相比中dex文件反编译得到的Smali代码更难被破解,因此so库也可以被用于安全领域。这里为后面要讲的内容提前说明一下,一般情况下我们是把so库一并打包在APK内部的,但是so库其实也是可以从外部存储文件加载的。

第二种,“基于ClassLoader的动态加载dex/jar/apk文件”,就是我们上面提到的“在Android中动态加载由Java代码编译而来的dex包并执行其中的代码逻辑”,这是常规Android开发比较常用到的一种技术,目前网络上大多文章说到的动态加载指的就是这种。

3.Android中的动态加载技术
Java的可执行文件是Jar,运行在虚拟机上JVM上,虚拟机通过ClassLoader加载Jar文件并执行里面的代码。所以Java程序也可以通过动态调用Jar文件达到动态加载的目的。

Android项目中,所有Java代码都会被编译成dex文件,Android应用运行时,就是通过执行dex文件里的业务代码逻辑来工作的。使用动态加载技术可以在Android应用运行时加载外部的dex文件,而通过网络下载新的dex文件并替换原有的dex文件就可以达到不安装新APK文件就升级应用(改变代码逻辑)的目的。

4.Android动态加载的大致过程

无论上面的哪种动态加载,其实基本原理都是在程序运行时加载一些外部的可执行的文件,然后调用这些文件的某个方法执行业务逻辑。需要说明的是,因为文件是可执行的(so库或者dex包,也就是一种动态链接库),出于安全问题,Android并不允许直接加载手机外部存储这类noexec(不可执行)存储路径上的可执行文件

对于这些外部的可执行文件,在Android应用中调用它们前,都要先把他们拷贝到data/packagename/内部储存文件路径,确保库不会被第三方应用恶意修改或拦截,然后再将他们加载到当前的运行环境并调用需要的方法执行相应的逻辑,从而实现动态调用。

动态加载的大致过程就是:

  1. 把可执行文件(.so/dex/jar/apk)拷贝到应用APP内部存储;
  2. 加载可执行文件;
  3. 调用具体的方法执行业务逻辑;

5.动态加载 so库
动态加载so库应该就是Android最早期的动态加载了,不过so库不仅可以存放在APK文件内部,还可以存放在外部存储。Android开发中,更换so库的情形并不多,但是可以通过把so库挪动到APK外部,减少APK的体积,毕竟许多so库文件的体积可是非常大的。

6.动态加载 dex/jar/apk文件

我们经常讲到的那种Android动态加载技术就是这种,后面我们谈到“动态加载”如果没有特别指定,均默认是这个。

基础知识:类加载器ClassLoader和dex文件
动态加载dex/jar/apk文件的基础是类加载器ClassLoader,它的包路径是java.lang,由此可见其重要性,虚拟机就是通过类加载器加载其需要用的Class,这是Java程序运行的基础。

现在网上有多种基于ClassLoader的Android动态加载的开源项目,大部分核心思想都差不多,按照复杂程度以及具体实现的框架,大致可以分为以下三种形式。

简单的动态加载模式

Android应用在运行时使用ClassLoader动态加载外部的dex文件非常简单,不用覆盖安装新的APK,就可以更改APP的代码逻辑。但是Android却很难使用插件APK里的res资源,这意味着无法使用新的XML布局等资源,同时由于无法更改本地的Manifest清单文件,所以无法启动新的Activity等组件。

不过可以先把要用到的全部res资源都放到主APK里面,同时把所有需要的Activity先全部写进Manifest里,只通过动态加载更新代码,不更新res资源,如果需要改动UI界面,可以通过使用纯Java代码创建布局的方式绕开XML布局。同时也可以使用Fragment代替Activity,这样可以最大限度得避开“无法注册新组件的限制”。

这种模式的框架比较适用一些UI变化比较少的项目,比如游戏SDK,基本就只有登陆、注册界面,而且基本不会变动,更新的往往只有代码逻辑。

代理Activity模式
我们可以通过动态加载,让现在的Android应用启动一些“新”的Activity,甚至不用安装就启动一个“新”的APK。宿主APK需要先注册一个空壳的Activity用于代理执行插件APK的Activity的生命周期。

  1. 宿主APK可以启动未安装的插件APK;

  2. 插件APK也可以作为一个普通APK安装并且启动;

  3. 插件APK可以调用宿主APK里的一些功能;

  4. 宿主APK和插件APK都要接入一套指定的接口框架才能实现以上功能;

同时也主要有一下几点限制:

  1. 需要在Manifest注册的功能都无法在插件实现,比如应用权限、LaunchMode、静态广播等;

  2. 宿主一个代理用的Activity难以满足插件一些特殊的Activity的需求,插件Activity的开发受限于代理Activity;

  3. 宿主项目和插件项目的开发都要接入共同的框架,大多时候,插件需要依附宿主才能运行,无法独立运行;

代理Activity模式的核心在于“使用宿主的一个代理Activity为插件所有的Activity提供组件工作需要的环境”,随着代理模式的逐渐成熟,现在还出现了“使用Hack手段给插件的Activity注入环境”的模式。

动态创建Activity模式
动态创建Activity模式的核心是“运行时字节码操作”,现在宿主注册一个不存在的Activity,启动插件的某个Activity时都把想要启动的Activity替换成前面注册的Activity,从而是后者能正常启动。

  1. 主APK可以启动一个未安装的插件APK;
  2. 插件APK可以是任意第三方APK,无需接入指定的接口,理所当然也可以独立运行;

动态加载技术的作用与缺点

作用

  1. 规避APK覆盖安装的升级过程,提高用户体验,顺便能 规避 一些安卓市场的限制;
  2. 动态修复应用的一些 紧急BUG,做好最后一道保障;
  3. 当应用体积太庞大的时候,可以把一些模块通过动态加载以插件的形式分割出去,这样可以减少主项目的体积,提高项目的编译速度,也能让主项目和插件项目并行开发;
  4. 插件模块可以用懒加载的方式在需要的时候才初始化,从而 提高应用的启动速度;
  5. 从项目管理上来看,分割插件模块的方式做到了 项目级别的代码分离,大大降低模块之间的耦合度,同一个项目能够分割出不同模块在多个开发团队之间 并行开发,如果出现BUG也容易定位问题;
  6. 在Android应用上 推广 其他应用的时候,可以使用动态加载技术让用户优先体验新应用的功能,而不用下载并安装全新的APK;
  7. 减少主项目DEX的方法数,65535问题 彻底成为历史(虽然现在在Android Studio中很容易开启MultiDex,这个问题也不难解决);

缺点

  1. 开发方式可能变得比较诡异、繁琐,与常规开发方式不同;
  2. 随着动态加载框架复杂程度的加深,项目的构建过程也变得复杂,有可能要主项目和插件项目分别构建,再整合到一起;
  3. 由于插件项目是独立开发的,当主项目加载插件运行时,插件的运行环境已经完全不同,代码逻辑容易出现BUG,而且在主项目中调试插件十分繁琐;
  4. 非常规的开发方式,有些框架使用反射强行调用了部分Android系统Framework层的代码,部分Android ROM可能已经改动了这些代码,所以有存在兼容性问题的风险,特别是在一些古老Android设备和部分三星的手机上;
  5. 采用动态加载的插件在使用系统资源(特别是Theme)时经常有一些兼容性问题,特别是部分三星的手机;

其他动态修改代码的技术

上面说到的都是基于ClassLoader的动态加载技术(除了加载SO库外),使用ClassLoader的一个特点就是,如果程序不重新启动,加载过一次的类就无法重新加载。因此,如果使用ClassLoader来动态升级APP或者动态修复BUG,都需要重新启动APP才能生效。

除了使用ClassLoader外,还可以使用jni hook的方式修改程序的执行代码。前者是在虚拟机上操作的,而后者做的已经是Native层级的工作了,直接修改应用运行时的内存地址,所以使用jni hook的方式时,不用重新应用就能生效。

目前采用jni hook方案的项目中比较热门的有阿里的dexposed和AndFix。

动态加载开源项目

[Freedom]我个人手写的插件化框架
DL dynamic-load-apk
android-pluginmgr
360 DroidPlugin
携程网 DynamicAPK
女娲 Nuwa
Android-Plugin-Framework

这篇关于Android插件化学习之路(一)之动态加载综述的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1089318

相关文章

51单片机学习记录———定时器

文章目录 前言一、定时器介绍二、STC89C52定时器资源三、定时器框图四、定时器模式五、定时器相关寄存器六、定时器练习 前言 一个学习嵌入式的小白~ 有问题评论区或私信指出~ 提示:以下是本篇文章正文内容,下面案例可供参考 一、定时器介绍 定时器介绍:51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成。 定时器作用: 1.用于计数系统,可

问题:第一次世界大战的起止时间是 #其他#学习方法#微信

问题:第一次世界大战的起止时间是 A.1913 ~1918 年 B.1913 ~1918 年 C.1914 ~1918 年 D.1914 ~1919 年 参考答案如图所示

[word] word设置上标快捷键 #学习方法#其他#媒体

word设置上标快捷键 办公中,少不了使用word,这个是大家必备的软件,今天给大家分享word设置上标快捷键,希望在办公中能帮到您! 1、添加上标 在录入一些公式,或者是化学产品时,需要添加上标内容,按下快捷键Ctrl+shift++就能将需要的内容设置为上标符号。 word设置上标快捷键的方法就是以上内容了,需要的小伙伴都可以试一试呢!

AssetBundle学习笔记

AssetBundle是unity自定义的资源格式,通过调用引擎的资源打包接口对资源进行打包成.assetbundle格式的资源包。本文介绍了AssetBundle的生成,使用,加载,卸载以及Unity资源更新的一个基本步骤。 目录 1.定义: 2.AssetBundle的生成: 1)设置AssetBundle包的属性——通过编辑器界面 补充:分组策略 2)调用引擎接口API

Javascript高级程序设计(第四版)--学习记录之变量、内存

原始值与引用值 原始值:简单的数据即基础数据类型,按值访问。 引用值:由多个值构成的对象即复杂数据类型,按引用访问。 动态属性 对于引用值而言,可以随时添加、修改和删除其属性和方法。 let person = new Object();person.name = 'Jason';person.age = 42;console.log(person.name,person.age);//'J

大学湖北中医药大学法医学试题及答案,分享几个实用搜题和学习工具 #微信#学习方法#职场发展

今天分享拥有拍照搜题、文字搜题、语音搜题、多重搜题等搜题模式,可以快速查找问题解析,加深对题目答案的理解。 1.快练题 这是一个网站 找题的网站海量题库,在线搜题,快速刷题~为您提供百万优质题库,直接搜索题库名称,支持多种刷题模式:顺序练习、语音听题、本地搜题、顺序阅读、模拟考试、组卷考试、赶快下载吧! 2.彩虹搜题 这是个老公众号了 支持手写输入,截图搜题,详细步骤,解题必备

《offer来了》第二章学习笔记

1.集合 Java四种集合:List、Queue、Set和Map 1.1.List:可重复 有序的Collection ArrayList: 基于数组实现,增删慢,查询快,线程不安全 Vector: 基于数组实现,增删慢,查询快,线程安全 LinkedList: 基于双向链实现,增删快,查询慢,线程不安全 1.2.Queue:队列 ArrayBlockingQueue:

加载资源文件失败

背景         自己以前装了一个海康的深度学习算法平台,试用期是一个月,过了一个月之后,因为没有有效注册码或者加密狗的支持了导致无法使用,于是打算卸载掉,在卸载一个软件的时候,无论是使用控制面板还是软件自带的卸载功能,总是卸载不掉,提示“加载资源文件失败”。该软体主要包括以下两部分: 用自带卸载功能卸载的时候分别提示如下:     用控制面板卸载的时候反应很慢,最后也是提示这个

硬件基础知识——自学习梳理

计算机存储分为闪存和永久性存储。 硬盘(永久存储)主要分为机械磁盘和固态硬盘。 机械磁盘主要靠磁颗粒的正负极方向来存储0或1,且机械磁盘没有使用寿命。 固态硬盘就有使用寿命了,大概支持30w次的读写操作。 闪存使用的是电容进行存储,断电数据就没了。 器件之间传输bit数据在总线上是一个一个传输的,因为通过电压传输(电流不稳定),但是电压属于电势能,所以可以叠加互相干扰,这也就是硬盘,U盘

Eclipse+ADT与Android Studio开发的区别

下文的EA指Eclipse+ADT,AS就是指Android Studio。 就编写界面布局来说AS可以边开发边预览(所见即所得,以及多个屏幕预览),这个优势比较大。AS运行时占的内存比EA的要小。AS创建项目时要创建gradle项目框架,so,创建项目时AS比较慢。android studio基于gradle构建项目,你无法同时集中管理和维护多个项目的源码,而eclipse ADT可以同时打开