后期-打包、上架相关

Android打包

Android打包流程图

官网最新的图

Android打包流程图

Gradle 与 Android 构建入门

打包流程

有了前面这些铺垫,让我们实际看看在执行打包 Task 时,实际还执行了哪些 Task。环境配置如下:

  1. Gradle 5.1.1
  2. Android Gradle Plugin 3.1.2
  3. org.gradle.parallel=true 开启并行编译
  4. releaseminifyEnabled true
1
2
# --dry-run 表示不实际执行每个 Task
gradlew assembleRelease --dry-run

Task 很多,接下来为大家介绍几个重点的 Task,其余没介绍的感兴趣的同学可以找找对应的实现类,看看它的实现。

preBuild

描述:做一些编译前的检查
一个例子:有的人可能遇到下面的错误

1
"Android dependency "+ display+ "is set to compileOnly/provided which is not supported"

这个的原因就是由前面说过的 compileClasspath 和 runtimeClasspath 引起的。
当一个组件因为不同的依赖配置项导致它的 compileClasspath 比如为 1.1.1版本,但他的 runtimeClasspath 是 1.1.2版本,preBuild 就会检测出这个问题并报错我们处理

compileReleaseAidl

类:AidlCompile
描述:内部使用 AidlProcessor 调用 call 方法使用 build-tool 下的 aidl 执行编译。

各类 generate和 merge

这些 Task 允许我们在整个编译工程中动态的生成一些代码,生成好的资源需要和已有的资源进行合并,并且需要注意有可能覆盖已有资源,就不再详细介绍了,

过程

图片

第一步:我们有 app 工程下的 Java 源文件,还有 AIDLgenerateR.java 等生成的 Java 源文件,还有本身依赖,源码子工程的 jar 包、远端 aar 解压的 jar 包等一系列二进制文件,源码文件是 javac 需要编译的内容,二进制文件 .jar/.class 则是当 javac 编译遇到一个类名等符号时,如果发现在现有的源文件找不到,该去哪找的集合,对应的 javac 参数就是 -classpath

而这个参数,其实就是 compileClasspath 的一个应用,如果你源文件引用了一个类,它的 jar 包不在 compileClasspath 中,那么在编译时 javac 就会报错找不到符号了

第二步:当源文件被编译成类文件后,Google 提供了 Transform 机制允许我们对二进制文件在打包前进行修改,比如前面图片中的 :app:transformClassesWithXXXForRelease SKIPPED 就是我们自定义的 Transform。通过 :app:transformClassesAndResourcesWithProguardForRelease SKIPPED 也可以看到 Proguard 也是通过 Transform 机制实现的,这里图片中一个 .class 文件,一个是 .jar/.class 文件,第一个显然是 javac 编译后的产物,第二个则是 runtimeClasspath,就是那些需要被打包的二进制。相信大家这样就理解了 compileClasspathruntimeClasspath 是如何影响打包过程

第三步:当 Transform 处理好所有的 class 文件后,接下来就是将 .class 文件转换为 .dex 文件。值得注意的是,javac 只能发现源码的问题,不会发现那些未参与编译的二进制的问题。而在 dex 转换过程中则可以发现比如类重复问题或者一个类,名字不变,但是由 Class 变成了 Interface 这类严重的代码问题。

第四步:就是将前面的和资源进行打包。对应的类是 PackageApplication,得到这个 Task 后可以对打包的内容进行自定义

查看apk签名

  • 法一:
    使用JDK命令行工具查看,直接对比证书指纹。命令:keytool -printcert -v -file XXX.RSA
    其中xxx.RSA可通过:用压缩软件打开apk,找到META-INF下的CERT.RSA即可
  • 法二:
    用压缩软件打开apk,拷贝CERT.RSA到本地,更改后缀后为 CERT.p7b,在windows上直接打开(选中“证书”,选择右侧证书文件,打开证书文件看详细信息)

查看签名文件keystore的SHA1、MD5

AS自带的debug.keystore

  • MAC的:

    1. cd到jdk的bin目录下
      1
      cd /Library/Java/JavaVirtualMachines/jdk1.8.0_151.jdk/Contents/Home/bin
    2. 输入以下语句获取(密钥:android)
      1
      keytool -v -list -keystore /Users/xxx/.android/debug.keystore
  • Windows的:

    1. cd到jdk的bin目录下【有配置jdk环境变量的话这步可省略】
      1
      2
      3
      cd C:\Program Files\Java\jdk1.7.0_79\bin
      或者:
      cd D:\dev\AS\jbr\bin
    2. 输入以下语句获取(密钥:android)
      1
      keytool -v -list -keystore  C:\Users\Administrator\.android\debug.keystore
1
2
3
keytool -list -v -keystore XXX.keystore
keytool -printcert -jarfile XXX.apk
keytool -printcert -file CERT.RSA 【解压apk,拿到 META-INF/CERT.RSA

上面的命令行以前可以看MD5,现在只有【SHA1、SHA256

去掉冒号的js代码:"CD:B0:3E:73:53:2B:C2:A2:54:5D:2E:75:67:74:BB:92".replace(/\:/g,"")【新版Chrome不让用了。那就直接用文本的全局替换】

现在取MD5

【AS配置的jdk11生成的签名文件】

  1. 法一:AS右侧Gradle:Tasks>android>signingReport,双击
    【报Algorithm HmacPBESHA256 not available更改AS的jdk为自带的jbr路径(jdk17)】
  2. 法二:单独取md5,使用腾讯工具【忘记包了】,有另外的apk:“MD5签名生成器
    【报Algorithm HmacPBESHA256 not available,更改环境变量为AS的jbr路径】

命令行生成数字签名文件

1
keytool -genkey -alias demo.keystore -keyalg RSA -validity 40000 -keystore demo.keystore

加固

加固原理

apk 上架之前需要加固

  • 乐固只有 exe,会把签名去掉,只有加固的功能
  • 360 加固宝【只勾选“签名校验”】,会把签名去掉,有加固和重新签名的功能【只有每日一次免费加固,oppo、三星渠道不可用】
  • oppo在线加固 【用基础版加固即可】
  • 梆梆加固【加固比较慢,加固后的包也比较大】
  • github加固工具

so加固

加壳步骤:

  1. 增加init函数:cpp文件添加 void my_init(){}
  2. Android.mk文件添加:LOCAL_LDFLAGS += -WI,-init=my_init
  3. 将编译好的so拷贝到 upx-3.96-win64 文件夹中
  4. 用命令行进入 upx-3.96-win64 目录中
  5. 输入命令:upx --android-shlib 文件名称.so
  6. 加壳后的so会替换原有的so

签名、多渠道分包

packer:签名并多渠道分包

多渠道分包:packer-ng-plugin: 下一代Android打包工具(对Gradle 7.x的支持),对加固过未签名的apk进行多渠道分包

分包渠道信息

32位渠道【360加固后的包用它】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
360
baidu
wandoujia
tengxun
lianxiang
huawei
xiaomi
vivo
baiduSearch
baiduInfo
toutiaoSearch
toutiaoInfo
studio
honor
nubiya
lianxiangsem1

32位渠道【360加固的包以下平台报错,用梆梆加固】

1
2
sanxing
oppo

64位渠道

1
2
3
4
meizu
shenmasem01
shenmasem02
shenmasem03

命令行签名【推荐用packer

  1. 前提:要把%JAVA_HOME%\bin设置到环境变量

  2. jarsigner【是v1】 命令可以(给加固后未签名的)重新签名
    例:

    1
    2
    3
    4
    5
    #签名文件是 .keystore,注意不要带后缀
    jarsigner -verbose -keystore /Users/用户名/mykeystore -signedjar ./signed.apk ./unsigned.apk haha

    #签名文件是 .jks
    jarsigner -verbose -keystore D:\mykey\my.jks -signedjar D:\mykey\signed.apk D:\mykey\aaa\myunsign.apk abc

    注意:证书名称即 Alias(你设置的别名);去掉 keystore 的扩展名称即可解决“keystore No such file or directory”问题;
    注意:jarsigner签名的是v1。要v2签名的话用apksigner

  3. apksigner【v2】

    1
    apksigner sign --ks (签名地址) --ks-key-alias (别名) --out (签名后的apk地址) (待签名apk地址)    

    例子:

    1
    apksigner sign --ks D:\pack\597app.jks --ks-key-alias 597App --out "597Combine_jiagu_signed.apk" "597Combine_jiagu.apk" 

上架

先注册,比较主流的应用市场

  • 360手机助手

  • 百度手机助手

  • 豌豆荚

  • 腾讯应用宝

  • 联想乐商店

  • 华为应用市场

  • 荣耀

  • 小米开放平台

  • vivo

  • 百度

  • 头条

  • 三星应用商店

  • OPPO应用商店

  • 魅族应用中心

  • 努比亚

  • 搜狗手机助手

  • 应用汇

  • 机锋市场

  • 乐视应用市场

  • 联通沃商店

  • 易用汇

  • 木蚂蚁

  • 优亿市场

  • 冒泡堂

  • N多网

  • 锤子应用商店

  • 海信应用商店

  • 网易应用中心

  • PC6安卓网

    准备的材料

  • APK文件、名称、版本号

  • APP的简介200字左右

  • 一句话简介20字以内

  • 软件截图4-5张

  • icp备案、软著安全评估报告、资质文件等

脱壳

AS library生成jar包和aar的方法总结

打包问题

:app:transformClassesAndResourcesWithR8ForRelease

问题:AS打包卡在app:transformClassesAndResourcesWithR8ForRelease

卡在app:transformClassesAndResourcesWithR8ForRelease很长时间一直不能生成包,有时候产生java.lang.OutOfMemoryError: GC overhead limit exceeded错误。而且编译打包时偶尔会报Error:java.lang.OutOfMemoryError

解决(在 gradle.properties中添加):

1
2
3
4
5
6
7
8
9
10
11
# 编译时使用守护进程
org.gradle.daemon=true
#JVM 最大允许分配的堆内存,按需分配
org.gradle.jvmargs=-Xmx16896m -XX:MaxPermSize=4096m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# 使用并行编译
org.gradle.parallel=true
org.gradle.configureondemand=true
#启用新一代Dex编译器D8
android.enableD8=true
#启用gradle缓存
org.gradle.caching=true

运行时报警告:

1
2
3
WARNING: The following project options are deprecated and have been removed:
android.enableAapt2
This property has no effect, AAPT2 is now always used.

解决方案:
移除gradle.propertiesandroid.enableAapt2=true