Android一键多渠道分发打包实战和解析
当项目需要有更多的客户的时候,你就会考虑将apk上架到应用商店了,无奈天朝Android应用商店真的是百家争鸣,据某地不完全统计已经有900+。若将Apk上架到所有的应用商店是个好主意,但是据统计也就那么十来个应用商店的占有率已经超过95%了,所以我觉得并没有必要上架所有应用商店。这里就好比Android里面适配机型一个道理,机型无数,但是也就那么几个品牌占有的绝大多数市场。
话说回来为什么要打渠道包(比如说应用宝要发一个apk,小米应用商店要发一个apk等等),而不是同一个apk放到每一个应用商店呢?主要有下列原因:
1. 不同的应用商店对于apk的要求不同,有的甚至需要一些定制,比如某些应用商店要求在启动页加入商店的Logo
2. 为了跟踪每家应用商店的用户数等等统计数据
幸运的是gradle天生就提供了打多渠道包的功能,这里我们主要解决下列问题:
- 如何打多个渠道的包
- 如何支持友盟统计
- 如何在代码中根据不同渠道做执行不同的代码块,即渠道定制
- 如何支持多包名打包
- 一种对于BuildConfig的更有意思的用法(彩蛋)
修改build.gradle和预编译BuildConfig类解析
在moudle的build.gradle的android元素下,有这么productFlavors这么一个函数,它支持不同“口味”的版本编译。比如我们需要做一个应用宝的渠道版本,可以添加下列代码:
android { productFlavors { YingYongBao { manifestPlaceholders = [UMENG_CHANNEL:"YingYongBao"] buildConfigField "String", "CHANNEL", "\"YingYongBao\"" } }
随后在AndroidManifest.xml的配置友盟渠道号的地方改成:
<meta-data android:name="UMENG_CHANNEL" android:value="${UMENG_CHANNEL}" />
productFlavors中的manifestPlaceholder类似于给AndroidManifest文件定义了一个变量,上述例子中改变量名字就加UMENT_CHANNEL,值为字符串YingYongBao,这样子改渠道打包出来的apk即可以发布到应用宝市场上了。同时你在友盟的后台统计模块控制台中,也可以看到一个叫做“YingYongBao”的渠道来源,可以跟踪该渠道的用户使用数据。这样我们要解决的第2个问题OK了。
productFlavors中的buildConfigField是在等于在预编译期间创建了一个变量叫做CHANNEL,类型是字符串类型,值是字符串“YingYongBao”。
当我们写C/C++代码的时候通常会写预编译宏变量,可以控制代码中在不同编译条件下的执行分支。比如:
#ifdef _MACRO_YING_YONG_BAO { // code for yingyongbao } #else { // code for others } #endif
当需要执行应用宝那部分代码的时候则编译时打开宏_MACRO_YING_YONG_BAO。而Android Studio也提供了类似的机制,它在预编译阶段生成了一个叫做BuildConfig.java的类,它位于:
当我们预编译时会自动生成这么一个类,按照上述编译应用宝渠道的操作中,我们看下这个BuildConfig.java是什么内容:
这里发现有一个叫做CHANNEL的String常量,而且值是“YingYongBao”,所以想到那么这里为了让不同渠道执行不同的代码,我们于是可以在代码中这样子写:
if (BuildConfig.CHANNEL.equals("YingYongBao")) { // code block for yingyongbao } else if (BuildConfig.CHANNEL.equals("360") { // code block for 360 } else { // default code }
这里需要注意的一点,通过buildConfigField定义的变量中,如果是String类型的话,必须要在值里面写上转义符号\",或者写成:
buildConfigField "String", "CHANNEL", ‘"YingYongBao"’
否则的话预编译器会认为YingYongBao不是一个值而是另一个变量,从而报错:
到此,第3个问题也解决了!
在Android Studio中一键打包
到现在还没有说如何打包的事情,这点很简单,只要打开Android Studio的gradle面板,刷新task列表,会发现很多task,只要双击下assembleRelease这个task即可。稍等一段时间,Android Studio自动帮你生成所有的渠道的Apk。
生成的所有apk在这里:
这里有两个注意点:
- 执行打包前,最好先执行下clean那个task
- build那个task也可以打所有渠道的包,但是比较慢,因为它同时也打出来debug包,而assembleRelease只打release包,理论上减少一半时间
问题1解决
多包名支持
在build.gradle中是用applicationId来表述包名的,在productFlavors中是可以嵌入applicationId的定义,比如我们在Beta这样的渠道中想要打出一个com.qianmi.xxx.beta的包名的apk,可以这么写:
productFlavors { Beta { manifestPlaceholders = [UMENG_CHANNEL:"Beta"] buildConfigField "String", "CHANNEL", "\"Beta\"" applicationId "com.qianmi.xxx.beta" } }
问题4解决
BuildConfig的一种有意思的用法(彩蛋)
我们知道发布出去的apk都是带有签名的,而从Android Studio等IDE中直接run到手机上的是没有签名的,但是两个手机,一台装了签名的apk一台是直接run上去的,你能区分出来吗(实际是看不出来的)?
如果我们能动态的区分出来,同时对于直接从IDE运行出来的Apk中在app的某个界面(比如关于界面)有下列标志多好?
而在正式发布出来的版本中没有“开发版本”这个字串。
首先我们在build.gradle中的android元素下的buildTypes函数中加入:
buildTypes { release { minifyEnabled true zipAlignEnabled true shrinkResources true signingConfig signingConfigs.release proguardFiles 'proguard-rules.pro' } debug { minifyEnabled false zipAlignEnabled true shrinkResources true buildConfigField "String", "CHANNEL", "\"dev\"" } }
这里有一个release和一个debug。当你通过上述讲解中的gradle task中打包时是release,打包时读取具体的productFlavors。而如果你是直接从IDE运行出来调试的话,会检查buildTypes中debug这个地方,这里我们同样把CHANNEL这个常量给定义出来了,值是dev。
于是我们再在关于界面,写上下列语句即可:
if (BuildConfig.CHANNEL.equals("dev")) { tvVersion.setText("v" + AppUtils.getVersionName(getApplicationContext()) + " " + getString(R.string.dev_version)); } else { tvVersion.setText("v" + AppUtils.getVersionName(getApplicationContext())); }
题外话
到此为止,多渠道打包问题已经解决。但是打包的时候发现如果渠道越多实际上打包的时间是越慢的,因为Android Studio在为每个渠道一个个的生成apk,所以我们这里提供的方式仅仅适用于渠道比较少的情况下。如果你真的想要在900个以上的应用市场发布你们的apk,那么显然这种方式就不行了。这里提供两个解决方法。
一个是对同一个base的apk进行修改的方式,可以参考美团技术团队的博客:
http://tech.meituan.com/mt-apk-packaging.html
或者
http://blog.csdn.net/luck_apple/article/details/8634572?utm_source=tuicool&utm_medium=referral
第二个是试用一些厂商的支持多渠道打包的软件,比如360的加固宝:
文末附上一家统计公司提供的国内应用市场占有率的报告:
转载请注明出处: http://www.cnblogs.com/soaringEveryday/p/5368540.html