标签:
来对多渠道打包,并生成不同的包名的知识点做个总结。需要生成不同包名的原因是为了运营的ASO。
当然上面两种方法各有优劣,最后说一下他们的各自的一些特点。
例如:我这里需要两个不同包名的包,那就需要建立两个不同渠道的文件夹。
因为包名是在Manifest文件里面定义的,所以需要建立Manifest文件,当然这个Manifest文件里面拷贝过来,你可能会觉得太麻烦,我只是替换一个包名需要把整个Manifest都拷贝过来么,这也太坑了,其实没有这么严重,包名一般包括在用户手机桌面上面显示的名字,和你去应用程序管理器里面看到的名字,这两个名字是可以不同的。
在应用程序管理器里面显示的名字是由application节点的android:label属性决定的。
用户手机桌面上面显示的名字是由应用程序的第一个Activity的android:label决定的,如果第一个Activity没有指定label属性的话,就是使用的Application的android:label的名字。
顺便提一句,应用程序的第一个Activity是有下面的intent-filter的Activity。
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
这里我们明白了,应用程序的两个包名,那么既然我们需要改变的只是包名而已,就只需要把application节点和第一个Activity在Manifest里面的配置拷贝过来就可以了。
所以这里需要的两个Manifest文件分别是这样的:
先贴一下main渠道的主的Manifest文件:
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.name.replace"
xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
...
其他配置
...
</application>
</manifest>
app_name_1渠道:
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.name.replace"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name_1"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:replace="android:label">
<activity
android:name=".MainActivity"
android:label="@string/desk_name_1"
tools:replace="android:label">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
app_name_2渠道:
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.name.replace"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name_2"
android:supportsRtl="true"
android:theme="@style/AppTheme"
tools:replace="android:label">
<activity
android:name=".MainActivity"
android:label="@string/desk_name_2"
tools:replace="android:label">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
这里可以看到app_name_1里面的Manifest和app_name_2里面的Manifest并没有太大的差别,只是包名改了一下而已。这两个文件没有什么可比较的价值。
着重看一下app_name_2渠道的Manifest和main里面的Manifest的差别,如果你说没有差别,那就比较尴尬了,妈蛋,我450度的眼镜都看出差别了。
xmlns:tools=”http://schemas.android.com/tools”
并且在application节点和第一个Activity节点添加了tools:replace=”android:label”语句。
如果没有添加Gradle在使用Manifest Merger Tool进行合并Manifest的时候出出问题。
不需要main里面的其他的一些四大组件相关的一些东西。
productFlavors {
app_name_1 {}
app_name_2 {}
app_main {}
}
这里的渠道名要和一开始建立的文件夹的名字一样,如果不一样,打出的包就是main里面的包。
得益于gradle对于占位符的支持,第二种方法远没有第一种方法那么麻烦。
关于占位符的用法有很多,比如可以动态的定义Activity的名字,Service的名字等。详细的可以自行Google。
关于gradle的清单合并可以看这一篇翻译的文章,讲解的非常详细:
清单合并:http://blog.csdn.net/maosidiaoxian/article/details/42671999
由于不需要建立文件夹,这里说的Manifest就是main里面的Manifest。
当属性值包含一个占位符时,合并工具将把此占位符的值换成一个注入的值。注入的值是在build.gradle里面定义的。
占位符值的语法是 ${name},因为@符号已经预留给了链接。在最后的文件合并发生之后,并且生成合并后的 android 的清单文件输出之前,带有占位符的所有值将都会被替换为注入的值。如果变量名是未知的,将导致构建失败。
Manifest文件:
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.name.replace"
xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="${app_name}"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<meta-data
android:name="UMENG_CHANNEL"
android:value="${umeng_channel}"/>
<activity
android:name=".MainActivity"
android:label="${desk_name}">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
这里定义了三个占位符,app_name,desk_name,umeng_channel。
占位符可以替换的还有很多用处,想了解更多的可以自己查资料。
这里贴出整个Manifest文件,供大家更好的看清。
apply plugin: ‘com.android.application‘
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
applicationId "com.name.replace"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
manifestPlaceholders = [
app_name : "@string/app_name",
desk_name : "@string/app_name",
umeng_channel: "测试版"
]
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile(‘proguard-android.txt‘), ‘proguard-rules.pro‘
}
}
productFlavors {
// app_name_1 {}
// app_name_2 {}
// app_main {}
app_qq {
manifestPlaceholders = [
app_name : "@string/app_name_1",
desk_name : "@string/desk_name_1",
umeng_channel: "应用宝"
]
}
app_pp {
manifestPlaceholders = [
app_name : "@string/app_name_2",
desk_name : "@string/desk_name_2",
umeng_channel: "pp助手"
]
}
}
}
dependencies {
compile fileTree(dir: ‘libs‘, include: [‘*.jar‘])
testCompile ‘junit:junit:4.12‘
compile ‘com.android.support:appcompat-v7:23.3.0‘
}
可以看到我们对Manifest里面定义的三个占位符做了替换。
这里我们既在defaultConfig里面定义了,又在不同的渠道里面做了再次定义,最后渠道里面的优先级会高。
其实如果一开始没有配置渠道信息,但是我们定义了占位符的话,是编译不过去的,必须在defaultConfig里面定义默认的一些属性。
最开始的时候我们说了这两种方法各有优缺点,在以前的公司的时候采用的是第一种方案,原因有两点:
1.采用第一种方案,可以对每个渠道加上渠道的logo,比如360的渠道需要360的logo,百度的渠道需要百度的logo,那么采用第一种方法就很好实现了,把main里面的res/layout里面的启动界面拷贝一份到对应的渠道文件里面进行修改,替换logo。
第一种方法里面不止可以放置Manifest文件,还可以放置res目录。
2.当时的渠道包不需要我们技术人员去打,由运营去打,技术这边当时是开发了一个工具供运营那边打渠道包专用,技术这边只需要打好不同的包名的包即可。
所以第一种方法的优点是可以完成一些其余的特殊的操作,缺点是比较麻烦。第二种方法是实现比较简单,但是有些特殊的操作就不能行了。
标签:
原文地址:http://blog.csdn.net/qy274770068/article/details/51213878