您的位置:首页 > 移动开发 > Android开发

彻底掌握Android多分包技术MultiDex-用Ant和Gradle分别构建(一)

2017-01-29 12:11 351 查看
版权声明:本文为博主原创文章,未经博主允许不得转载;来自http://blog.csdn.net/mynameishuangshuai

目录(?)[+]

    Andrid多分包技术在大型项目编译方面起着至关重要的作用,作为一个高级开发者我们有必要掌握此技能,现在我带领大家统一学习此项技能,并教会大家分别使用Ant和Gradle构建。

什么是Dex

    Dex是Dalvik VM executes的全称,即Android Dalvik执行程序。在Android中单个Dex文件所能包含的最大方法数为65536,这包含Android
FrameWork、依赖的Jar包,以及应用本身的代码中所有的方法。

65536产生的原因

Android系统中,一个Dex文件中存储方法id用的是short类型数据,所以导致你的dex中方法不能超过65536
在2.3系统之前,虚拟机内存只分配了5M

多分包技术的应用

    一句话为了解决单个dex包65536方法数限制问题 

    针对于65536的问题,我们在应用层是无法改变Android系统的结构的,所以我们无法将数据类型从short改变为int或者其他类型,也就是说一个dex中的方法数不能超过65536是我们无法逾越的鸿沟,我们只能通过优化项目代码达到减少一个dex中的方法数的目的,但是随着时间的推移和功能的增加,总有一天还是会出现方法数超过65536的情况,因此根据谷歌官方建议,我们使用多分包技术。 

    其实我们日常使用的大多数软件都使用到了多分包技术,比如下面就是我们解压了一款知名应用的APK包,我们可以看到他们使用了多分包技术,APK中包含三个dex文件,分别是classes.dex,classes2.dex,classes3.dex



本篇博客首先给大家讲解使用ant构建。

Ant构建MultiDex

Ant是一种基于Java的build工具。理论上来说,它有些类似于(Unix)C中的make ,但没有make的缺陷。

(一)搭建Ant编译环境

1.首先下载Ant:http://ant.apache.org/bindownload.cgi



下载后,我们解压到指定路径,这里我解压到D盘



2.配置NDK环境变量

打开我的电脑–属性–高级–环境变量

新建系统变量ANT_HOME 

变量名:ANT_HOME 

变量值:D:\apache-ant-1.9.7



选择“系统变量”中变量名为“Path”的环境变量,双击该变量,把ANT安装目录的绝对路径,添加到Path变量的值中,并使用半角的分号和已有的路径进行分隔。 

变量名:Path 

变量值:%ANT_HOME%\bin;



完成以上操作后,ANT环境变量配置结束,我们测试环境变量的配置成功与否。在cmd命令行窗口输入“ant -version”,输出以下信息即为配置正确。如图:



(二)编写Ant构建脚本

通常我们的Ant构建文件都放在SDK根目录下的tools夹下,在里面我们找到ant目录,进去后找到buildxml文件。



这里我们可以把这个build.xml文件拷贝到项目目录中去,然后进行修改。



下面是我配置的build.xml源码
<?xml version="1.0" encoding="UTF-8"?>
<!-- 版权所有,未经同意请勿转载!猴子搬来的救兵 http://blog.csdn.net/mynameishuangshuai -->
<!-- project项目标签 -->
<project
name="MultiDex"
default="release" >

<!-- 项目编译环境配置 -->

<property
name="sdk-folder"
value="D:\adt-bundle-windows-x86_64-20140702\sdk" />

<property
name="platform-folder"
value="${sdk-folder}\platforms\android-20" />

<property
name="platform-tools-folder"
value="${sdk-folder}\build-tools\android-4.4W" />

<property
name="jdk-folder"
value="C:\Program Files\Java\jdk1.7.0_17" />

<property
name="android-jar"
value="${platform-folder}\android.jar" />

<property
name="tools.aapt"
value="${platform-tools-folder}/aapt.exe" />

<property
name="tools.javac"
value="${jdk-folder}\bin\javac.exe" />

<property
name="tools.dx"
value="${platform-tools-folder}\dx.bat" />

<property
name="tools.apkbuilder"
value="${sdk-folder}\tools\apkbuilder.bat" />

<property
name="tools.jarsigner"
value="${jdk-folder}\bin\jarsigner.exe" />

<!-- 项目输入目录配置 -->

<property
name="project-dir"
value="." />

<property
name="assets"
value="${project-dir}\assets" />

<property
name="res"
value="${project-dir}\res" />

<property
name="src"
value="${project-dir}\src" />

<property
name="libs"
value="${project-dir}\libs" />

<!-- 项目输出目录配置 -->

<property
name="bin"
value="${project-dir}\bin" />

<property
name="gen"
value="${project-dir}\gen" />

<property
name="manifest"
value="${project-dir}\AndroidManifest.xml" />
<!-- 生成文件放置地方 -->

<property
name="java-file-gen"
value="${gen}\com\castiel\demo\*.java" />

<property
name="java-file-src"
value="${src}\com\castiel\demo\*.java" />

<property
name="main-dex-name"
value="${bin}\classes.dex" />

<property
name="sub-dex-name"
value="${bin}\classes2.dex" />

<property
name="package-temp-name"
value="${bin}\${ant.project.name}.arsc" />
<!-- 未签名包 -->

<property
name="unsigned-apk-name"
value="${ant.project.name}_unsigned.apk" />

<property
name="unsigned-apk-path"
value="${bin}\${unsigned-apk-name}" />
<!-- 签名包 -->

<property
name="signed-apk-name"
value="${ant.project.name}.apk" />

<property
name="signed-apk-path"
value="${bin}\${signed-apk-name}" />
<!-- 密钥 -->

<property
name="keystore-name"
value="${project-dir}\castiel_key.keystore" />

<property
name="keystore-alias"
value="castiel" />

<property
name="main-dex-rule"
value="${project-dir}\main-dex-rule.txt" />

<taskdef resource="net/sf/antcontrib/antlib.xml" />

<!-- 初始化target -->

<target name="init" >

<echo message="init..." />

<delete includeemptydirs="true" >

<fileset dir="${bin}" >

<include name="**/*" >
</include>
</fileset>
</delete>

<mkdir dir="${bin}" />
</target>

<!-- 生成R.java类文件 -->

<target
name="gen-R"
depends="init" >

<echo message="Generating R.java from the resources." />

<exec
executable="${tools.aapt}"
failonerror="true" >

<!-- package表示打包 -->

<arg value="package" />

<arg value="-f" />

<arg value="-m" />

<arg value="-J" />

<arg value="${gen}" />

<arg value="-S" />

<arg value="${res}" />

<arg value="-M" />

<arg value="${manifest}" />

<arg value="-I" />

<arg value="${android-jar}" />
</exec>
</target>

<!-- 编译源文件生成对应的class文件 -->

<target
name="compile"
depends="gen-R" >

<echo message="compile..." />

<javac
bootclasspath="${android-jar}"
compiler="javac1.7"
destdir="${bin}"
encoding="utf-8"
includeantruntime="false"
listfiles="true"
target="1.7" >

<src path="${project-dir}" />

<classpath>

<!-- 引入第三方jar包所需要引用,用于辅助编译,并没有将jar打包进去。 -->

<fileset
dir="${libs}"
includes="*.jar" />
</classpath>
</javac>
</target>

<!-- 构建多分包dex文件 -->

<target
name="multi-dex"
depends="compile" >

<echo message="Generate multi-dex..." />

<exec
executable="${tools.dx}"
failonerror="true" >
<arg value="--dex" />
<arg value="--multi-dex" />
<!-- 多分包命令,每个包最大的方法数为10000 -->
<arg value="--set-max-idx-number=10000" />
<arg value="--main-dex-list" />
<!-- 主包包含class文件列表 -->
<arg value="${main-dex-rule}" />
<arg value="--minimal-main-dex" />
<arg value="--output=${bin}" />
<!-- 把bin下所有class打包 -->
<arg value="${bin}" />
<!-- 把libs下所有jar打包 -->
<!-- <arg value="${libs}" /> -->
</exec>
</target>

<!-- 打包资源文件(包括res、assets、AndroidManifest.xml) -->

<target
name="package"
depends="multi-dex" >

<echo message="package-res-and-assets..." />

<exec
executable="${tools.aapt}"
failonerror="true" >

<arg value="package" />

<arg value="-f" />

<arg value="-S" />

<arg value="${res}" />

<arg value="-A" />

<arg value="${assets}" />

<arg value="-M" />

<arg value="${manifest}" />

<arg value="-I" />

<arg value="${android-jar}" />

<arg value="-F" />
<!-- 放到临时目录中 -->

<arg value="${package-temp-name}" />
</exec>
</target>
<!-- 对临时目录进行打包 -->

<target
name="build-unsigned-apk"
depends="package" >

<echo message="Build-unsigned-apk" />

<java
classname="com.android.sdklib.build.ApkBuilderMain"
classpath="${sdk-folder}/tools/lib/sdklib.jar" >

<!-- 输出路径 -->

<arg value="${unsigned-apk-path}" />

<arg value="-u" />

<arg value="-z" />

<arg value="${package-temp-name}" />

<arg value="-f" />

<arg value="${main-dex-name}" />

<arg value="-rf" />

<arg value="${src}" />

<arg value="-rj" />

<arg value="${libs}" />
</java>
</target>

<!-- 拷贝文件到apk项目的根目录下 -->

<target
name="copy_dex"
depends="build-unsigned-apk" >

<echo message="copy dex..." />

<copy todir="${project-dir}" >

<fileset dir="${bin}" >

<include name="classes*.dex" />
</fileset>
</copy>
</target>

<!-- 循环遍历bin目录下的所有dex文件 -->
<target
name="add-subdex-toapk"
depends="copy_dex" >

<echo message="Add subdex to apk..." />

<foreach
param="dir.name"
target="aapt-add-dex" >

<path>

<fileset
dir="${bin}"
includes="classes*.dex" />
</path>
</foreach>
</target>

<!-- 使用aapt命令添加dex文件 -->

<target name="aapt-add-dex" >
<echo message="${dir.name}" />
<echo message="执行了app" />
<!-- 使用正则表达式获取classes的文件名 -->
<propertyregex
casesensitive="false"
input="${dir.name}"
property="dexfile"
regexp="classes(.*).dex"
select="\0" />
<if>
<equals
arg1="${dexfile}"
arg2="classes.dex" />
<then>
<echo>
${dexfile} is not handle
</echo>
</then>
<else>
<echo>
${dexfile} is handle
</echo>
<exec
executable="${tools.aapt}"
failonerror="true" >
<arg value="add" />
<arg value="${unsigned-apk-path}" />
<arg value="${dexfile}" />
</exec>
</else>
</if>
<delete file="${project-dir}\${dexfile}" />
</target>

<!-- 生成签名的apk -->
<target
name="sign-apk"
depends="add-subdex-toapk" >

<echo message="Sign apk..." />

<exec
executable="${tools.jarsigner}"
failonerror="true" >
<!-- keystore -->
<arg value="-keystore" />
<arg value="${keystore-name}" />
<!-- 秘钥 -->
<arg value="-storepass" />
<arg value="123456" />
<!-- 秘钥口令 -->
<arg value="-keypass" />
<arg value="123456" />
<arg value="-signedjar" />
<!-- 签名的apk -->
<arg value="${signed-apk-path}" />
<!-- 未签名的apk -->
<arg value="${unsigned-apk-path}" />
<!-- 别名 -->
<arg value="${keystore-alias}" />
</exec>
</target>

<!-- 签名发布 -->

<target
name="release"
depends="sign-apk" >

<delete file="${package-temp-name}" />

<delete file="${unsigned-apk-path}" />

<echo>
APK is released.path:${signed-apk-path}

</echo>
</target>

</project>

<!-- 版权所有,未经同意请勿转载!猴子搬来的救兵 http://blog.csdn.net/mynameishuangshuai -->
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448

为了方便大家理解,这里我们对build的流程进行分析,详见下图:



main-dex-rule.txt 

该文件中只放置了一个class文件
com/castiel/demo/MainActivity.class
1
1

ant编译前整个项目结构



ant脚本编译过程

在执行cmd命令,进入项目根目录路径,然后执行ant命令





编译成功后,解压APK可以看到我们成功的实现了多分包技术,生成两个dex文件。



最后成功运行项目



下一篇博客,我们将和大家一起学习使用Gradle构建。

彻底掌握Android多分包技术MultiDex-用Ant和Gradle分别构建(二)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: