Android 如何几秒生成一百个渠道包

1,212 阅读1分钟

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

Android 如何几秒生成一百个渠道包

众所周知,因为国内Android应用分发市场的现状,我们在发布APP时,一般需要生成多个渠道包,上传到不同的应用市场。这些渠道包需要包含不同的渠道信息,在APP和后台交互或者数据上报时,会带上各自的渠道信息。这样,我们就能统计到每个分发市场的下载数、用户数等关键数据。

一、普通的多渠道打包

先讲讲普通的多渠道打包

在用Eclipse的年代,我们会在AndroidManifest.xml文件中增加渠道的配置,每次打包时就会切换下渠道号再次打包。代码如下

<meta-data
            android:name="APP_CHANNEL"
            android:value="360" />

如需要切换百度渠道,则再次更改为

<meta-data
            android:name="APP_CHANNEL"
            android:value="baidu" />

如此一来,重复的工作量大大增加。于是就有了AndroidStudio上的多渠道打包。

  1. 在AndroidManifest.xml文件中,还是依旧的meta-data标签,只是属性改为动态的属性。如下
<meta-data
            android:name="APP_CHANNEL"
            android:value="${APP_CHANNEL_VALUE}" />
  1. 在build.gradle中的android标签内增加如下代码,其中APP_CHANNEL_VALUE后面的值就是渠道号的值
productFlavors {
        xiaomi {
            manifestPlaceholders = [APP_CHANNEL_VALUE: "xiaomi"]
        }
        baidu {
            manifestPlaceholders = [APP_CHANNEL_VALUE: "baidu"]
        }
    }

如提示:All flavors must now belong to a named flavor dimension. 此时还需要在defaultConfig的标签内添加flavorDimensions属性,整体的代码如下:

defaultConfig {
        applicationId "com.channel.project"
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        flavorDimensions "1" //一般与versionCode相同
    }
  1. 在代码中获取渠道号的方法
/**
     * 获取项目的渠道号
     *
     * @return
     */
    public static String getChannel(Context context) {
        ApplicationInfo ai = null;
        try {
            ai = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
            Bundle bundle = ai.metaData;
            return bundle.getString("APP_CHANNEL");
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return "";
    }
  1. 如下,在我们使用build-Generate Signed Bundle or APK时,点击Next,就会出现以下界面,全选即可,打包就在默默的进行了。

Image.png

  1. 细心的你会发现,如果项目较大或者渠道较多,会消耗大量的时间,怎么解决这个问题呢,看看walle的解决方案。

二、多渠道打包walle

带着上个的问题来探寻快速打包的秘密。

一、大概原理

为了方便理解,我把原理总结了一下,android的apk是基于zip格式压缩的,walle在zip中插入了一个区域,这个区域叫做apk sign block,所以快速打包的根本原因在于仅改变了apk sign block区域的内容,在运行时读取apk sign block中的内容。

详细原理大家有兴趣的去看看相关链接。 www.jianshu.com/p/604ffd019… 快速打包的工具当然不止walle一个,该篇仅仅是对walle实战的记录。

二、walle实战

官方github传送门:github.com/Meituan-Dia… 推荐该官方链接,更权威更详细。当然有些坑可以看issues。

  1. 引入配置文件 (注意:引入该快速打包方式前,要将以前的普通打包方式代码移除干净) 在位于项目的根目录 build.gradle 文件中添加Walle Gradle插件的依赖, 如下:
buildscript {
    dependencies {
        classpath 'com.meituan.android.walle:plugin:1.1.6'
    }
}

并在当前App的 build.gradle 文件中apply这个插件,并添加上用于读取渠道号的AAR

apply plugin: 'walle'
dependencies {
    compile 'com.meituan.android.walle:library:1.1.6'
}

并在当前App的 build.gradle 文件添加如下代码

walle {
    // 指定渠道包的输出路径
    apkOutputFolder = new File("${project.buildDir}/outputs/channels")
    // 定制渠道包的APK的文件名称
    apkFileNameFormat = 'app_${channel}-v${versionName}-${versionCode}-${buildTime}.apk'
    // 渠道配置文件appName
    channelFile = new File("${project.getProjectDir()}/channel")
}
  1. 在当前app的根目录创建channel文件,讲需要打包的渠道号依次写入,eg
meituan # 美团
samsungapps #三星
hiapk
anzhi
xiaomi # 小米
  1. 在代码中获取渠道号
WalleChannelReader.getChannel(context.getApplicationContext())

如需在debug时的默认渠道号

WalleChannelReader.getChannel(context.getApplicationContext(),"defaultChannel")

  1. 如需指定多个key-value作为渠道 1)添加channel_json,内容为
{
  "defaultExtraInfo": {
    "Channel_Name_1": "default",
    "Channel_Name_2": "default"
  },
  "channelInfoList": [
    {
      "channel": "xiaomi",
      "extraInfo": {
        "Channel_Name_1": "xiaomi_value_1",
        "Channel_Name_2": "xiaomi_value_2"
      }
    },
      {
      "channel": "360",
      "extraInfo": {
        "Channel_Name_1": "360_value_1",
        "Channel_Name_2": "360_value_2"
      }
    }
  ]
}
  1. 修改当前App的 build.gradle-walle标签
walle {
    // 指定渠道包的输出路径
    apkOutputFolder = new File("${project.buildDir}/outputs/channels")
    // 定制渠道包的APK的文件名称
    apkFileNameFormat = 'app_${channel}-v${versionName}-${versionCode}-${buildTime}.apk'
    // 渠道配置文件appName
    configFile = new File("${project.getProjectDir()}/channel_json")
//    channelFile = new File("${project.getProjectDir()}/channel")
}
  1. 对应的渠道信息获取方式如下
ChannelInfo channelInfo= WalleChannelReader.getChannelInfo(this.getApplicationContext());
if (channelInfo != null) {
   String channel = channelInfo.getChannel();
   Map<String, String> extraInfo = channelInfo.getExtraInfo();
}
// 或者也可以直接根据key获取
String value1 = WalleChannelReader.get(context, "Channel_Name_1");
String value2 = WalleChannelReader.get(context, "Channel_Name_2");

请详细注意debug时为null的情况,debug时如何使用渠道呢?了解了再更新

 /**
     * 获取项目的渠道号
     *
     * @return
     */
    public static String getChannel(Context context) {
        String value = WalleChannelReader.get(context, "Channel_Name_1");
        return value == null ? "defaultValue" : value;
    }
  1. 生成渠道包,在terminal中使用命令,生成渠道apk就在app-outputs-channels目录下
./gradlew clean assembleReleaseChannels

注:整个过程中出现Error:Plugin requires 'APK Signature Scheme v2 Enabled' for release. 1)请详细检查配置文件,确保正确性。 2)升级Gradle Plugin版本,大于2.2.0即可,如果手工设置了v2SigningEnabled=false,需要去掉。