React-Native多渠道打包(android)

Posted by 鸿杰 on July 19, 2016

在AndroidManifest.xml中一般都会有当前渠道的配置信息,以talkingData为例:

<meta-data android:name="TD_CHANNEL_ID" android:value="渠道名" />

所以在打包之前我们得有一个自动修改当前渠道的脚本。下面以python为例:

# 将指定文件中符合指定正则的string替换为replaceStr
def modifyConfig(filePath, pattern, replaceStr):
	file = codecs.open(filePath, 'r', 'utf-8')
	fileContent = file.read()
	file.close()

	fileContent = re.sub(pattern, replaceStr, fileContent)

	file = codecs.open(filePath, 'wb', 'utf-8');
	file.write(fileContent)
	file.close()

# 修改AndroidManifest
modifyConfig(
	r".\app\src\main\AndroidManifest.xml",
	re.compile(r'<meta-data android:name="TD_CHANNEL_ID" android:value=".+" />'),
	u'<meta-data android:name="TD_CHANNEL_ID" android:value="新的渠道" />'
)	

然后加上打包指令:gradlew assembleRelease

# 开始打包
ps = subprocess.Popen("gradlew assembleRelease", shell = True)
ps.wait()

将该python文件移动到RN根目录中的android目录下,然后执行,不出意外的话,将会生成一个渠道名为新的渠道的安装包。那么问题来了,生成的安装包名都是app-release.apk,如果批量生成多个安装包,那不岂不是会相互覆盖吗?

so,下一步便是修改生成apk的文件名。打开android\app\build.gradle文件,将applicationVariants.all的逻辑修改如下:(从def outputFile = output.outputFile开始是新加的逻辑)

applicationVariants.all { variant ->
    variant.outputs.each { output ->
        // For each separate APK per architecture, set a unique version code as described here:
        // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits
        def versionCodes = ["armeabi-v7a":1, "x86":2]
        def abi = output.getFilter(OutputFile.ABI)
        if (abi != null) {  // null for the universal-debug, universal-release variants
            output.versionCodeOverride =
                    versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
        }
        
        def outputFile = output.outputFile
        if (outputFile != null && outputFile.name.endsWith('.apk')) {
            //这里修改apk文件名
            def fileName = outputFile.name.replace("app", "测试");
            output.outputFile = new File(outputFile.parent, fileName)
        }
    }
}

新增加的逻辑便是修改apk的文件名了。

同时在打包python里加上修改build.gradle的逻辑,以便让AndroidManifest.xml中的渠道名就是build.gradle中的apk文件名。

# 修改build.gradle
modifyConfig(
	r".\app\build.gradle",
	re.compile(r'def fileName = outputFile.name.replace\("app", ".+"\);'),
	u'def fileName = outputFile.name.replace("app", "%s");' % channel
)

此时,再执行python,便会得到名为新的渠道-release.apk的安装包了。

最后便是加入多个渠道的打包逻辑,完整的python脚本如下:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# 

'''
多渠道自动打包工具
'''

__author__ = "zhouhongjie@apowo.com"

import os, re, codecs, subprocess

# 渠道列表
channelList = ["snpole", u"应用宝", "360"]

def modifyConfig(filePath, pattern, replaceStr):
	file = codecs.open(filePath, 'r', 'utf-8')
	fileContent = file.read()
	file.close()

	fileContent = re.sub(pattern, replaceStr, fileContent)

	file = codecs.open(filePath, 'wb', 'utf-8');
	file.write(fileContent)
	file.close()

def createAPK(channel):
	# 修改AndroidManifest
	modifyConfig(
		r".\app\src\main\AndroidManifest.xml",
		re.compile(r'<meta-data android:name="TD_CHANNEL_ID" android:value=".+" />'),
		u'<meta-data android:name="TD_CHANNEL_ID" android:value="%s" />' % channel
	)
	# 修改build.gradle
	modifyConfig(
		r".\app\build.gradle",
		re.compile(r'def fileName = outputFile.name.replace\("app", ".+"\);'),
		u'def fileName = outputFile.name.replace("app", "%s");' % channel
	)
	
	# 开始打包
	ps = subprocess.Popen("gradlew assembleRelease", shell = True)
	ps.wait()

if __name__ == '__main__':
	for x in channelList:
		createAPK(x);
	print(u"搞定了")

我这个是比较简单粗暴的打包脚本了,看上去似乎没啥技术含量,但是其实很多问题只要解决了,不管技术方案如何,那就是个好的方案。

那有没有更高大上的解决方案呢?诺,拿去: packer-ng-plugin