-
一个apk包也可以发布到多个市场,为什么需要多渠道?
当哪个渠道需要统计用户多少,用户粘性,需要更加个性化设计时,我们需要在apk中添加渠道信息以区分不同市场;
-
Android的两种签名方式
- Android7.0以前,使用V1签名方式,是jar signature,源自于JDK;
- Android7.0之后,使用V2签名方式,是Android独有的apk signature;
- v1方式7.0前后都可用,v2方式7.0以下会安装失败,Android Studio可以同时选择两种签名方式(一个apk中共存,根据手机系统版本自动选择);
-
多渠道打包的几种实现方式
- python脚本
1. 配置python环境
2. git clone https://github.com/GavinCT/AndroidMultiChannelBuildTool.git
3. info目录下的channel用来存放渠道,多个渠道之间用换行隔开;
MultiChannelBuildTool.py是多渠道打包的脚本;
ChannelUtil.java用来解析渠道,直接拷贝到Android工程中使用即可;
- 本质就是修改apk压缩包,将渠道信息写入META_INF中
- Android官方(gradle配置)
1. AndroidMainfest.xml中加入渠道区分标识
<meta-data android:name="channel" android:value="${channel}"/>
2. app module的build.gradle中配置 productFlavors,
flavorDimensions "default"
productFlavors {
qihu360 {
}
yingyongbao {
}
baidu {
}
huawei {
}
xiaomi {
}
}
productFlavors.all {
flavor -> flavor.manifestPlaceholders = [channelName: name]
}
//但是不得不说这个是真的慢~慢~慢~~
- 美团walle
- github地址: https://github.com/Meituan-Dianping/walle
1. 配置build.gradle
buildscript {
dependencies {
classpath 'com.meituan.android.walle:plugin:1.1.7'
}
}
apply plugin: 'walle'
dependencies {
compile 'com.meituan.android.walle:library:1.1.7'
}
2. 配置插件
walle {
// 指定渠道包的输出路径
apkOutputFolder = new File("${project.buildDir}/outputs/channels");
// 定制渠道包的APK的文件名称
apkFileNameFormat = '${appName}-${packageName}-${channel}-${buildType}-v${versionName}-${versionCode}-${buildTime}.apk';
// projectName - 项目名字
// appName - App模块名字
// packageName - applicationId (App包名packageName)
// buildType - buildType (release/debug等)
// channel - channel名称 (对应渠道打包中的渠道名字)
// versionName - versionName (显示用的版本号)
// versionCode - versionCode (内部版本号)
// buildTime - buildTime (编译构建日期时间)
// fileSHA1 - fileSHA1 (最终APK文件的SHA1哈希值)
// flavorName - 编译构建 productFlavors 名
// 渠道配置文件
channelFile = new File("${project.getProjectDir()}/channel")
}
3. 获取渠道信息
String channel = WalleChannelReader.getChannel(getApplicationContext());
LjyToastUtil.toast(mContext, "当前渠道:" + channel);
- 组件化中的多渠道
当需要生成用户端和管理端,或者个性化深度定制的VIP app时,又或者某些版本不需要微信支付,分享等功能,也就没有必要嵌入这些模块,
这样可以减少业务量和包容量;
android{
flavorDimensions "jin"
productFlavors {
higher {
applicationId "com.ljy.publicdemo"
minSdkVersion 26
buildConfigField 'boolean', 'isLite', "false"
resValue "string", "app_name_new", "JinDemo"
manifestPlaceholders = [
app_icon: "@mipmap/ic_launcher_normal",
channelName: "higher",
verNum:"2"
]
}
lower {
applicationId "com.ljy.publicdemo.lite"
minSdkVersion 17
buildConfigField 'boolean', 'isLite', "true"
resValue "string", "app_name_new", "JinDemoLite"
manifestPlaceholders = [
app_icon: "@mipmap/ic_launcher_lite",
channelName: "lower",
verNum:"1"
]
}
}
}
dependencies {
...
lowerImplementation project(':lower_setting')
higherImplementation project(':heigher_setting')
}
通过productFlavors属性来设置多渠道,manifestPlaceholders中可以设置不同渠道中的不同属性,这些属性要在AndroidManifest中声明才能使用;
<!-- 渠道声明-->
<meta-data android:name="channel" android:value="${channelName}"/>
<!-- 版本声明-->
<meta-data android:name="ver" android:value="${verNum}"/>
通过xxxImplementation 来配置不同渠道需要引用的module
//获取meta-data中的渠道号等信息
/**
* @Author: LiuJinYang
* 获取meta-data工具类
*/
public class AppMetaUtil {
public static String channelNum = "";
/**
* 获取meta-data值
* @param context 上下文对象
* @param metaName metaData key
* @return metaData值
*/
public static Object getMetaData(Context context, String metaName) {
Object obj = null;
try {
if (context != null) {
String pkgName = context.getPackageName();
ApplicationInfo appInfo = context.getPackageManager()
.getApplicationInfo(pkgName, PackageManager.GET_META_DATA);
obj = appInfo.metaData.get(metaName);
}
} catch (PackageManager.NameNotFoundException e) {
LjyLogUtil.e(e.getMessage());
}
return obj;
}
/**
* 获取渠道号
* @param context 上下文对象
* @return 渠道号
*/
public static String getChannelNum(Context context){
if (TextUtils.isEmpty(channelNum)){
Object obj=AppMetaUtil.getMetaData(context,"channel");
if (obj!=null&&obj instanceof String){
channelNum=(String) obj;
}
}
return channelNum;
}
}