Small插件源码分析

导读

目标读者
使用方式
机制原理
关键概念
关键流程
涉及文件
Hook点列表
Small源码解析参考
Android源码参考

目标读者:

1.快速了解small原理机制的人
2.自己,知识梳理和记忆
对于想学代码解析的同学,这里可以当作一个概要或引言,相信会对你的学习起到事半功倍的效果。也可以读完代码后来这里理理整体思路。(其实,对于开源软件或者非自己写的项目,我推荐的学习路径是先了解整体机制,核心重点,然后再研读代码。不过说实话,符合这个思路的文章不多,当然,也可能是我没找到。)
幸运的是,你遇到了^_^

使用方式:

建议还是简单看一下使用方式,先有一个感觉。
大家直接看官方文档吧,还是比较简单的:http://code.wequick.net/Small/cn/quickstart

机制原理:

Small主要通过Hook Android framework层的关键逻辑,达到将插件资源和代码无缝的集成到主工程中。主要要解决的是三个问题:1. 代码加载;2. 资源加载;3. android既有机制支持,如Activity。

主要解决方案是:

1. 代码加载的解决方案:
Hook DexClassLoader:加入插件代码,包括java和native
Hook PathClassLoader:低版本的native lib是通过pathclassLoader。高版本都在DexClassLoader中
2. 资源加载的解决方案:
Hook AssetManager:加入插件资源
3. android既有机制支持的解决方案:
Hook Instrumentation: 启动startActivity时将目标realactivity替换成占桩的stubactivity,利用AMS机制进行正常的启动activity流程。
Hook Handler.Callback:将AMS调用的stubactivity替换成realactivity。与前面的instrumentation配合,完成AMS的欺骗。
Hook TaskStackBuilder: pendingintent的支持。因为这个启动不是通过instrumentation的startactivity


small插件hook关键点,绿色的是hook关键点

关键概念:

为了体系化的呈现热更新,让接入方很容易的接入,small作者提出了几个概念来组织整套逻辑。
Small:外观类,使用入口。比如:preSetup(), setup()这两个是环境配置,关键就是前面hook点的实施;openUri():activity跳转
Bundle:这个类比较杂,建议分成两部分看。
一部分概念是每个插件的映射体,即每一个module都映射成了一个bundle,所以bundle里存着每一个apk的相关信息,比如manifest,dex,native lib等;
另一部分对应整个流程的管理,比如small的presetup和setup与bundlerlauncher的串联都是在这个逻辑流程里。
这里还要说一下bundle.json,这个是bundle加载的入口,也是bundle和module的映射引导逻辑。bundle从bundle.json中找到各个module,并对module进行解析。同时bundle.json也规定了不同模块的跳转逻辑。
BundleLauncher:每一个bundler的处理者。因为bundle对应着各种类型,比如host apk,plugin apk,
BundleParser:manifest的解析者。small中manifest的解析是自己做的,逻辑在这个类里面。
ActivityLauncher->BundlerLauncher:host apk中activity的launcher
ApkBundleLauncher->SoBundlerLauncher->BundlerLauncher: 插件中activity的launcher
WebBundleLauncher->SoBundlerLauncher->BundlerLauncher:插件中html的launcher

各关键概念之间的关系:

Small是整体入口,通过bundle加载bundle.json并管理整个插件的hook,加载,解析流程。
Small也是跳转的入口,内部通过bundle.json制定的规则,找到对应的bundlelauncher,然后启动跳转

关键流程:

ActivityLauncher的流程

ActivityLauncher的流程

ApkBundleLauncher的主要流程

ApkBundleLauncher的流程

OpenUri的流程


openUri的流程

涉及文件:

bundle.json的读取顺序
Cache: SharedPreference: small.xml(key=bundle.json, value=*)
Patch: data/user/0/packagename/files/bundle.json
Asset: assets/bundle.json

bundle source:支持打包到asset中,也支持so模式(系统安装时会自动帮你加载,帮你提速)
asset base file: /data/user/0/packagename/app_small_base/pkg.apk
asset patch file: /data/user/0/packagename/app_small_patch/pkg.apk 
library base file: /data/app/packagename/lib/arbi/libpkg.so
library patch file: /data/app/packagename/app_small_patch/libpkg.so

bundle target:
ApkBundleLauncher extract path: /data/user/0/packagename/files/storage/pkg/

Hook 点列表:

DexClassLoader//Java Class
PathClassLoader//Native library
AssetManager//Resource中AssetManager的path
Instrumentation//ActivityThread中的mInstrumentation

ClassLoader Hook

V9

DexClassLoader.java(dalvik/src/main/java/dalvik/system/DexClassLoader.java)
mFiles(File[])
mPaths(String[])
mZips(ZipFile[])
mDexs(DexFile[])
PathClassLoader.java
libraryPathElements(List<String>)

V14

DexClassLoader.java
pathList(DexPathList).dexElements[](dalvik.system.DexPathList$Element[])
pathList(DexPathList).nativeLibraryDirectories(File[])

v23

DexClassLoader.java
pathList(DexPathList).dexElements[](dalvik.system.DexPathList$Element[])
pathList(DexPathList).nativeLibraryDirectories(File[])
pathList(DexPathList).nativeLibraryPathElements(alvik.system.DexPathList$Element[])

v26

DexClassLoader.java
pathList(DexPathList).dexElements[](dalvik.system.DexPathList$Element[]//构造方法有变化)
pathList(DexPathList).nativeLibraryDirectories(File[])
pathList(DexPathList).nativeLibraryPathElements(dalvik.system.DexPathList$NativeLibraryElement[])

AssetManager Hook

获得AssetManager:
1.<24  直接new AssetManager
2.>=24 从application.getAssets
增加新的asset path
1.<28 AssetManager.addAssetPath
2.>=28 AssetManager.addAssetPaths
AssetManager.ensureStringBlocks

ResourcesManager.java
mActiveResources(ArrayMap<ResourcesKey, WeakReference<Resources> >)
ActivityThread.java
mActiveResources(ArrayMap<ResourcesKey, WeakReference<Resources> >)

替换每一个Resources里的resource为前面处理后的AssetManager
resources.updateConfiguration(resources.getConfiguration(), resources.getDisplayMetrics());
v>21:resources.mTypedArrayPool(android.util.pools.SynchronizedPool<TypedArray>).acquire()

这是hook ResourceManager中的resource,除此之外,还需要hook Activity的resource
activity.getResources().mResourcesImpl(android.content.res.ResourcesImpl.java).mAssets(AssetManager)

ActivityThread Hook

获得ActivityThread的两种方式:
android.app.ActivityThread.currentActivityThread()
context.@mLoadedApk.@mActivityThread

android.app.ActivityThread.currentActivityThread()
android.app.ActivityThread.currentApplication()
android.app.ActivityThread$ApplicationThread.scheduleRelaunchActivity()
android.app.ActivityThread$CreateServiceData.info
android.app.ActivityThread$ActivityClientRecord.intent
android.app.ActivityThread$ActivityClientRecord.activityInfo

Launch Activity之前update ActivityInfo,update Theme,update Resource

Small源码解析参考:

源码:
https://github.com/wequick/Small
如果是新版android studio,记得下载这个branch:
https://github.com/wequick/Small/tree/support-android-gradle-3.0
源码解析文章比较多,我这里就不罗嗦了,大家自己查阅即可。
https://www.jianshu.com/p/3724ee20ad99
https://www.jianshu.com/p/b2843950aae8
如果之前没接触过hook,可以看这边文章了解一下
https://www.cnblogs.com/codingblock/p/6580364.html

Android源码参考:

http://androidxref.com/2.3.7/xref/frameworks/base/core/java/android/content/pm/PackageParser.java
http://androidxref.com/2.3.7/xref/libcore/dalvik/src/main/java/dalvik/system/PathClassLoader.java

http://androidxref.com/7.0.0_r1/xref/libcore/dalvik/src/main/java/dalvik/system/DexClassLoader.java
http://androidxref.com/7.0.0_r1/xref/libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
http://androidxref.com/7.0.0_r1/xref/libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
http://androidxref.com/7.0.0_r1/xref/frameworks/base/core/java/android/app/ActivityThread.java
http://androidxref.com/7.0.0_r1/xref/frameworks/base/core/java/android/app/ResourcesManager.java
http://androidxref.com/7.0.0_r1/xref/frameworks/base/core/java/android/content/res/Resources.java
http://androidxref.com/7.0.0_r1/xref/frameworks/base/core/java/android/content/res/ResourcesImpl.java
http://androidxref.com/7.0.0_r1/xref/frameworks/base/core/java/android/content/res/AssetManager.java
http://androidxref.com/7.0.0_r1/xref/frameworks/base/core/java/android/content/pm/ApplicationInfo.java
http://androidxref.com/7.0.0_r1/xref/frameworks/base/core/java/android/content/pm/PackageInfo.java
http://androidxref.com/7.0.0_r1/xref/frameworks/base/core/java/android/content/pm/ActivityInfo.java

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 222,252评论 6 516
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,886评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,814评论 0 361
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,869评论 1 299
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,888评论 6 398
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,475评论 1 312
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,010评论 3 422
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,924评论 0 277
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,469评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,552评论 3 342
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,680评论 1 353
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,362评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,037评论 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,519评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,621评论 1 274
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,099评论 3 378
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,691评论 2 361