android基础:多渠道打包和代码混淆

多渠道打包

AndroidManifest.xml文件解析

补充知识
AndroidManifest.xml 是每个android程序中必须的文件。它位于整个项目的根目录,描述了package中暴露的组件(activities, services, 等等),他们各自的实现类,各种能被处理的数据和启动位置。 除了能声明程序中的Activities, ContentProviders, Services, 和Intent Receivers,还能指定permissions和instrumentation(安全控制和测试)

1 <meta-adata>

语法

1
2
3
<meta-data android:name="string"
android:resource="resource specification"
android:value="string"/>

被包含于

<activity>
<activity-alias>
<service>
<receiver>
四个元素中。

说明
这个元素用name-value对的格式给其父组件提供任意可选的数据。
一个组件元素能够包含任意多个<meta-data>子元素,所有这些元素中定义的值会被收集到一个Bundle对象中,并且提供给组件的PackageItemInfo.metaData属性字段。
通常值是通过其value属性来指定的。但是,也可以使用resource属性来代替,把一个资源ID跟值进行关联。

例如,下面的代码就是把存储在@string/kangaroo资源中的值跟”zoo”名称进行关联:

1
<meta-data android:name="zoo" android:value="@string/kangaroo" />

另一个方面,使用resource属性会给zoo分配一个数字资源ID,而不是保存在资源中的值。例如:

1
<meta-data android:name="zoo" android:resource="@string/kangaroo" />

要避免使用多个独立的实体来提供相关的数据。相反如果有复杂的数据要跟组件关联,那么把数据作为资源来保存,并使用resource属性,把相关的资源ID通知给组件。

属性

  • android:name
    针对项目的一个唯一名称。使用Java样式的命名规则,可以确保名称的唯一性,例如:com.example.project.activity.fred
  • android:resource
    这个属性定义了一个要引用的资源。资源的ID会跟这个项目进行关联。通过Bundle.getInt()方法能够从meta-data的Bundle对象中获取这个ID。

  • android:value
    这个属性会给这个项目分配一个值。下表列出了可能分配的数据的数据类型,以及获取这些数据的方法:

多渠道打包

以友盟统计为例,在AndroidManifest.xml里面会有

1
2
3
<meta-data
android:name="UMENG_CHANNEL"
android:value="Channel_ID" />

Channel_ID就是渠道标示,我们的目标就是在编译的时候这个值能够自动变化。

1 在AndroidManifest.xml里配置PlaceHolder

1
2
3
<meta-data
android:name="UMENG_CHANNEL"
android:value="${UMENG_CHANNEL_VALUE}" />

${UMENG_CHANNEL_VALUE}这是个引用,具体值在gradle中配置

2 在build.gradle设置productFlavors

首先配置UMENG_CHANNEL_VALUE的默认值

1
2
3
4
5
defaultConfig {
···
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "channel_name"]
···
}

然后配置不同的渠道id

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
android {  
...
productFlavors {
xiaomi {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "xiaomi"]
}
_360 {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "_360"]
}
baidu {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "baidu"]
}
wandoujia {
manifestPlaceholders = [UMENG_CHANNEL_VALUE: "wandoujia"]
}
}
...
}

更简洁的写法,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 友盟多渠道打包
productFlavors {
wandoujia {}
c360 {}
baidu {}
xiaomi {}
tencent {}
taobao {}
...
}

productFlavors.all { flavor ->
flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
}

然后在签名配置完成后,就可以使用命令行打包了

gradle命令行打包

1 使用gradle命令配置签名

首先在app的build.gradle文件配置要签名的keystore文件

为了不暴露密码,不直接在signingConfigs中配置密码,需要在控制台输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
def releaseTime() {
return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
}

android {
...

//执行lint检查,有任何的错误或者警告提示,都会终止构建,我们可以将其关掉。
lintOptions {
abortOnError false
}


signingConfigs {
debug {
// No debug config
}
//release版本
release{
storeFile file('../keystore文件名')
keyAlias 'asar'
storePassword System.console().readLine("\nKeystore password: ")
keyPassword System.console().readLine("\nKey password: ")
}
}


buildTypes {
debug {
// 显示Log
buildConfigField "boolean", "LOG_DEBUG", "true"

versionNameSuffix "-debug"
minifyEnabled false
zipAlignEnabled false
shrinkResources false
signingConfig signingConfigs.debug
}
release {
...
// 不显示Log
buildConfigField "boolean", "LOG_DEBUG", "false"
//混淆
minifyEnabled true
//zipAlgn优化
zipAlignEnabled true
//移除无用的resource文件
shrinkResources true

//加载默认的混淆配置文件
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
//签名
signingConfig signingConfigs.release
//配置输出的apk文件名
applicationVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk')) {
// 输出apk名称为zlot_v1.0_2015-01-15_wandoujia.apk
def fileName = "zlot_v${defaultConfig.versionName}_${releaseTime()}_${variant.productFlavors[0].name}.apk"
output.outputFile = new File(outputFile.parent, fileName)
}
}
}



}
}

...

2 使用命令行打包

1 view->ToolWindows->Terminal打开命令行
2 在命令行输入gradlew -v ,如果第一次会执行下载gradle,记得翻墙,不然会慢到崩溃
3 执行gradlew clean,等待时间会比较长
4 执行gradle build,开始编译,成功后zlot\build\outputs\apk目录会有编译后的apk,
这时编译的apk包括debug版本,release版本,特别慢
补充

  • gradlew assembleRelease :只编译并打Release的包
  • gradlew assembleDebug :只编译并打包debug版本

除此之外 assemble 还能和 Product Flavor 结合创建新的任务,其实 assemble 是和 Build Variants 一起结合使用的,而Build Variants = Build Type + Product Flavor , 举个例子大家就明白了:

  • gradlew assembleWandoujiaRelease只打包wandoujia渠道的release包

3 比命令行更简单的打包方式

打开androidStudiode gradle面板,会发现多了很多任务,

然后直接双击任务生成apk

错误处理

当build faile时,找不到错误的原因,可以执行gradlew check进行检测,一般会给出错误原因,
比如

1
2
3
4
5
6
7
8
ndroid {
lintOptions {
abortOnError false
}
}
...

* Try:

意义是让我们关闭lint检查,因为开启的话,有任何的错误或者警告提示,都会终止构建

代码混淆

参考文献

Android 使用Android Studio + Gradle 或 命令行 进行apk签名打包

Android Studio系列教程六–Gradle多渠道打包

Android studio 使用心得(四)—android studio 多渠道打包(二)

[Android Studio] Android studio 多渠道打包(超简洁版)