Python: 另辟蹊径实现Android多渠道打包
May 26, 2015
要先说明的是本文说的“渠道”单指在
说起 Android 多渠道打包,真是八仙过海各显神通:有手动一个个耐心打包的,有用
通过apk里
上述方法各有优劣,在这里就不一一赘述了。
本文要介绍的是另一种方法:
直接修改APK中的
上述种种,说白了都是围绕着如何修改
首先得知道一点,APK中的
Table of Contents
如何修改AXML中渠道名
想要修改一个文件,你得先了解它的格式。AXML文件格式其实早已有人研究,如: 《发布C语言的Android binary XML(AXML)解析代码》, 《AndroidManifest Ambiguity方案原理及代码》。
这里就直接引用结论了:
综上结论,可知,
那么为了方便后期修改,我们可以先编译的一个特殊的“占位渠道包”,这个包的渠道名是一个 占位字符串,而这个字符串在AXML占的数据块长度能 适应所有渠道名的长度。假设一个占位字符串长度16,那么它自然可以被个数小于16的任意字符串所替代,如占位字符串’abcdefghijklmnop’,渠道有’xxxx’,’abcdef789’…
通过这个特殊的渠道包,我们就能够生成所有渠道包。
通过占位渠道包生成其他渠道包
大致步骤如下:
-
解压这个占位渠道包A中的
AndroidManifest.xml -
用真正的渠道名替换
AndroidManifest.xml 中的 占位字符串 -
拷贝一份新的占位渠道包B,删除掉
META-INF/* 和AndroidManifest.xml -
将修改后的
AndroidManifest.xml 重压缩到新的包B中 - 重命名渠道包B,并签名
- zipalign
- 完成一个渠道
如果你的渠道列表实在非常的多,你大概需要用多线程来优化这个步骤吧!
优劣
这个方法的优点在于:
1. 快,比所有需要重编译代码的方法快(包括apktool重打包、gradle定义flavor等);
2. 不依赖于第三方工具(都是自己写的实现脚本,算第三方不…)
缺点在于:
1. 要重新签名(倒也算不上什么缺点);
2. 需要注意占位字符串的长度不要太短了= =
实现脚本
我实现的版本这里就不献丑了,等你来完善吧!
核心代码如下:
python 替换AXML中的字符串:
def 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 ( '<i' , len ( string )) 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 [/code] </div> </figure> <p>签名APK命令形如下:</p> jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ~/.android/debug.keystore -storepass android -keypass android path/to/channel.apk AndroidDebugKey
完。
本文思路源于@某因幡,向这只安静的兔子致敬~~
原文:http://www.yrom.net/blog/2015/05/25/the_other_way_to_package_multi_channel_apks/
0 Comments