Android 插件化-程序员宅基地

技术标签: android  

efab53147c63400897d40061030b8733.jpg

 1.插件化

插件可以理解为免安装的apk,而支持插件的app称为宿主。

一个apk通常会包含如下几个部分:

classes.dex:Java代码字节码

res:资源文件

lib:so 文件

assets:静态资产文件

AndroidManifest.xml:清单文件

Android系统在打开应用之后,只是开辟进程,然后使用ClassLoader加载classes.dex至进程中,执行对应的组件而已。

 

插件化让apk中的代码(主要是指Android组件)能够免安装运行,这样带来很多好处:

①减少安装Apk的体积、按需下载模块

②动态更新插件

③宿主和插件分开编译,提升开发效率

④解决方法数超过65535的问题

 

插件化与热修复:

插件化和热修复不是同一个概念,虽然在技术实现的角度来说,它们都是从系统加载器的角度出发,通过“欺骗”Android系统的方式来让宿主正常的加载和运行插件/补丁中的内容,但是它们的出发点是不同的。插件化是把需要实现的模块或功能独立出来,减少宿主的规模,当需要使用到相应功能时再去加载相应的模块。热修复则是从修复bug的角度出发,强调的是在不需要二次安装应用的前提下修复已知的bug。

 

插件化与组件化:

组件化是将一个App分成多个模块,每个模块都是一个组件(module),开发过程中可以让这些组件相互依赖或独立编译、调试部分组件,但是这些组件最终会合并成一个完整的apk发布到应用市场。组件化开发有一个弊端 ,就是多个模块必须是并发开发 , 模块之间相互依赖 , 如果修改了一个模块 ,那就必须重新打包 。而插件化是将整个app拆分成若干模块,其中有1个宿主模块、若干插件模块 。宿主模块和插件模块可以分开进行编译,二者之间互不影响,各个模块可以并发进行开发。打包时,将宿主模块和插件模块分开进行打包,即宿主模块和插件模块都各自是一个单独apk安装文件 。只需发布宿主Apk到应用市场,插件Apk通过动态按需下发到宿主Apk,实现宿主模块动态更新插件。

 

现在大型Android项目基本都是组件化+插件化开发。项目架构上都是组件化的框架,某些修改频繁的Module模块设置成插件模块,编译成独立的apk文件,以插件的形式进行部署,供宿主模块调用。应用运行时,点击启动某个插件apk中的界面,首先下载对应的插件apk文件,将其放在内置存储区中,然后加载该apk文件,主要是类加载器加载dex文件中的Class字节码数据。

插件包apk是运行后才加载的,将该apk文件中的dex加载到内存之后,其中的Activity、Service等组件类与当前应用运行的组件是有区别的:

①插件包apk中的类是通过DexClassLoader加载到内存中的,仅仅加载了字节码数据,因此插件包中的组件类没有上下文,组件的初始化、赋予上下文等必须由开发者手动完成,应用系统是不管的。而应用中的Activity、Service等组件初始化不需要开发者干预,都是由系统完成的,这些组件都在清单文件中注册过了,系统按照清单初始化相关组件。

②加载的插件中的Activity并不是Activity组件,只是有Activity方法、成员、继承关系的字节码类。而且插件Activity类没有生命周期,不在AMS管理范畴内。因此这些通过插件包加载进来的组件类的生命周期,需要开发者进行管理。


2.插件化实现步骤

①使用DexClassLoader加载插件apk中Activity对应的Class字节码类对象 。

要让插件Apk真正运行起来,首先要找到插件apk的存放位置,然后解析加载apk里面的代码。由于插件是未安装的apk,系统不会处理其中的类,所以需要使用ClassLoader加载Apk,然后反射里面的代码。

首先需要创建DexClassLoader实例,调用其loadClass(className)方法就可以加载插件中的类了。然后通过反射执行类的方法:

Class loadClass = pluginClassLoader.loadClass( activityName);

loadClass.getMethod("test",null).invoke(loadClass);

这个过程叫做ClassLoader注入。完成注入后,所有来自宿主的类使用宿主的ClassLoader进行加载,所有来自插件Apk的类使用插件ClassLoader 进行加载。由于ClassLoader的双亲委派机制,实际上系统类不会受ClassLoader的类隔离机制影响,这样宿主apk就可以在宿主进程中使用来自于插件的组件类了。

②为加载进来的Activity类注入上下文并管理生命周期,让系统能调用插件apk中的组件。

Android系统中四大组件是需要在系统中注册的,并且由系统管理生命周期,具体是在Android系统的AMS和PMS中注册,四大组件的解析和启动都需要依赖AMS和PMS。而插件是动态加载的,所以插件中的四大组件不可能注册到宿主的Manifest文件中,因此不能和系统直接进行交互。 而且仅仅构造出四大组件的实例是没用的,还需要管理组件的生命周期。

插件化如何支持组件生命周期的管理,大致分为两种方式:运行时容器技术(ProxyActivity代理)和预埋StubActivity,hook系统启动Activity的过程。

以运行时容器技术为例,就是在宿主apk中预埋一些空的Android组件。比如Activity,可以在宿主中预置一个ContainerActivity extends Activity ,并且在AndroidManifest.xml中注册它。这样,ContainerActivity就作为插件Activity的容器,它从Intent接收插件的不同信息,如pluginName、pluginApkPath、pluginActivityName等。当ContainerActivity启动时,就加载插件的 ClassLoader、Resource,并反射pluginActivityName对应的Activity类。当完成加载后,ContainerActivity要做两件事:

1)转发所有来自系统的生命周期回调至插件Activity

2)接受插件Activity的系统方法调用,并转发回系统

第一步可以通过复写ContainerActivity的生命周期方法来完成,而第二步需要定义一个 PluginActivity,然后在编写插件apk中的Activity 组件时,需要继承自PluginActivity。

大概原理就是这么简单,启动插件组件需要依赖容器,容器负责加载插件组件并且完成双向转发,转发来自系统的生命周期回调至插件组件,同时转发来自插件组件的系统调用至系统。

③使用AssetManager将插件apk中的资源主动加载进来 

这一步就是资源注入,资源注入主要依靠两个接口:

1)PackageManager.getPackageArchiveInfo:根据apk路径解析一个未安装Apk的PackageInfo

2)PackageManager.getResourcesForApplication:根据ApplicationInfo创建一个Resources实例

可以在ContainerActivity的onCreate中加载插件apk的时候,用这两个方法创建出一份插件资源实例。具体就是先用 PackageManager.getPackageArchiveInfo拿到插件apk的PackageInfo,有了PacakgeInfo之后就可以组装一份ApplicationInfo,然后通过 PackageManager.getResourcesForApplication来创建资源实例。大概代码像这样:

PackageManager packageManager = getPackageManager();

PackageInfo packageArchiveInfo = packageManager.getPackageArchiveInfo(

    pluginApkP

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/zenmela2011/article/details/126304850

智能推荐

pic秒表c语言程序,PIC单片机C语言编程实例三-第7章秒表.doc-程序员宅基地

文章浏览阅读91次。PIC单片机C语言编程实例三-第7章秒表.docPAGEPAGE 133第7章 秒 表7.2.2 程序清单该源程序已在实验板上调试通过,读者可直接引用,并可利用软件编程的灵活性,加以拓展,实现更为复杂的功能。#include#include //此程序实现计时秒表功能,时钟显示范围00.00~99.99秒,分辨度:0.01秒unsigned chars0,s1,s2,s3;//定义0.0..._pic跑表启动条件

Java调用Groovy脚本_groovy evaluate-程序员宅基地

文章浏览阅读2.1k次。概述 在Java和Groovy的结合中,经常会碰到需要从Java代码中调起一个写好的Groovy脚本,我们可以通过GroovyShell来实现。其中最重要的就是GroovyShell和Binding两个类,其中GroovyShell可以调起一个Groovy脚本,而Binding可以向脚本里面传递参数。简单示例//通过Binding向要执行的groovy脚本传递变量 Bindi..._groovy evaluate

服务器发送信息给arduino,arduino通过esp8266模块发送数据到云服务器-程序员宅基地

文章浏览阅读1.4k次,点赞2次,收藏25次。arduino通过esp8266模块发送数据到云服务器我是代码小白,一个正在做毕设的秃头少年。鄙人拙作,有不当之处,还请指教。最近买了一套arduino设备,打算做一个物联网设备小玩意,可是怎么把数据上传到云服务器可愁坏我了。通过对比实验,我决定用esp8266wifi模块进行通信。云服务器的话,我现在还没写相应的代码,所以先用Onenet平台进行配置。Onenet平台进行配置1.进入Onenet..._esp8266 arduino wifi发送数据

latex h t b p是什么意思_latex htpb-程序员宅基地

文章浏览阅读1.4w次,点赞9次,收藏17次。常用选项[htbp]是浮动格式:『h』当前位置。将图形放置在正文文本中给出该图形环境的地方。如果本页所剩的页面不够,这一参数将不起作用。『t』顶部。将图形放置在页面的顶部。『b』底部。将图形放置在页面的底部。『p』浮动页。将图形放置在一只允许有浮动对象的页面上。在table或者figure 后加 [!htb] 是系统忽略“美学”标准,把表格和图片插入到你的代码中,是动的,但是不加感叹号,它就是按顺序选择h(此处),t(上方),b(下方),所以为了让图片随着你的代码移动,最好加一个[!htb]_latex htpb

【转载】linux下的usb抓包方法-程序员宅基地

文章浏览阅读67次。1 linux下的usb抓包方法1、配置内核使能usb monitor:make menuconfigDevice Drivers --> USB Support --> USB Monitor --> Sel..._linux安装tcpdump 查看usb

计算机组成pc em ir,计算机组成 课程设计报告.doc-程序员宅基地

文章浏览阅读164次。计算机组成 课程设计报告计算机组成原理课程设计报告姓 名:班 级:学 号:指导老师:2016年 6月31日目 录第一章 背景知识与课设任务概述11.1课设目的11.2课设任务11.2111.2211.2321.2421.252第二章 课设内容32.1指令的执行流程32.1.132.1.242.1.352.2存储器62.2.162.3运算器72.3.172.4硬件系统组成122.4..._计算机组成课程设计报告

随便推点

Hamburgers 二分答案_hamburger题解-程序员宅基地

文章浏览阅读5.2k次。题目大意:有一种汉堡,用B、S、C三种原料做成,现在告诉你当前有的B、S、C的个数,到商店买的B、S、C的单价(商店无限供应这三种原料),还有你拥有的钱。问最多能做多少个汉堡。刚开始我还以为是模拟,先把能用的用完,再去买。但是写了半天写不下去了,找了一下题解才发现是二分答案板子题。发现自己对二分还是不是很敏感。AC代码://https://blog.csdn.net/hesorche..._hamburger题解

ubuntu下安装uhd+gnuradio_无法定位软件包 libuhd003-程序员宅基地

文章浏览阅读1.9k次。提示:安装uhd+gnuradio实际上并不难,只是实际安装的时候,作为新手经常会因为缺乏相关知识而踩不少坑,以下是我踩坑安装的一些记录。gnuradio+uhd安装过程ubuntu下安装uhd+gnuradioExample: For UHD 3.9.5:Example: For UHD 3.14.0.0win10下安装ubuntu双系统使用usrpb210ubuntu18.04安装方法有两种,一种是使用已经编译好的二进制码,缺点是版本通常比较旧,但学习usrp也不需要太新的版本,另外,这种_无法定位软件包 libuhd003

Awk命令详解_在linux系统中,awk允许进行多种测试。作为样式匹配,还提供了模式匹配表达式,以下-程序员宅基地

文章浏览阅读3.1k次,点赞4次,收藏45次。awk文本和数据进行处理的编程语言awk 是一种编程语言,用于在linux/unix下对文本和数据进行处理。数据可以来自标准输入(stdin)、一个或多个文件,或其它命令的输出。它支持用户自定义函数和动态正则表达式等先进功能,是linux/unix下的一个强大编程工具。它在命令行中使用,但更多是作为脚本来使用。awk有很多内建的功能,比如数组、函数等,这是它和C语言的相同之处,灵活性是awk最大的优势。awk命令格式和选项语法形式awk [options] ‘script’ va_在linux系统中,awk允许进行多种测试。作为样式匹配,还提供了模式匹配表达式,以下

QT5实现简单的TCP通信_qt tcpsocket 同步接收-程序员宅基地

文章浏览阅读8.3w次,点赞115次,收藏776次。这段时间用到了QT的TCP通信,做了初步的学习与尝试,编写了一个客户端和服务器基于窗口通信的小例程。使用QT的网络套接字需要.pro文件中加入一句:QT += network一、客户端1、客户端的代码比服务器稍简单,总的来说,使用QT中的QTcpSocket类与服务器进行通信只需要以下5步:(1)创建QTcpSocket套接字对象socket = new Q..._qt tcpsocket 同步接收

基于相空间重构的混沌背景下微弱信号检测算法matlab仿真,对比SVM,PSO-SVM以及GA-PSO-SVM_微弱信号检测 仿真-程序员宅基地

文章浏览阅读500次。支持向量机(Support Vector Machine,SVM)是一种用于分类和回归的机器学习方法,其原理基于寻找一个最优超平面(或者曲线在非线性情况下)来划分不同类别的数据点。SVM 的目标是找到一个能够最大化不同类别之间的间隔(margin)的超平面,从而在未知数据上取得良好的泛化能力。SVM 的目标是找到一个超平面,使得距离超平面最近的数据点(支持向量)到超平面的距离(间隔)最大。_微弱信号检测 仿真

解决atom上gcc无法识别 <ros/ros.h>_ros类型无法识别-程序员宅基地

文章浏览阅读335次。找到插件 linter-gcc在Settings里找到GCC Inlude Path和GCC -isystemInclude Paths两栏填入ros的Include路径 /opt/ros/noetic/include注意ros版本不同路径可能有所不同,可以在先文件夹里找一找确定一下_ros类型无法识别