深刻浅出Android打包

Android市场的渠道分散已不是什么新鲜事,但如何高效打包还是令许多开发者头疼的问题。本篇文章着重介绍了目前最新的三种打包方案,而且从安全方面对这三种方案进行点评,相信会给开发者带来新的助力。html

通常需求的打包,一条行命令就出来了。复杂一些的话,也就是一个简单的开源工具,或是一段小配置代码就搞定了。既然如此,为何我还要来写Android打包相关内容?主要有如下两个方面的缘由:java

  • Android市场开放,渠道愈来愈多,几千个渠道已是常态。因此,须要一个高效且安全的打包方式,来支持成千上万的渠道打包重任。
  • 如今Android应用程序都会使用ProGuard或者DexGuard来进行代码混淆,在引入代码混淆的前提下,须要针对某一个版本尽可能只保留一份符号表(Android是mapping文件)。多份不一样的符号表对应到一个版本的程序,会给Crash修正等后期问题的追查形成很大困难。

因此,当Android开发团队有必定规模时,须要一种优雅而且高效安全的打包方式来衔接从代码到应用程序包的这个过程。python

历史回顾

大概在3-4年前,国内的移动开发者都会使用友盟来作Crash收集和一些用户的行为数据收集。因此,友盟定义渠道的方式基本定义了数据收集工具的渠道定义方式。友盟默认的渠道直接从AndroidManifest文件中的meta-data里面读取。如如下代码所示:android

<meta-data android:name="UMENG_APPKEY" android:value="4ee5c714ee5c714ee5c71">
</meta-data>
<meta-data android:name="UMENG_CHANNEL" android:value="wandoujia">
</meta-data>

在示例中,UMENG_APPKEY是帐户的惟一标示,UMENG_CHANNEL的内容为wandoujia,即表示这时渠道是wandoujia。这里再多说一句渠道的概念,通常的渠道可能会表示具体是哪一个应用市场。好比上传豌豆荚和上传360手机助手的渠道标示不同,固然,若是有一些线下推广的话,也须要区分渠道。随着数据统计愈来愈细,渠道也会更加的层出不穷。如今一个日活在百万的应用,Android的渠道在几千个是很是正常的现象。git

在AndroidManifest定义渠道的年代,多渠道打包无非如下两种方案:github

  • 方案一:彻底的从新编译,即在代码从新编译打包以前,在AndroidManifest中修改渠道标示;
  • 方案二:经过ApkTool进行解包,而后修改AndroidManifest中修改渠道标示,最后再经过ApkTool进行打包、签名。

如今看来,这两种方式效率都不高。方案一基本上毫无效率可言,只能支持20个之内的渠道规模。方案二效率高一些,若是再引入多线程驱动来进行打包,支持200之内的渠道打包毫无压力。可是随着Android的版本升级,代码混淆和加固工具的引入,这种方案已经不能正常运行了。因此,须要一种兼容性好而且高效的多渠道打包方式。web

三种高效的多渠道打包方式

Android项目自己是开源的,你们均可以去研究任何实现,基于这个特性,一种叫作“Android黑科技”的学科迅速诞生。如下介绍的多渠道打包方式都应该属于其范畴。api

添加comments多渠道打包安全

首先解释什么是comments(注释或评论)的打包方式?bash

Android应用使用的APK文件就是一个带签名信息的zip文件,根据zip文件格式规范(请参见:https://pkware.cachefly.net/webdocs/casestudies/APPNOTE.TXT),每一个文件的最后都必须有一个叫Central Directory Record(https://users.cs.jmu.edu/buchhofp/forensics/formats/pkzip.html)的部分,这个CDR的最后部分叫“End of Central Directory Record”,这一部分包含一些元数据,它的末尾是zip文件的注释。注释包含Comment Length和File Comment两个字段。前者表示注释的长度,后者是注释的内容,正确修改这一部分不会对zip文件形成破坏,利用这个字段,咱们能够添加一些自定义数据。

简单来讲就是:咱们利用的是zip文件“能够添加comment(摘要)”的数据结构特色,在文件的末尾写入任意数据,而不用从新解压zip文件(apk文件就是zip文件格式);因此该工具不须要对APK文件解压缩和从新签名便可完成多渠道自动打包,可谓是高效、速度快,无兼容问题。

这种高效的方式是奇虎360的一位工程师公布的,他已经把相关工具的代码开放在GitHub上面了,地址为https://github.com/seven456/MultiChannelPackageTool。工具自己为命令行形式,一条命令便可生成一个多渠道包,而且很是高效。做者本人公布的性能数据是:5M的APK,1秒种能打300个。

就是由于速度很是快,如今一些厂商用这种方案来作安装包的动态生成。在用户下载以前渠道是没有添加进去的,用户选择下载之后经过渠道来源判断渠道标示,而且写入。过程瞬间完成,用户下载时毫无感知。

固然,利用工具把渠道写入应用程序包内,还应该提供一种在APK内部读取渠道的方式。读取时方法也很是简单,首先经过反射方法找到APK在手机中的位置,而后经过读zip comment的方式获取渠道内容。读取comment和找APK位置的代码都能在这个急速添加渠道的工具中找到,在这里再也不详细介绍。须要注意的是,在Android中使用Java反射的性能很是差,须要作一些优化处理。

若是你以为这种方式的安全比较差,也能够本身从新实现一个小工具,而且对comment的内容加密。固然,加密使用的密钥须要在APK代码中体现,这种加密方案也只是提升了一点破译的门槛而已。

美团的Android多渠道打包

美团点评技术团队(原美团技术团队)有一个公开的博客,每篇文章都在领域内有必定的影响。其中有一篇就是讨论“如何高效地进行Android多渠道打包?”,可参见:http://tech.meituan.com/mt-apk-packaging.html

文章中首先提到了ApkTool的多渠道打包方式,而后讲到美团的业务发展已经有900多个渠道,ApkTool的方式已经不能支持这种规模的多渠道打包。因此,美团点评技术团队开始探索一种更加高效的多渠道打包方式。

美团的作法是把一个Android应用包看成zip文件包进行解压,而后发如今签名生成的目录下添加一个空文件不须要从新签名。利用这个机制,该文件的文件名就是渠道名。这种方式不须要从新签名等步骤,很是高效。

下面咱们来详细讲解一下这个过程。首先把一个已经签名好的Android应用程序包解压缩,签名文件的目录为:META-INF,如图1所示。

图片描述 

图1 美团Android多渠道打包压缩文件目录

图中有两个框选的部分,分别为:assets目录和META-INF目录。assets等下文介绍豌豆荚高效打包方式的时候再说。咱们这里先来看看META-INF目录。在META-INF目录下的三个文件,就是一个Android应用程序包被正确签名以后的生成文件,当这个程序包中的文件被修改之后,验证信息就须要从新生成,不然验证的签名信息就不正确。这套签名机制也是从Java的jar包方式继承过来的。

美团多渠道打包方式,正是利用签名的验证方式,巧妙地放入了一个空文件,利用文件名来表示渠道方式的完成。若是放入了一个非空的文件,这时签名验证的机制就会被破坏。美团的具体作法是经过一段简单的Python脚原本完成。代码以下:

import zipfile
zipped = zipfile.ZipFile(your_apk, 'a', zipfile.ZIP_DEFLATED) 
empty_channel_file = "META-INF/mtchannel_{channel}".format(channel=your_channel)
zipped.write(your_empty_file, empty_channel_file)

经过运行Python脚本,Android应用程序包中便会多出一个文件,如图2所示。

图片描述 

图2 经过运行Python脚本,Android应用程序包中多出来的一个文件

渠道添加成功后,能够参照美团点评技术团队的博客,在Android程序中添加读取渠道的代码,便可拥有高效的多渠道打包方式。

固然,这个技术实现自己能够有更加优雅和高效的方式——添加渠道。原理不变,添加方式能够进行改变。添加文件可使用AAPT这个工具。AAPT自己是Android平台自带的一个命令行工具,能够实现从Android程序包中删除和添加任意文件。固然,若是添加文件破坏了签名验证信息,则是另一回事了。关于AAPT的使用和其在多渠道签名打包时的用法,会在下文进行讲解。

若是如今想把美团的渠道删除,使用AAPT输入一条简单的命令行便可(能删除就能添加,添加部分仍是在豌豆荚多渠道打包方案中介绍吧):

aapt remove  meituan.apk META-INF/mtchannel_meituan

豌豆荚Android多渠道打包

豌豆荚的多渠道打包方案和美团的所用的方式同样,都是添加一个文件,文件自己会带入渠道消息。但不一样点在于它添加的是一个不为空的文件。以前已经提到过了,若是添加一个非空文件,就会破坏签名校验,须要从新签名。

从这个方案能够看出,豌豆荚多渠道打包和美团多渠道打包正好是利用了一个特性的两个方面。固然,若是须要从新签名,效率会差一些。在失去效率的同时,拥有了更加安全的渠道管理。下面具体讲解,豌豆荚多渠道打包管理方案。

首先,若是把渠道文件还放在META-INF目录下的话,从新签名时渠道文件会被删掉。因此,渠道文件须要从新找一个合理的地方存放。以前的文章也提到过,还有一个assets目录。assets目录和META-INF目录同样,能够随便放入小文件。若是是空文件不须要从新签名

加入渠道时主要有如下步骤。

  • 首先须要准备好渠道文件。在Android应用程序包的同级目录,新建一个assets目录,而且在该目录下放入一个channel.txt文件,文件内容是渠道信息,能够是wandoujia或360等。
  • 使用AAPT加入渠道文件:
aapt add wandoujia.apk asserts/channel.txt
  • 在渠道加入后,从新签名APK文件而且进行对齐。 
    从新签名相关的步骤就再也不介绍了,若是感兴趣的话,推荐看一下calabash-android这个开源项目的resign相关部分实现,calabash的从新签名实现是我见过最严谨的。

图片描述 

图3 豌豆荚打包方案处理一个包须要大约4秒

说完了豌豆荚多渠道打包的实现部分之后,须要来谈一下效率。以前的两种多渠道打包方案效率都很高,处理一个包都能在1秒以内完成。但豌豆荚的多渠道打包方案因为必须从新签名,会慢一些(但也没有慢多少)。在屏蔽了一些关键信息以后,从图3中能够看到,一次处理大概须要4秒的时间。如今一台通常配置的计算机,开8个线程处理,速度彻底能够接受。

从安全的角度看多渠道打包方案

三种打包方案都是高效的实现,固然也有各自的侧重点。如下,从安全的角度来分析。

对于Android应用包的安全问题来讲,主要须要考虑渠道被恶意篡改的可能性和成本。若是一个渠道商,经过网络劫持和篡改渠道的组合方式来获取暴利,对于程序开发者来讲可能会存在着巨大的经济损失。

在介绍篡改渠道以前,仍是先简单介绍AAPT这个工具的通常使用方法。AAPT是在AndroidSDK内的一个命令行工具,具体位置在AndroidSDK目录下的build-tools/19.0.0或者其余版本下,AAPT并不存在于每一个发行版本中,因此通常使用的AAPT是19.0.0或19.0.1版本。

使用AAPT主要有如下4种场景:

  • 查看Android开发包的基本信息,例如:包名、版本等。以微信的Android版本为例:
INPUT  apt dump badging weixin.apk
OUTPUT package: name='com.tencent.mm' versionCode='740' versionName='6.3.13.49_r4080b63'
uses-permission:'com.tencent.mm.plugin.permission.READ'
uses-permission:'com.tencent.mm.plugin.permission.WRITE'
uses-permission:'com.tencent.mm.permission.MM_MESSAGE'
uses-permission:'com.huawei.authentication.HW_ACCESS_AUTH_SERVICE'
sdkVersion:'15'
targetSdkVersion:'23'
uses-feature-not-required:'android.hardware.camera'
uses-feature-not-required:'android.hardware.camera.autofocus'
uses-feature-not-required:'android.hardware.bluetooth'
uses-feature-not-required:'android.hardware.location'
uses-feature-not-required:'android.hardware.location.gps'
uses-feature-not-required:'android.hardware.location.network'
uses-feature-not-required:'android.hardware.microphone'
uses-feature-not-required:'android.hardware.telephony'
uses-feature-not-required:'android.hardware.touchscreen'
uses-feature-not-required:'android.hardware.wifi'
uses-permission:'android.permission.ACCESS_NETWORK_STATE'
uses-permission:'android.permission.ACCESS_COARSE_LOCATION'
uses-permission:'android.permission.ACCESS_FINE_LOCATION'
uses-permission:'android.permission.CAMERA'
.....

从以上代码能够看到这个微信的版本信息,而且知道了微信的包名是:com.tencent.mm,还能看到微信这个应用使用到了哪些权限等信息(更多的信息仍是交给读者本身仔细研究吧)。

使用AAPT查看Android开发包的目录结构以下:

INPUT  aapt list  weixin.apk
OUTPUT META-INF/MANIFEST.MF
META-INF/COM_TENC.SF
META-INF/COM_TENC.RSA
assets/jsapi/wxjs.js
assets/avatar/default_nor_avatar.png
assets/avatar/default_meishiapp.png
assets/avatar/default_hd_avatar.png
assets/ipcall_country_code.txt
assets/merged_features.xml
assets/address
.....

又看到了META-INF目录和assets目录。通常在Android的应用中assets都会放一些静态的资源。

  • 使用AAPT添加一个文件:
INPUT aapt add wandoujia.apk  assets/channel.txt
  • 使用AAPT删除一个文件
INPUT aapt remove wandoujia.apk assets/channel.txt

在掌握了以上简单的四个命令行后,修改美团的渠道就已经很是简单了。先经过list命令找到渠道文件,而后直接删除文件,最后再加入本身的恶意渠道。但豌豆荚的渠道也能够经过相同的方式来进行篡改,但篡改以后须要从新签名。在没有官方的签名文件时,是没有办法彻底伪造一个相同的签名的。

添加comments多渠道打包方式中虽然渠道信息不是明文显示,但也存在着被篡改的可能性。若是本身进行一些简单的加密能够杜绝大多数的恶意篡改,但因为不须要从新签名,这种方法仍是存在着被篡改的机率的。

具体选择哪种多渠道打包方式,仍是由业务决定的。豌豆荚做为一个应用商店,须要有本身的渠道,渠道的安全性会很很差。若是是一个普通的App,通常都是在一些应用商店上发布新版本,应用商店自己会给开发者提供相对安全的环境。因此这也就是美团方案依然可以使用的关键。

结束语

本文重点介绍了3种高效的多渠道打包方式。这三种方式都不会产生二次编译,二次编译会对一个版本的程序产生多个版本的符号表,在后期的问题追查中会有不少障碍。利用结束语简单说说符号表的重要性吧。

当程序正确打包发布之后,能够经过各类免费、收费或本身开发的工具来收集Crash等堆栈信息,查看错误并进行改正。你可能会获得一段这样的异常堆栈:

ava.lang.RuntimeException: There is no view set with ViewPackage found in the log tree.
at o.λ.ˎ(:64)
at o.λ.ˊ(:25)
at o.к$ˋ.run(:233)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.os.HandlerThread.run(HandlerThread.java:61)

而后若是没有一个符号表帮忙,你会陷入深深的沉思:这究竟是什么问题?个人代码在哪里?若是有了正确的符号表,一条命令行就能够就能够把相关的异常堆栈信息翻译正确。符号表和编译过程有着紧密的耦合,因此在选择打包工具时,都应该选择不产生二次编译的工具。在以上提到的三款工具中,添加多渠道都是基于已有APK进行添加的,并非基于源码来添加渠道。因此都彻底知足条件。

考虑到文章的篇幅,有一些内容没有展开说,开发者若是遇到这方面的问题,欢迎与我交流。

相关文章
相关标签/搜索