APK打包的详细说明
2019独角兽企业重金招聘Python工程师标准>>>
一.应用的打包签名
1.打包是什么?
打包就是根据签名和其他的标识生成安装包
2.签名是什么?
1.在android应用文件(apk)中保存的一个特别字符串
2.用来标识不同的应用开发者:开发者A,开发者B
3.一个应用开发者开发的多款应用使用同一个签名
> 就好比是一个人写文章,签名就相当于作者的署名。
> 如果两个应用都是一个开发者开发的,那么签名就是一样的。
> 这个开发者,可以是个人,也可以是公司、团体。
3.为什么要使用签名?
原因1:最简单直接的回答: 系统要求的。
>Android系统要求每一个Android应用程序必须要经过数字签名才能够安装到系统中,也就是说如果一个Android应用程序没有经过数字签名,是没有办法安装到系统中的!
原因2:
不同程序员开发的应用包名可能会相同, 导致一个应用覆盖掉另一个应用。
> 如果只有包名的概念,那么如果B应用与已经安装的A应用包名一样,那就实现覆盖。不合理!
> 而事实上是装不上B的,它会提示,存在包名一致,但是签名不一样的。这就不会覆盖。
4.如何为APK签名?
1.)用来生成应用签名的文件
一般我们在androidStudio运行的是采用的默认的debug.keystore
Eclipse生成的是xxxx.keystore
androidstudio生成的是xxxx.jks
2.)打包自己签名APK文件(我们这里采用的AndroidStudio)步骤:
Build-->Generate Signed APK如下图所示:
如果没有key store文件,那就创建文件
> 这里指定一个文件名。注意:在as中,签名文件keystore类型的文件变为jks格式的文件。
> 这里输入的是表明是谁,不适合写一个包名。
> 上面的密码是用来访问最上面的path路径文件的密码。而最上面的文件用来生成一个签名字符串。
> 下面Alias是一个别名。接下来的密码:是用来生成一个签名的时候,还需要的一个密码。与上面的密码可以不一致。
> 再下面的25,指的是有效期。google应用市场规定应用的有效期不低于25年。还可以改为100年。也就是说100年以后,我们的xxx.jks签名文件就失效了。
> 指明生成的apk的位置,Build Type:release:发布版 debug:测试版。
> 点击finish,稍等一会
> 默认在As工程中bin下生成的apk文件也有签名。只是用到了debug_keystore(测试)的模式。而我们想发布到应用市场的话,那就需要我们自己提供一个签名,不能用默认的了,不合适。如下:
3.)在代码中获得应用签名
public void getSingInfo() {try {PackageInfo packageInfo = getPackageManager().getPackageInfo("com.atguigu.p2p", PackageManager.GET_SIGNATURES);Signature[] signs = packageInfo.signatures;Signature sign = signs[0];parseSignature(sign.toByteArray());} catch (Exception e) {e.printStackTrace();}
}
public void parseSignature(byte[] signature) {try {CertificateFactory certFactory = CertificateFactory.getInstance("X.509");X509Certificate cert = (X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(signature));String pubKey = cert.getPublicKey().toString();String signNumber = cert.getSerialNumber().toString();
//发布的key和版本号Log.e("TAG", "pubKey:" + pubKey);Log.e("TAG", "signNumber:" + signNumber);} catch (Exception e) {e.printStackTrace();}
}
4.)使用签名文件时我们应该注意的几个方面:
千万不要把xxxx.keystore/xxxx.jks文件搞丢了
假说如果搞丢了:当APK版本升级时,用户必须首先撤除APK,在安装。否则直接安装会失败。
举例个使用场景:
公司程序员A写的应用,然后走了。后来来了一个程序员B把应用更新以后,又重新打包发布,使用的签名不一致。那完了。
导致出现更新不了应用的情况。必须把应用卸载了才能重新装。那想象一下,如果一个20万用户的应用,在更新时,发现安装不了。那可能随后就卸载了。或者就不更新了。
另外还影响的是:应用市场有下载量排名,如果重新签名,那就得重头来算。
5.)打包过程中遇到问题怎么解决?
现象:
Android导出APK包时出现,编译调试时不会出现。
错误信息:
Error:(16) Error: "baidutieba_client_inavailable" is not translated in "en" (English) [MissingTranslation]
Error:(63) Error: "baidutieba" is not translated in "en" (English) [MissingTranslation]
Error:(67) Error: "share_to_baidutieba" is not translated in "en" (English) [MissingTranslation]
错误截图:
解决办法:
resources 标签内增加两个属性即可:
<?xml version="1.0" encoding="utf-8" ?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:ignore="MissingTranslation">
</resources>
二.友盟的多渠道打包
1.)原理的说明:
1.什么是多渠道包?
渠道包就是要在安装包中添加渠道信息,也就是channel,对应不同的渠道,例如:小米市场、360市场、应用宝市场等
2.为什么要提供多渠道包?
我们要在安装包中添加不同的标识,应用在请求网络的时候携带渠道信息,方便后台做运营统计(这就是添加渠道信息的用处)。
3.实现多渠道打包的原理:
一般来讲,这个渠道的标识会放在AndroidManifest.xml的Application的一个Metadata中。然后就可以在java中通过API获取对应的数据了。
4.如何实现?
现在android渠道多种多样,其实渠道不仅仅局限于应用市场,一种推广方式也可以看做一个渠道,比如:通过人拉人的方式去推广,官网上推广,百度推广等。所以说渠道成千上万,为了推广,有时候一次也会打成千的安装包,那你半天或者一天啥都别干了,所以介绍几个大公司高效的打包方式,借鉴一下。
第一种:友盟就提供了多渠道打包的方式,可用于渠道统计等。
现在Android的构建工具换成了gradle,通过gradle,简单配置后就可以实现自动打所有渠道包。
2.)友盟打包的步骤:
1.首先去友盟的官网去注册,申请APP KEY
2.按照umeng的要求,manifest文件中需要有
<meta-dataandroid:name="UMENG_CHANNEL"android:value="${UMENG_CHANNEL_VALUE}" />
这段配置,value那里就是wandoujia,360之类的渠道名称,但是我们在这里不会去写渠道名,写的是一个占位符,后面gradle编译的时候会动态的替换掉它。
3.在module(一般也就是app)的build.gradle的android{}中添加如下内容:
productFlavors{wandoujia{manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"]}xiaomi{manifestPlaceholders=[UMENG_CHANNEL_VALUE: "xiaomi"]}}
productFlavors是android节点的一个自节点。你需要打什么渠道的包,就在这里按umeng的要求用渠道名给UMENG_CHANNEL_VALUE赋值。
4.优化1:上面只是两个渠道,如果有几十个渠道,都这样写,重复的东西太多,观察到每个渠道就是flavor的名称,所以修改如下:
productFlavors{wandoujia{//manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"]}xiaomi{//manifestPlaceholders=[UMENG_CHANNEL_VALUE: "xiaomi"]}}productFlavors.all { flavor ->flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]}
5.优化2:上面经过签名打包后生成的apk的名称是有默认命名规则的,如:xxx-xiaomi-release.apk 但是我们想包含版本信息如:xxx-xiaomi-release-1.0.apk,所以最终打包脚本如下:
productFlavors{wandoujia{//manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"]}xiaomi{//manifestPlaceholders=[UMENG_CHANNEL_VALUE: "xiaomi"]}}productFlavors.all { flavor ->flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]}applicationVariants.all { variant ->variant.outputs.each { output ->def outputFile = output.outputFileif (outputFile != null && outputFile.name.endsWith('.apk')) {def fileName = outputFile.name.replace(".apk", "-${defaultConfig.versionName}.apk")output.outputFile = new File(outputFile.parent, fileName)}}}
5.获取渠道
在代码中我们可以通过读取mate-data信息来获取渠道,然后添加到请求参数中,获取方法如下:
private String getChannel() {try {PackageManager pm = getPackageManager();ApplicationInfo appInfo = pm.getApplicationInfo(getPackageName(), PackageManager.GET_META_DATA);return appInfo.metaData.getString("UMENG_CHANNEL");} catch (PackageManager.NameNotFoundException ignored) {}return "";
}
6.执行签名打包:
这时候你去app/build/outputs/apk中就能看到自动打好的渠道包了。
3.美团多渠道打包
1.)原理:把一个Android应用包当作zip文件包进行解压,然后发现在签名生成的目录下(META-INF)添加一个空文件不需要重新签名。利用这个机制,该文件的文件名就是渠道名。这种方式不需要重新签名等步骤,非常高效。
2.)美团打包的步骤:
下载python工具包
1、将要打包的apk放到PythonTool中
2、在PythonTool/info/channel.txt中写入需要的渠道,一个渠道占一行
3、双击执行PythonTool/MultiChannelBuildTool.py文件(需要Python环境),就会生成渠道包
4、获取渠道信息:将JavaUtil文件中的ChannelUtil.java拷贝到工程,调用ChannelUtil.getChannel即可获取渠道
3.)用Java代码获取多渠道信息
package com.czt.util;import java.io.IOException;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.preference.PreferenceManager;
import android.text.TextUtils;public class ChannelUtil {private static final String CHANNEL_KEY = "cztchannel";private static final String CHANNEL_VERSION_KEY = "cztchannel_version";private static String mChannel;/*** 返回市场。 如果获取失败返回""* @param context* @return*/public static String getChannel(Context context){return getChannel(context, "");}/*** 返回市场。 如果获取失败返回defaultChannel* @param context* @param defaultChannel* @return*/public static String getChannel(Context context, String defaultChannel) {//内存中获取if(!TextUtils.isEmpty(mChannel)){return mChannel;}//sp中获取mChannel = getChannelBySharedPreferences(context);if(!TextUtils.isEmpty(mChannel)){return mChannel;}//从apk中获取mChannel = getChannelFromApk(context, CHANNEL_KEY);if(!TextUtils.isEmpty(mChannel)){//保存sp中备用saveChannelBySharedPreferences(context, mChannel);return mChannel;}//全部获取失败return defaultChannel;}/*** 从apk中获取版本信息* @param context* @param channelKey* @return*/private static String getChannelFromApk(Context context, String channelKey) {//从apk包中获取ApplicationInfo appinfo = context.getApplicationInfo();String sourceDir = appinfo.sourceDir;//默认放在meta-inf/里, 所以需要再拼接一下String key = "META-INF/" + channelKey;String ret = "";ZipFile zipfile = null;try {zipfile = new ZipFile(sourceDir);Enumeration<?> entries = zipfile.entries();while (entries.hasMoreElements()) {ZipEntry entry = ((ZipEntry) entries.nextElement());String entryName = entry.getName();if (entryName.startsWith(key)) {ret = entryName;break;}}} catch (IOException e) {e.printStackTrace();} finally {if (zipfile != null) {try {zipfile.close();} catch (IOException e) {e.printStackTrace();}}}String[] split = ret.split("_");String channel = "";if (split != null && split.length >= 2) {channel = ret.substring(split[0].length() + 1);}return channel;}/*** 本地保存channel & 对应版本号* @param context* @param channel*/private static void saveChannelBySharedPreferences(Context context, String channel){SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);Editor editor = sp.edit();editor.putString(CHANNEL_KEY, channel);editor.putInt(CHANNEL_VERSION_KEY, getVersionCode(context));editor.commit();}/*** 从sp中获取channel* @param context* @return 为空表示获取异常、sp中的值已经失效、sp中没有此值*/private static String getChannelBySharedPreferences(Context context){SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);int currentVersionCode = getVersionCode(context);if(currentVersionCode == -1){//获取错误return "";}int versionCodeSaved = sp.getInt(CHANNEL_VERSION_KEY, -1);if(versionCodeSaved == -1){//本地没有存储的channel对应的版本号//第一次使用 或者 原先存储版本号异常return "";}if(currentVersionCode != versionCodeSaved){return "";}return sp.getString(CHANNEL_KEY, "");}/*** 从包信息中获取版本号* @param context* @return*/private static int getVersionCode(Context context){try{return context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionCode;}catch(NameNotFoundException e) {e.printStackTrace();}return -1;}
}
4.)美团打包的优缺点
优点:
这种打包方式速度非常快,900多个渠道不到一分钟就能打完
缺点:
1、google如果哪天更改打包规则,使得在META-INF中建立空文件还需要重新打包,这种方式将不可用
2、一些不法的渠道商很容易通过工具修改渠道,如果一个渠道商,通过网络劫持和篡改渠道的组合方式来获取暴利,对于程序开发者来说可能会存在着巨大的经济损失
4.360多渠道打包
1.)原理:
apk文件本质就是zip文件,利用zip文件“可以添加comment(摘要)”的数据结构特点,在文件的末尾写入任意数据,而不用重新解压zip文件,我们就可以将渠道信息写入摘要区
2.)打包的步骤
1、将要写入渠道信息的apk放入MCPTool文件夹中
2、修改MCPTool.bat批处理文件,更改渠道和密码(渠道信息为了安全需要加密)
3、将apk拖到MCPTool.bat上执行,将会生成渠道包
4、修改MCPTool-check.bat中的密码和MCPTool.bat中的密码一致
5、将渠道包拖到MCPTool-check.bat上执行,就可以检查渠道信息是否正确
6、获取渠道:将MCPTool.java添加到工程或者将MCPTool.jar导入工程,调用MCPTool.getChannelId(this,"12345678","") 第一个参数为context,第二个是密码,第三个是默认值
注意点:在使用工具360打包时,出现了如下问题:
C:\ProgramData\Oracle\Java\javapath\java.exe找不到,原因是可能系统自己查找时默认了C:\ProgramData\Oracle\Java\javapath下的java.exe,解决的思路是将%JAVA_HOME\bin放在path路径的最前面就可以解决。
3.)优缺点:
优点:
1、5M的apk,1秒种能打300个
2、在下载apk的同时,服务端可以写入一些信息,例如邀请码,分享信息等
缺点:
渠道信息也是很容易修改,虽然可以加密,只是提高了修改的门槛
转载于:https://my.oschina.net/quguangle/blog/917100
APK打包的详细说明相关推荐
- Android反编译apk修改版本号重新打包签名详细教程(超详细)
文章目录 一.反编译工具介绍 1:apktool 获取资源文件 2:dex2jar(源码文件获取) 3:jd-gui 查看APK中classes.dex转化成出的jar文件,即源码文件 二.apkto ...
- 安卓逆向_1 --- 逆向环境配置、APK 文件结构、APK 打包流程
哔哩哔哩:https://www.bilibili.com/video/BV1UE411A7rW?p=1 Android 逆向工程师系统培训‹第九期›( 课程目录 ):https://ke.yijin ...
- Android 应用apk打包原理
文章目录 前言 一.打包前 - app源码结构 二.打包后 - apk 文件结构 - apk结构? - 图示 (1) - 图示 (2) 三.那是怎么生成的? 1.apk 打包流程 2.详解:构建流程中 ...
- 【朝花夕拾】Android性能篇之(四)Apk打包
前言 转载请声明,转自[https://www.cnblogs.com/andy-songwei/p/9721337.html],谢谢! APK,即Android Package,是将android程 ...
- android Apk打包过程概述_android是如何打包apk的
最近看了老罗分析android资源管理和apk打包流程的博客,参考其他一些资料,做了一下整理,脱离繁琐的打包细节和数据结构,从整体上概述了apk打包的整个流程. 流程概述: 1.打包资源文件,生成R. ...
- android-xBuild apk差分与合成,zip差分与合成,lua打包,apk打包,png/jpg图片压缩
android-xBuild 是一项集成了apk差分与合成,zip差分与合成,lua打包,apk打包,png/jpg图片压缩五大功能的开源项目 (github地址:https://github.com ...
- 【Android 安装包优化】APK 打包流程 ( 文件结构 | 打包流程 | 安装流程 | 安卓虚拟机 )
文章目录 一.APK 文件结构 二.APK 打包流程 三.APK 安装流程 四.安卓虚拟机 一.APK 文件结构 Android 应用的安装包时 以 " .apk " 为后缀的 A ...
- 【Android 安装包优化】动态库打包配置 ( “armeabi-v7a“, “arm64-v8a“, “x86“, “x86_64“ APK 打包 CPU 指令集配置 | NDK 完整配置参考 )
文章目录 一.动态库打包配置 二.NDK 完整配置参考 三.参考资料 一.动态库打包配置 在 build.gradle 构建脚本中 , 配置 ndk 编译的动态库 CPU 架构类型 ; 在 " ...
- 【错误记录】Android Studio 导入外部 so 动态库报错 ( java.lang.UnsatisfiedLinkError | 指定 APK 打包动态库的 CPU 架构 )
文章目录 一.报错信息 二.解决方案 ( 指定 APK 打包动态库的 CPU 架构 ) 一.报错信息 外部引用 so 动态库 , 如果只有一个 armeabi-v7a 的动态库 , 那么如果在 arm ...
最新文章
- detectmultiscale函数参数含义_OpenCV detectMultiScale函数
- 用php对文件的操作
- 用户相关的文件、解析以及命令的使用
- 支付系统整体架构详解
- java 区块链使用_使用Java创建第一个区块链
- Python标准库的强大功能的相关介绍
- js 拼接html 表格,js合并table单元格(拼table的时候并不知道具体几行几列)
- HDU2855—Fibonacci Check-up
- 字符串和字符数组的关系
- 安全强化你的 Linux 服务器的七个步骤
- opencv +opencv_contrib+CMake+VS2015
- [转]Flex 中的皮肤
- 打开的文件过多问题排查思路及解决过程
- 用matlab绘制对数图像,matlab图像绘制(进阶篇)
- 搭建-ico图标制作
- DSP-BIOS使用入门
- 华为笔记本键盘说明图_没有键盘的笔记本:华为 MateBook上手图赏
- python报错: arry[i] = t IndexError: list assignment index out of range
- fatal: unable to access ‘https://github.com/xxx/123.git/‘: Failed connect to github.com:443 解决方案
- python火柴人游戏代码_Python小游戏 Hangman