1、为什么要进行多渠道打包?
安卓应用商店(一个商店也叫做一个渠道,如豌豆荚,360手机助手,应用宝)众多,大大小小几百个,每当我们发新版本时,需要将Android客户端分发到各个应用市场,为了统计这些市场的效果(下载量、活跃数等),需要有一种方法来唯一标识它们,所以才有了多渠道打包。
2、如何统计各个渠道的下载量、活跃数?
现在有比较成熟的第三方应用帮我们实现统计功能(比如友盟),统计的本质就是收集用户信息传输到后台,后台生成报表,帮助我们跟踪分析并完善app。通过android系统的方法已经可以获取到引用版本号,版本名称,系统版本,机型等各种信息,唯独应用商店(渠道)的信息我们是没有办法从系统获取到的,所以我们就人为的在apk里面添加渠道信息(其实就用一个字段进行标识,如wandoujia
,360
,yingyongbao
),我们只要把这些信息打包到apk文件并将信息传输到后台,后台根据这个标识,可以统计各个渠道的下载量了。
多渠道打包只需要关注两件事情:
- 将渠道信息写入apk文件
- 将apk中的渠道信息传输到统计后台
3、使用Gradle进行多渠道批量打包**
-
通常都是在在
AndroidManifest.xml
中加入渠道区分标识写入一个meta标签:<meta-data android:name="channel" android:value="${channel}" />
-
在app目录下在build.gradle中配置productFlavors添加如下代码:
productFlavors { qihu360 {} // 360手机助手 yingyongbao {} // 腾讯应用宝 wandoujia {} // 豌豆荚 baidu {} // 百度手机助手 miui {} // 小米 flyme {} // 魅族 lenovo {} // 联想-乐商店 oppo {} // Oppo-可可软件商店 huawei {} // 华为 vivo {} // vivo hiapk {} // 安卓市场 sj91 {} // 91手机助手 sogou {} // 搜狗手机市场 sohu {} // 搜狐应用中心 taobao {} // 淘宝手机助手 gfan {} // 机锋 appchina {} // 应用汇 mumayi {} // 木蚂蚁 wangyi163 {} // 网易应用 nduoa {} // N多市场 mm10086 {} // MM商城-中国移动 wostore {} // WO商店 youyi {} // 优异 uc {} // UC+开发平台 anzhi {} // 安智市场 google {} // google play amazon {} // 亚马逊 } productFlavors.all { flavor -> flavor.manifestPlaceholders = [channel: name] }
AndroidStudio的
Build -> Generate signed apk
打签名包时即可选择设置渠道:
在代码中获取渠道信息:
public static String getFlavorChannel(Context context) {
try {
PackageManager pm = context.getPackageManager();
ApplicationInfo appInfo = pm.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
return appInfo.metaData.getString("channel");
} catch (PackageManager.NameNotFoundException ignored) {
}
return "";
}
- 在android studio中测试不同渠道的apk。
4.APP定制渠道包(马甲包)
在App的开发过程中,经常会遇到产品或者运营的同事提出要制作马甲包的需求;马甲包简单来说就是原APP的小号,与原APP包除了包名,应用名称、图标等给用户加以区分的东西,其他功能基本不变亦或者只采用原App的部分功能的APP包。
-
ApplicationId,版本号
Android 应用都有自己的包名。包名是设备上每个应用程序的唯一标识,同样也是在各个下载平台的唯一标识。就是说,假如你已经使用某个包名来发布应用,就不能再去改变应用的包名,因为这样做会导致你的应用被视为一个全新的应用,你现有的用户也不会收到应用的更新通知。
随着渠道越来越多,不同渠道对应用的要求也不尽相同。有时候我们需要发布不同的版本,例如 pro,hd 版本,支持用户可以下载安装不同的版本。那么我们需要设置不同的ApplicationId和对应的版本号, 同时要与 PackageName 解耦合。
- 代码中引用的 R 类要保持不变;
- 在构建不同版本的应用时,对应的(引用了 R 的) .java 源文件也不能改动。
那么我们只需要在productFlavors
对应的渠道中指定applicationId
和versionCode
,versionName
,例我们指定GooglePlay的applicationId
:
productFlavors {
GooglePlay {
//指定这个渠道的版本号
versionCode 2
versionName "1.2"
//指定区别于其他渠道的 applicationId
applicationId "com.liujc.androidtools.hd"
}
yingyongbao {} // 腾讯应用宝
wandoujia {} // 豌豆荚
baidu {} // 百度手机助手
miui {} // 小米
//其他...
}
-
BuildConfig
Gradle会在generateSources阶段为flavor生成一个BuildConfig.java
文件。BuildConfig类默认提供了一些常量字段,比如应用的版本名(VERSION_NAME
),应用的包名(PACKAGE_NAME
)等。更强大的是,开发者还可以添加自定义的一些字段。下面的示例假设debug
版开启LOG功能,使用debug的api,而release
版则使用不开启LOG和使用release时的api:
那么代码中就可以使用 BuildConfig.LOG_DEBUG 和 BuildConfig.API_HOST 了。buildTypes { debug { // debug模式下,显示log buildConfigField("boolean", "LOG_DEBUG", "true") buildConfigField ("String", "API_HOST", "\"http://api.test.com\"")//debug API Host } release { // release模式下,不显示log buildConfigField("boolean", "LOG_DEBUG", "false") buildConfigField("String", "API_HOST", "\"http://api.release.com\"")//release API Host } }
备注: 这里简单介绍下buildConfigField
方法,可以发现其有三个参数:/** * Adds a new field to the generated BuildConfig class. * * <p>The field is generated as: {@code <type> <name> = <value>;} * * <p>This means each of these must have valid Java content. If the type is a String, then the * value should include quotes. * * @param type the type of the field * @param name the name of the field * @param value the value of the field */ public void buildConfigField( @NonNull String type, @NonNull String name, @NonNull String value)
String type 要创建的字段类型 如上面的String与boolean String name 要创建的字段名 如上面的API_HOST与LOG_DEBUG String value 创建此字段的值 如上面的"\"http://api.release.com\""与"true" 转义字符
。
-
控制是否自动更新
一般应用在启动时都会默认检查客户端是否有更新,如果有更新就会提示用户下载。但是有些渠道和应用市场不允许这种默认行为,所以在适配这些渠道时需要禁止自动更新功能。
解决的思路是提供一个配置字段,应用启动的时候检查该字段的值以决定是否开启自动更新功能。使用flavor可以完美的解决这类问题。甚至可以在productFlavors对应的渠道号进行区别,例如豌豆荚版默认禁止版本自动更新:android { defaultConfig { buildConfigField "boolean", "AUTO_UPDATES", "true" } productFlavors { wandoujia { buildConfigField "boolean", "AUTO_UPDATES", "false" } } }
多渠道资源文件定制
客户端经常会和一些应用分发市场合作,需要在应用的启动界面中加上第三方市场的Logo,类似这类适配形式还有很多。通常对于不同渠道,我们会区别不同的资源。例如我们一款应用需要在360发布,而应用图标和欢迎界面要一个360标志的图或者应用名称和其他渠道不同,那么这个时候就需要按渠道打包对应的应用图标和欢迎图片以及应用名称了了。
Gradle在构建应用时,会优先使用flavor所属dataSet中的同名资源。所以,在flavor的dataSet中添加同名的字符串资源,以覆盖默认的资源。上面我们已经有针对360的渠道了,就是qihu360,我们只需要在app/src/目录下添加渠道对应的文件夹qihu360,然后覆盖对应要覆盖的内容。下面是定制应用图标的步骤:
- 添加qihu360文件夹,那么在
app/src/
目录下面就有 main , androidTest , qihu360,test这四个文件夹了。main 目录是通用正常渠道包目录,qihu360是我们需要定制资源的渠道包目录。如下图:
- 并添加如下应用名字符串资源
src/qihu360/res/values/strings.xml
:
默认的应用名字符串资源如下(<resources> <string name="app_name">360MultiChannelBuild</string> </resources>
src/main/res/values/strings.xml
):
最后运行360版本的app即应用名称显示成360MultiChannelBuild。<resources> <string name="app_name">MultiChannelBuild</string> </resources>
通过以上例也可以定制其他资源,包括drawable
,styles.xml
甚至AndroidManifest.xml
也都是可以的。即可实现不同渠道不同的应用显示。
-
使用第三方SDK
某些渠道会要求客户端嵌入第三方SDK来满足特定的适配需求。问题的难点在于如何只为特定的渠道添加SDK,其他渠道不引入该SDK。使用flavor可以很好的解决这个问题,下面以为qihu360
flavor引入com.qihoo360.union.sdk:union:1.0
SDK为例进行说明:
android {
productFlavors {
qihu360 {
}
}
}
...
dependencies {
//参与编译但不参与打包
provided 'com.qihoo360.union.sdk:union:1.0'
//指定qihu360这个渠道可以打包这个库
qihu360Compile 'com.qihoo360.union.sdk:union:1.0'
}
上例添加了名为qihu360的flavor,并且指定编译和运行时都依赖com.qihoo360.union.sdk:union:1.0
。而其他渠道只是在构建的时候依赖该SDK,打包的时候并不会添加它。
接下来,需要在代码中使用反射技术判断应用程序是否添加了该SDK,从而决定是否要显示360 SDK提供的精品应用。部分代码如下:
class MyActivity extends Activity {
private boolean useQihuSdk;
@override
public void onCreate(Bundle savedInstanceState) {
try {
Class.forName("com.qihoo360.union.sdk.UnionManager");
useQihuSdk = true;
} catch (ClassNotFoundException ignored) {
}
}
}
最后打包运行即可生成包含360精品应用模块的渠道包了。