第1页
Small:插件化轻巧之道
林光亮
第2页
0x00
诞生
0x01
轻
0x02
巧
0x03
TODO
第3页
诞生
模块1
模块2
模块3
模块4
模块5
模块6
第4页
诞生
分析支付宝客户端插件机制 @唐巧-‐猿题库
手机淘宝客户端架构探索实践 @于佳-‐阿里
第5页
诞生
+ iOS
HTML
Android
Bundle
L auncher
第6页
诞生
Android
Dynamic-‐Load-‐APK @任玉刚-‐百度
Direct-‐Load-‐APK @罗迪-‐高中生
第7页
诞生 Host
模块1 模块2
模块3
第8页
诞生 Host
模块1
公共 模块
模块3
第9页
诞生
使用public.xml 锁定公共资源ID
公共 模块
Host
DLA 改进
打通宿主与插件的 资源与代码共享
模块1 模块3
第10页
诞生
使用public.xml 锁定公共资源ID
公共 模块
Host
DLA 改进
打通宿主与插件的 资源与代码共享
模块1 模块3
第11页
诞生
Android-‐Plugin-‐Framework @Limpoxe
ACDD @Bunny Blue
第12页
诞生 aapt
模块1 0x7A
模块2 0x7B 模块3 0x7C
第13页
诞生 aapt
模块1 0x7A
模块2 0x7B 模块3 0x7C
第14页
轻
BA C
A
轻盈产出 @Compile-‐time
A A’
B
轻度Hook @Run-‐time
第15页
轻/轻盈产出 APK
第16页
轻/轻盈产出
AM.xml classes.dex resources.arsc
res/*
第17页
轻/轻盈产出
拆分粒度/方案
文本
AM$1
文件(*.jar)
dex$1
二进制
arsc$1
文件(*.xml/png)
res$1
AM.xml classes.dex resources.arsc
res/*
AM$2 dex$2 arsc$2 res$2
第18页
轻/轻盈产出/arsc格式
0000000: 0200 0c00 4804 0000 0100 0000 0100 1c00 0000010: 8c00 0000 0400 0000 0000 0000 0001 0000 0000020: 2c00 0000 0000 0000 0000 0000 2500 0000
0000030: 4800 0000 5700 0000 2222 7265 732f 6d69 0000040: 706d 6170 2d68 6470 692d 7634 2f69 635f 0000050: 6c61 756e 6368 6572 2e70 6e67 0020 2072 0000060: 6573 2f6d 6970 6d61 702d 6864 7069 2d76 0000070: 342f 6963 5f70 6c75 6769 6e2e 706e 6700
0000080: 0c0c 4c65 6172 6e69 6e67 4172 7363 0006 0000090: 0650 6c75 6769 6e00 0002 2001 b003 0000 00000a0: 7f00 0000 6e00 6500 7400 2e00 7700 6500 00000b0: 7100 7500 6900 6300 6b00 2e00 6100 7200 00000c0: 7300 6300 0000 0000 0000 0000 0000 0000
00000d0: 0000 0000 0000 0000 0000 0000 0000 0000 00000e0: 0000 0000 0000 0000 0000 0000 0000 0000 00000f0: 0000 0000 0000 0000 0000 0000 0000 0000 0000100: 0000 0000 0000 0000 0000 0000 0000 0000 0000110: 0000 0000 0000 0000 0000 0000 0000 0000
0000120: 0000 0000 0000 0000 0000 0000 0000 0000 0000130: 0000 0000 0000 0000 0000 0000 0000 0000
....H........... ................ ,...........%...
H...W...""res/mi pmap-hdpi-v4/ic_ launcher.png. r es/mipmap-hdpi-v 4/ic_plugin.png.
..LearningArsc.. .Plugin... ..... ....n.e.t...w.e. q.u.i.c.k...a.r. s.c.............
................ ................ ................ ................ ................
................ ................
第19页
轻/轻盈产出/arsc格式
0000000: 0200 0c00 4804 0000 0100 0000 0100 1c00 0000010: 8c00 0000 0400 0000 0000 0000 0001 0000 0000020: 2c00 0000 0000 0000 0000 0000 2500 0000
0000030: 4800 0000 5700 0000 2222 7265 732f 6d69 0000040: 706d 6170 2d68 6470 692d 7634 2f69 635f 0000050: 6c61 756e 6368 6572 2e70 6e67 0020 2072 0000060: 6573 2f6d 6970 6d61 702d 6864 7069 2d76 0000070: 342f 6963 5f70 6c75 6769 6e2e 706e 6700
0000080: 0c0c 4c65 6172 6e69 6e67 4172 7363 0006 0000090: 0650 6c75 6769 6e00 0002 2001 b003 0000 00000a0: 7f00 0000 6e00 6500 7400 2e00 7700 6500 00000b0: 7100 7500 6900 6300 6b00 2e00 6100 7200 00000c0: 7300 6300 0000 0000 0000 0000 0000 0000
00000d0: 0000 0000 0000 0000 0000 0000 0000 0000 00000e0: 0000 0000 0000 0000 0000 0000 0000 0000 00000f0: 0000 0000 0000 0000 0000 0000 0000 0000 0000100: 0000 0000 0000 0000 0000 0000 0000 0000 0000110: 0000 0000 0000 0000 0000 0000 0000 0000
0000120: 0000 0000 0000 0000 0000 0000 0000 0000 0000130: 0000 0000 0000 0000 0000 0000 0000 0000
....H........... ................ ,...........%...
H...W...""res/mi pmap-hdpi-v4/ic_ launcher.png. r es/mipmap-hdpi-v 4/ic_plugin.png.
..LearningArsc.. .Plugin... ..... ....n.e.t...w.e. q.u.i.c.k...a.r. s.c.............
................ ................ ................ ................ ................
................ ................
第20页
轻/轻盈产出/arsc格式
hex(LE) 小端代码
struct 数据结构
0200 ResTable_header
0100 ResStringPool_header
0002 ResTable_package
0100 ResStringPool_header
0100 ResStringPool_header
0202 ResTable_typeSpec
0102 ResTable_type
0200 0c00 4804 0000 0100 0000 0100 1c00 8c00 0000 0400 0000 0000 0000 0001 0000 2c00 0000 0000 0000 0000 0000 2500 0000
4800 0000 5700 0000 2222 7265 732f 6d69 706d 6170 2d68 6470 692d 7634 2f69 635f 6c61 756e 6368 6572 2e70 6e67 0020 2072 6573 2f6d 6970 6d61 702d 6864 7069 2d76 342f 6963 5f70 6c75 6769 6e2e 706e 6700
0c0c 4c65 6172 6e69 6e67 4172 7363 0006 0650 6c75 6769 6e00 0002 2001 b003 0000 7f00 0000 6e00 6500 7400 2e00 7700 6500 7100 7500 6900 6300 6b00 2e00 6100 7200
7300 6300 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
第21页
轻/轻盈产出/arsc格式
hex(LE) 小端代码
struct 数据结构
0200 ResTable_header
0100 ResStringPool_header
0002 ResTable_package
0100 ResStringPool_header
0100 ResStringPool_header
0202 ResTable_typeSpec
0102 ResTable_type
table: { package: { id: 0x7F, name: "net.wequick.arsc" }, strings: [ "res/mipmap-hdpi-v4/ic_launcher.png", "res/mipmap-hdpi-v4/ic_plugin.png", "LearningArsc", "Plugin" ], typeStrings: [ "attr", "mipmap", "string", "style" ], keyStrings: [ "ic_launcher", "ic_plugin", "app_name", "s_plugin", "AppTheme", "PluginTheme" ], typeSpecs: [ { types: [] }, { types: [ Configs@ic_launcher, Configs@ic_plugin ] }, { types: [ Configs@app_name, Configs@s_plugin ] }, { types: [ Configs@AppTheme, Configs@PluginTheme ] } ]
}
第22页
轻/轻盈产出/arsc格式
资源ID 0x
PP 包ID
7F
TT NNNN 类型ID 项目ID
02 0000
table: {
package: { id: 0x7F, name: "net.wequick.arsc" },
strings: [
"res/mipmap-hdpi-v4/ic_launcher.png",
"res/mipmap-hdpi-v4/ic_plugin.png",
"LearningArsc",
"Plugin" ],
03 04
typeStrings: [ "attr", "mipmap", "string", "style" ],
keyStrings: [
"ic_launcher", "ic_plugin", "app_name",
"s_plugin", "AppTheme", "PluginTheme"
],
typeSpecs: [
01 { types: [] },
{ types: [ CCoonnffigigss@@icic__lalauunncchheer,r Configs@ic_plugin ] },
03 { types: [ Configs@app_name, Configs@s_plugin ] }, 04 { types: [ Configs@AppTheme, Configs@PluginTheme ] }
]
}
第23页
轻/轻盈产出/arsc分离
想象中最简单的分离方式
host (0x7f) |-- mipmap (02) | |-- ic_launcher (0000) | `-- values |-- strings.xml (03) | |-- app_name (0000) | `-- syles.xml (04) |-- AppTheme (0000)
plugin (0x7f)
|-- mipmap (02) | |--padding_mipmap_0000 | `-- ic_plugin (0001)
`-- values |-- strings.xml (03) | |-- padding_string_0000 | `-- s_plugin (0001) `-- syles.xml (04) |-- padding_style_0000 `-- PluginTheme (0001)
存在问题:必须补齐资源(输出变大)、只能分离一个插件
第24页
轻/轻盈产出/arsc分离
实践中最极致的分离方式
host (0x7f) |-- mipmap (02) | |-- ic_launcher (0000) | `-- values |-- strings.xml (03) | |-- app_name (0000) | `-- syles.xml (04) |-- AppTheme (0000)
plugin ( 0x7e )
|-- mipmap (02)
|| | `-- ic_plugin (0000 )
`-- values |-- strings.xml (03)
|| | `-- s_plugin ( 0000 ) `-- syles.xml (04)
| `-- PluginTheme ( 0000 )
第25页
轻/轻盈产出
HOST
0x7e
0x7d
7c
第26页
轻/轻度Hook
方法A Hook A
方法B
一个对象
我是一个Hook
我重写一个「对象」的「方法」
让她忘了从前
第27页
轻/轻度Hook
方法A Hook A
方法B
一个对象
但是首先 我要找到她
在我的「进程」里 她「静态」的坐着 她的方法向我「开放」
第28页
轻/轻度Hook
我的「进程」
com.user.galen
启动 插件Activity
第29页
轻/轻度Hook
我的「进程」
com.user.galen
启动 插件Activity
系统进程
system.process
Intent解析 任务栈调度 Activity栈调度
我的「进程」
com.user.galen
实际启动 插件Activity
第30页
轻/轻度Hook
我的「进程」
com.user.galen
启动 插件Activity
系统进程
system.process
Intent解析 任务栈调度 Activity栈调度
我的「进程」
com.user.galen
实际启动 插件Activity
第31页
轻/轻度Hook
我的「进程」
com.user.galen
启动 插件Activity
Hook 伪装宿主
系统进程
system.process
Intent解析 任务栈调度 Activity栈调度
我的「进程」
com.user.galen
实际启动 插件Activity
第32页
轻/轻度Hook
我的「进程」
com.user.galen
启动 插件Activity
Hook 伪装宿主
系统进程
system.process
Intent解析 任务栈调度 Activity栈调度
我的「进程」
com.user.galen
Hook 还原插件
实际启动 插件Activity
第33页
轻/轻度Hook
实际启动 插件Activity
Small Droid Plugin
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// 创建Activity if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity) ; } java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent);
Android-PluginFramework ACDD
// 绑定Context Context appContext = createBaseContextForActivity(r, activity);
activity.attach(appContext, this, getInstrumentation(), ...); // 设置主题
int theme = r.activityInfo.getThemeResource(); if (theme != 0) {
activity.setTheme(theme);
}
Dynamic-Load-APK Direct-Load-APK
// 触发onCreate mInstrumentation.callActivityOnCreate(activity, r.state); }
第34页
轻/轻度Hook
插件方案
代表框架
包数
Droid Plugin
1/插件
完全隔离 Dynamic-Load-APK 1
宿主插件 两两融合
除主题外 完全融合
完全融合
Direct-Load-APK Android-Plugin-
Framework ACDD
Small
1 1 1 1
类加载器 资源管理器
个数
个数
Context
1/插件
1/插件
1/插件
1/插件 1/插件 1/插件 1 1
1/插件 1/插件 1/插件 1 1
1/插件Activity 1/插件Activity 1/插件Activity 1/插件Activity 1
第35页
巧
IDE友好 @Debug
模块变身 @Release
第36页
巧/IDE友好
支持创建插件模块 支持编译插件模块 支持插件模块间依赖
支持联合调试
第37页
巧/IDE友好
app
app.*
web.*
lib.* lib.*
第38页
巧/IDE友好
vendor
app
vendor
app.*
lib.*
web.*
vendor
lib.*
vendor
lib.*
第39页
巧/模块变身 模块是开发态,插件是目标态。
模块
开发态
目标态
app.* lib.* [other].*
Application模块 可以依赖其他模块、可以独立运行
Library模块 可以被app.*依赖
Application模块 可以独立运行
带代码、资源的插件 带代码、资源的插件
仅含assets的插件
转换难点
AAR(代码/资源) 剥离
资源ID锁定
第40页
巧/模块变身
dependencies { provided fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:23.2.1' compile 'com.android.support:design:23.2.1'
}
app.main `-- build/intermediates/exploded-aar `-- com.android.support/appcompat-v7/23.2.1
classes.jar res/*
*.class
dex
classes.dex
res/*
aapt
arsc res/*
AAR
第41页
巧/模块变身
dependencies { provided fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:23.2.1' compile 'com.android.support:design:23.2.1'
}
app.main `-- build/intermediates/exploded-aar `-- com.android.support/appcompat-v7/23.2.1
AAR
classes.jar res/*
*.class
dex
classes.dex
res/*
aapt
arsc res/*
Small
arsc res/*
第42页
总结
dependencies { provided fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:23.2.1'
compile 'com.android.support:design:23.2.1' }
app`-.-mbauinild/intermedi•ateIsD/exEp多lod e模d-a块ar `-- com.andro•id公.sup共por库t/app依com赖pat-v7/23.2.1
编译时分离
• PP分段 • AAR分离
• 并入宿主 • 轻度Hook
开发时聚合
classes.jar
*.class
dex 运行时融合 classes.dex
res/*
res/*
aapt
arsc Small arsc
res/*
res/*
第43页
TODO
• IDE多模块 • 公共库依赖
编译按时需分加离载
• PP分段 • AAR分离
开发时聚合
xib级别的warm swap
iOS插件化 App Store布
局
插件间依赖关系
• 并入宿主 • 轻度Hook
运行时融合
第44页
Q &A
Small@wequick GalenLin