为什么要多渠道

App一般会上传多个应用商店,例如应用宝、小米、华为、OPPO等。

现在的手机都自带应用商店,用户一般会在自带应用商店上搜索。

产品、运营可统计使用不同手机品牌的用户的消费情况,App日活、月活、UV、PV等。

传统方式打包的痛点

如果App不是很出名,一般都需要自己打包上传,除非像微信这种,商店自动抓包进行上传。曾经参加过一个技术沙龙,微信的工程师说到,他们的热更新技术,如果使用了360加固进行,就是失效,但是360应用商店必须加固后才能上传,所以他们就不上传了,最后360应用商店也还是抓包上传了(可见有实力就是不一样)。

不同的渠道,可能App名字不同,App图标Icon不同,如果手动替换的话,体力劳动特别累人,笔者就曾经做过一个app,需要给不同的景区生成app,每个景区的图标和app名字都不一样,传统方式就是打包完一个,手动替换再进行打包,10个还好,慢慢后面景区越来越多,上升了50个,工作量可想而知,而且还容易错,每个都需要手动检查一遍(曾经花2个小时,对着屏幕一个个替换,眼睛都花了,自然容易出错!),这样进行几次后,意识到这种重复劳动不应该由我们来做,应该交给机器呀!

Gradle配置多渠道打包

后面我们搜索了一下资料,决定使用Gradle配置的方式进行打包,但他也有优缺点。

优点:就是每个包都只需要配置好要替换的文件和占位,即可开始打包。

缺点:每次打包一个渠道包,都需要编译一次,项目非常大的时候,可谓非常耗时!

Manifest占位,动态替换meta标签值,动态标识渠道

实现步骤

配置App模块的build.gradle文件。

使用productFlavors,添加2个渠道,例如360和应用宝。

注意如果是3.0版本的Gradle,必须要添加上flavorDimensions纬度。

使用manifestPlaceholders,增加清单文件占位,格式:[占位名:值, 占位名:值]。

apply plugin: 'com.android.application'

android {

//省略其他配置...

//3.0版本Gradle开始必须添加纬度

flavorDimensions "default"

//多渠道打包配置

productFlavors {

//渠道包

app_360 {

manifestPlaceholders = [channel: "app_360"]

}

app_qq {

manifestPlaceholders = [channel: "app_qq"]

}

}

}

清单文件增加占位标识

例如我们使用友盟进行渠道记录,增加一个meta_data标签,标签名为UMENG_CHANNEL,值是占位符标识${channel}。

注意这个标识要和第一步的build.gradle文件中的productFlavors配置一致

例如build.gradle。

//占位名:channel,值:app_360

manifestPlaceholders = [app_name: "360渠道包", channel: "app_360"]

meta标签。

android:name="UMENG_CHANNEL"

android:value="${channel}" />

完整配置

package="me.zh.makechannelpackage">

android:allowBackup="true"

android:icon="@mipmap/ic_launcher"

android:label="@string/app_name"

android:supportsRtl="true"

android:theme="@style/AppTheme">

android:name="UMENG_CHANNEL"

android:value="${channel}" />

在Java代码层读取meta标签,获取其值即可。

//渠道工具类

public class ChannelUtil {

/**

* 获取app包内的渠道标识

*/

public static String getChannel(Context context) {

try {

PackageManager manager = context.getPackageManager();

ApplicationInfo appInfo = manager.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);

return appInfo.metaData.getString("UMENG_CHANNEL");

} catch (PackageManager.NameNotFoundException e) {

e.printStackTrace();

return "";

}

}

}

//调用

public class MainActivity extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

String channel = ChannelUtil.getChannel(getApplicationContext());

Toast.makeText(getApplicationContext(), "当前渠道:" + channel, Toast.LENGTH_SHORT).show();

}

}

给不同的编译环境配置清单Key

除了productFlavors中配置manifestPlaceholders生成渠道包外,在编译环境buildTypes下也可以使用,例如不同编译环境下,高德地图的key不同,就可以在buildTypes中使用。

修改build.gradle配置。

buildTypes {

debug {

manifestPlaceholders = [amap_key: "amp_debug_xxxxxkey"]

//省略其他配置...

}

release {

manifestPlaceholders = [amap_key: "amp_release_xxxxxkey"]

//省略其他配置...

}

}

清单文件中添加高德Key配置。

package="me.zh.makechannelpackage">

android:allowBackup="true"

android:icon="@mipmap/ic_launcher"

android:label="@string/app_name"

android:supportsRtl="true"

android:theme="@style/AppTheme">

android:name="com.amap.api.v2.apikey"

android:value="${amap_key}" />

Lib模块共享,App接入问题

一般我们都会使用多个Lib进行分模块开发,例如支付模块的Pay库中的微信回调Activity,7.0获取文件需要使用FileProvider等,都需要制定包名,但是我们的lib需要提供给其他App进行接入,清单文件的配置需要接入方配置,如果配置比较多,而且后续版本需要更换配置,都需要接入方重新配置会比较麻烦,那么可不可以将配置留在Lib库中,通过动态配置的方式动态配置呢。

例如微信支付的回调Activity配置,通过applicationId占位,可以动态获取到接入方的包名,只要按照约定,在包名下建立wxapi包下,建立WXEntryActivity文件即可。

package="me.zh.makechannelpackage">

android:allowBackup="true"

android:icon="@mipmap/ic_launcher"

android:label="@string/app_name"

android:supportsRtl="true"

android:theme="@style/AppTheme">

android:name="${applicationId}.wxapi.WXEntryActivity"

android:configChanges="keyboardHidden|orientation|screenSize"

android:exported="true"

android:theme="@android:style/Theme.Translucent.NoTitleBar" />

再例如图片选择库,使用到了FileProvider,我们不希望接入方配置,则也可以使用applicationId进行占位。

package="me.zh.makechannelpackage">

android:allowBackup="true"

android:icon="@mipmap/ic_launcher"

android:label="@string/app_name"

android:supportsRtl="true"

android:theme="@style/AppTheme">

android:name="android.support.v4.content.FileProvider"

android:authorities="${applicationId}.fileprovider"

android:exported="false"

android:grantUriPermissions="true">

android:name="android.support.FILE_PROVIDER_PATHS"

android:resource="@xml/file_paths" />

动态指定App名称

经过前面的配置已经足够配置出不同渠道标识的渠道包了,我们还可以继续拓展下,例如不同渠道的App名字不同。(一般因为有的应用商店会以App名称而被拒...)

修改build.gradle文件。

manifestPlaceholders中添加一个标识,app_name,值为对应的名称。例如360渠道为360渠道包,应用宝渠道为应用宝渠道。

//3.0Gradle开始必须添加纬度

flavorDimensions "default"

//多渠道打包配置

productFlavors {

//渠道包

app_360 {

manifestPlaceholders = [app_name: "360渠道包", channel: "app_360"]

}

app_qq {

manifestPlaceholders = [app_name: "应用宝渠道包", channel: "app_qq"]

}

}

动态指定App包名

例如我们debug环境下,希望测试包和正式包能够共存,那么共存就需要包名不同,所以可以对包名进行替换或者加后缀。

修改build.gradle文件。

applicationIdSuffix,给包名添加后缀。例如测试版加上.internal后缀。

applicationId,整个替换包名。(一般不会这么干,只是提一下)

//3.0Gradle开始必须添加纬度

flavorDimensions "default"

//多渠道打包配置

productFlavors {

//开发版

developer {

//测试版加包名后缀,方便和正式版共存

applicationIdSuffix ".internal"

manifestPlaceholders = [app_name: "开发版", channel : "app_internal"]

}

production {

//也可以完全替换包名

applicationId "me.zh.demo"

manifestPlaceholders = [app_name: "正式版",

channel: "app_internal"]

}

}

动态生成变量

开发中,Log打印是必不可少的,但是我们在正式环境是需要将Log打印去掉的,常规做法就是打印函数前加一个LogEnable的变量,将打印调用去掉。而我们一般会在常量类中添加开关变量。

public class App extends Application {

@Override

public void onCreate() {

super.onCreate();

Logger.setDelegate(new L());

Logger.setLogPrintEnable(true);

}

}

问题:而每次打包之前都需要将开关变量设置为false,很容易忘。

期望:我们希望不同buildType下的编译环境,打印Log是不一样的,例如buildType为debug时,Log开关为开,buildType为release时,Log开关为关。

Gradle为我们提供了buildConfigField,用于动态生成变量在BuildConfig,那么我们给不同的buildType进行添加即可。

//编译类型

buildTypes {

debug {

//打印开关变量

buildConfigField("boolean", "LOG_ENABLE", "true")

//省略其他配置...

}

release {

buildConfigField("boolean", "LOG_ENABLE", "false")

//省略其他配置...

}

}

在Java代码中,设置BuildConfig中生成的变量即可。

public class App extends Application {

@Override

public void onCreate() {

super.onCreate();

Logger.setDelegate(new L());

Logger.setLogPrintEnable(BuildConfig.LOG_ENABLE);

}

}

不同渠道替换资源文件

能替换包名和App名字了,原以为完毕,结果运营说,360商店我们要出首发!图标和启动图要加上360的标识!这时候就需要用到同结构、同名文件夹来替换了。

在和main文件夹下,建立同名渠道的文件夹,例如app_360渠道,建立的文件夹名为app_360。

assets文件夹、res文件夹、以及AndroidManifest.xml清单文件结构都要和main中的一致。

AppIcon同名即可替换。清单文件同名即可,内部特定不同的内容即可。

多渠道配置1.png

多渠道配置2.png

关于Java代码多渠道

上面说到资源文件,建立同目录进行替换,那么Java代码可以同级目录下,同名Java文件替换吗?很遗憾的是不可以,需要不同渠道,进行不同的逻辑时,只能通过刚才ChannelUtil获取渠道信息,进行逻辑判断了。

总结

使用Gradle来进行配置,大大减少了我们的工作量,妈妈再也不用担心我打渠道包需要2小时啦,至于gradle每构建一个渠道包都编译一次问题,可以单独给assets设置一个配置文件,后续使用zip修改配置文件,达到只打一个包,其他包都使用打包工具进行打包,时间可以节省非常多。

android多渠道打包流程,Android 多渠道打包总结相关推荐

  1. 【字节码插桩】Android 打包流程 | Android 中的字节码操作方式 | AOP 面向切面编程 | APT 编译时技术

    文章目录 一.Android 中的 Java 源码打包流程 1.Java 源码打包流程 2.字符串常量池 二.Android 中的字节码操作方式 一.Android 中的 Java 源码打包流程 Ja ...

  2. android p 权限流程,Android native 权限控制流程

    关联文章: 前言: 在 Android Runtime Permission 详解 中详细的说明了permission 在Android 6.0 前后的区别,对于M 以后应用可以通过checkPerm ...

  3. android屏幕适配流程,Android屏幕适配姿势

    GitHub地址 为什么要屏幕适配? device_framentation.png 统计 碎片化 品牌机型碎片化 屏幕尺寸碎片化 操作系统碎片化 为了保证用户获得一致的用户体验效果,使得某一元素在A ...

  4. android wifi wps 流程,android中WiFi wps连接方式

    Android 个人热点 wps方式连接流程 android手机开启个人热点时,可以选择WPS连接方式 个人热点端 连接端 当个人热点端选择按钮时,连接端只需要点击WPS按钮即可连接:当个人热点端选择 ...

  5. android zygote启动流程,Android zygote启动流程详解

    对zygote的理解 在Android系统中,zygote是一个native进程,是所有应用进程的父进程.而zygote则是Linux系统用户空间的第一个进程--init进程,通过fork的方式创建并 ...

  6. android otg 挂载流程,android USB OTG功能如何打开及实现

    1.检查HW原理图,确认是否支持OTG功能(vbus是否供上电,IDDIG pin链接是否正确) 2.若HW确认支持OTG功能,则按照如下方法分别打开USB OTG功能及实现挂载: 如何打开USB O ...

  7. android serviceconnection unbind流程,Android - Service Bind/Unbind 使用

    Service Bind/Unbind 使用方式 本文介绍了本地服务,也就是同一个进程内的服务,如何使用 Bind/Unbind 方式被 Activity 使用. 本文不涉及 AIDL 使用 Serv ...

  8. android volume挂载流程,Android SDCard UnMounted 流程分析(一)

    Android SDCard框架 Android SDCard框架,我们修改一般涉及到四大模块 Linux Kernel 用于检测热拔插,作为框架开发者来说,这者不用涉及 Vold 作为Kernel ...

  9. android serviceconnection unbind流程,Android unbindService 流程分析

    基于Android 6.0的源码剖析, 分析bind service的启动流程. /frameworks/base/core/java/android/app/ContextImpl.java /fr ...

  10. android 屏幕旋转流程,android自动屏幕旋转流程分析.doc

    android自动屏幕旋转流程分析.doc android自动屏幕旋转流程分析 在android设置(Settings)中我们可以看到显示(display)下有一个自动屏幕旋转的checkbox, 如 ...

最新文章

  1. Springboot的slf4j的配置文件模板
  2. [转]九个Console命令,让js调试更简单
  3. 16. 3Sum Closest 最接近的三数之和
  4. 比较两张大小相同的照片的差异,返回数值
  5. ext中ArrayStore,JsonStore,XmlStore的用
  6. vue data 值如何渲染_vue源码阅读复盘-watcher模块
  7. 实体与电商不是敌人 体验经济决定远方
  8. display:inline-block间隙产生的原因以及解决方案
  9. call mysql_connect_Call to undefined function mysql_connect()
  10. python—符号 | ^的使用
  11. Qt之进程间通信(Windows消息)
  12. linux kill 杀一个进程
  13. 二极管整流电路工作原理图
  14. 嵌入式开发——物联网
  15. 实现跨word文档的格式刷,两个word间格式刷
  16. erlang 学习ets表-2
  17. Java SHA哈希示例
  18. python求两个数的最大公约数穷举法_五十九、如何求N个数的最大公约数和最小公倍数...
  19. 腾讯手游助手吃鸡一直服务器繁忙,腾讯手游助手吃鸡手游常见问题解决办法介绍...
  20. oracle左关联+号表示方式

热门文章

  1. cubieboard mysql_Cubieboard开发笔记
  2. DateTime转为特定时区时间
  3. Django 修改时区时间
  4. xls文件格式与扩展名不匹配php,excel表格的文件格式和扩展名不匹配怎么办
  5. 将yolo格式数据集转换为coco格式数据集
  6. 拔丝芋头的Java学习日记--Day5
  7. 方舟服务器设置文档,方舟云服务器设置
  8. 006-Android-ContentProvider习题
  9. kvm 上部署虚拟机两种方法
  10. Passenger, Apache/Nginx, Rails之间的关系