android多渠道一起打包,另辟蹊径实现Android多渠道打包

要先说明的是本文说的“渠道”单指在AndroidManifest.xml 用定义的一个标识字符串(如友盟统计)。在代码或者通过其他文件定义的方式殊途同归。

说起 Android 多渠道打包,真是八仙过海各显神通:有手动一个个耐心打包的,有用Ant或Maven重复跑编译任务的,有用apktool解包后再修改重打包的,有在build.gradle定义一堆flavor的,乃至有通过apk里META-INF/下的空文件来定义渠道的。

上述方法各有优劣,在这里就不一一赘述了。

本文要介绍的是另一种方法:直接修改APK中的AndroidManifest.xml。

上述种种,说白了都是围绕着如何修改AndroidManifest.xml,如何重打包或是重编译。介绍的这个方法也不外如是,只是无需重打包重编译而已。

首先得知道一点,APK中的AndroidMainfest.xml,解压出来用文本编辑器可是不能直接打开的,它是aapt生成的一个二进制的xml格式(被称为AXML),得用其他工具(如apktool)先解析出来。所以问题来了,如何直接修改这个 AXML 文件?

如何修改AXML中渠道名

这里就直接引用结论了:string在AXML中是存放在StringChunk中的;string都是UTF-16编码的;如果需要往AXML中新增string是比较麻烦的(牵一发而动全身…);为了4字节对齐string数据块末尾可能被填充数个0x00…

综上结论,可知,定义的渠道值是UTF-16编码的string,并且可能被填充数个0x00。

那么为了方便后期修改,我们可以先编译的一个特殊的“占位渠道包”,这个包的渠道名是一个占位字符串,而这个字符串在AXML占的数据块长度能适应所有渠道名的长度。假设一个占位字符串长度16,那么它自然可以被个数小于16的任意字符串所替代,如占位字符串’abcdefghijklmnop’,渠道有’xxxx’,’abcdef789’…

通过这个特殊的渠道包,我们就能够生成所有渠道包。

通过占位渠道包生成其他渠道包

大致步骤如下:解压这个占位渠道包A中的AndroidManifest.xml

用真正的渠道名替换AndroidManifest.xml中的占位字符串

拷贝一份新的占位渠道包B,删除掉META-INF/*和AndroidManifest.xml

将修改后的AndroidManifest.xml重压缩到新的包B中

重命名渠道包B,并签名

zipalign

完成一个渠道

如果你的渠道列表实在非常的多,你大概需要用多线程来优化这个步骤吧!

优劣

这个方法的优点在于:快,比所有需要重编译代码的方法快(包括apktool重打包、gradle定义flavor等);

不依赖于第三方工具(都是自己写的实现脚本,算第三方不…)

缺点在于:要重新签名(倒也算不上什么缺点);

需要注意占位字符串的长度不要太短了= =

###实现脚本

核心代码如下: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

31def replace_axml_string(axml_data, old_string, new_string):

'''

axml_data: the raw bytearray readed from AndroidManifest.xml

'''

new_string_pack = axml_utf16_pack(new_string)

old_string_pack = axml_utf16_pack(old_string)

new_string_pack_len = len(new_string_pack)

old_string_pack_len = len(old_string_pack)

if old_string_pack_len < new_string_pack_len:

raise ValueError('new_string cannot be larger than old_string! ')

pos = 0

while True:

pos = find_pack_in_axml(axml_data, old_string_pack, pos)

if pos < 0:

break

axml_data[pos : pos + new_string_pack_len] = new_string_pack[ : new_string_pack_len]

delta = old_string_pack_len - new_string_pack_len

if delta:

axml_data[pos + new_string_pack_len: pos + old_string_pack_len] = bytearray(delta)

def axml_utf16_pack(string):

pack = bytearray(string.encode('utf-16'))

str_len_pack = struct.pack('

pack[ : 2] = struct.unpack('BB', str_len_pack[ : 2])

return pack

def find_pack_in_axml(axml_data, pack, start):

pos = axml_data.find(pack, start, -1)

return pos

签名APK命令形如下:jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ~/.android/debug.keystore -storepass android -keypass android path/to/channel.apk AndroidDebugKey

完。

本文思路源于@某因幡,向这只安静的兔子致敬~~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值