Android 马甲包与渠道包

渠道包与马甲包

目录:
一、马甲包
    1.1 概念与关键词
    1.2 主包与马甲包的区别
    1.3 马甲包的作用
    1.4 打法
    1.5 用法
二、渠道包
    2.1 概念与关键词
    2.2 渠道包配置文件概览
    2.3 渠道配置
    2.4 Gradle相关配置

一、马甲包

参考资料:
马甲app怎么向主app导流?
App马甲包是什么?
Android如何优雅的写马甲包
《侵删》

“一个游戏三个包,死了一个还有俩”
故事可以从2012年底游戏应用集体下架风波讲起。
这场下架风波使得整个游戏行业人心惶惶,尤其人人游戏,企业开发者账号被封,旗下近20款游戏一夜之间全部下架,这是由于过度刷榜挑战苹果底线带来的恶果。从此人人游戏在iOS端走向衰弱。
自从苹果打击刷榜以来,马甲便成了游戏圈的标配。

1.1 概念与关键词

  • 马甲包: APP主包的一种分身。
    马甲包是利用各大市场规则漏洞,通过技术手段,多次上架同一款产品的方法。
  • 主产品包: 与马甲包相对

1.2 马甲包 与 主产品包 的区别:

拥有同样的内容和功能,除了icon和应用名称不能完全一致,其他基本一致

  • 应用名称不一样
  • 关键词不一样
  • 应用图标不一样
  • 应用截图不一样
  • 开屏图片最好不一样
  • 其余的,比如主App的一些品牌因素,最好去掉

1.3 马甲包 的 作用:

graph TD
A[马甲包] -->导量
A -->覆盖更多关键词
A -->A/B测试
A -->刷榜
  1. 增加获取有效客户的渠道 - 导量
    每一个马甲包都相当于额外开出的积攒流量的渠道。可以通过导流来积累流量
    - 全部复制主App内容,共享后台,共享整个数据的情况无需导流
    - 正常的导量形式:一般通过弹窗,广告,Push等引导用户到App Sture下载主App
  2. 增加关键词的覆盖量
    单个App的关键词有100个字符的限制,多个App意味着可以覆盖到Nx100个字符的关键词
  3. 做A/B测试
    在主App不方便测试,而马甲包不一样,它可以随意更改或替代,所以早期马甲包都是为了做测试用
  4. 刷榜
    刷榜有被苹果下架的风险,但是刷榜能产生较高的性价比,可以用马甲包来刷榜,马甲包挂了就挂了。

1.4 打法

  1. 马甲采用主App的部分功能,不同功能性的马甲各自有自己的目标用户,再集体向主App导用户:喜马拉雅、玩图
  2. 隐藏主App,开发者账号不一样,资源允许的话,使用的后台也不一样

1.5 用法

  • build.gradle(app)android{}中添加 产品风味:productFlavors
productFlavors {
        /* 三种马甲包 马甲包可以多添加几种 */
        ceshi {} //测试  
        official {}//正式
        debug {} //演示
}
    // 产品风味~~~
productFlavors.each { flavor ->
        def props = new Properties()
        /* 读取config文件夹中的配置文件 */
        file("../config/${flavor.name}_config.properties").withInputStream {
            props.load(new InputStreamReader(it, "GBK"))
        }
        def application_id = props.getProperty("APPLICATION_ID")
        def app_name = props.getProperty("APP_NAME")
        def server_url = props.getProperty("SERVER_URL")
        def map_key = props.getProperty("MAP_KEY")
        def umeng_key = props.getProperty("UMENG_APPKEY")
        // 特调的applicationId
        flavor.applicationId = application_id
        // 清单文件占位符
        flavor.manifestPlaceholders = [
                APP_NAME    : app_name,
                MAP_KEY     : map_key,
                SERVER_URL  : server_url,
                UMENG_APPKEY: umeng_key
        ]
}
  • 新建config目录,添加配置文件
    ceshi_config . properties
    official_config . properties
    debug_config . properties
APPLICATION_ID =...
APP_NAME = ...
SERVER_URL =...
MAP_KEY=...
UMENG_APPKEY=...
  • 需要在Java文件中读取配置信息
<!--服务器请求地址 Manifest-->
<meta-data
    android:name="SERVER_URL"
    android:value="${SERVER_URL}" />
// 封装一个工具方法:
public static String getMetaDataInApp(@NonNull final String key) {
        String value = "";
        PackageManager pm = Utils.getApp().getPackageManager();
        String packageName = Utils.getApp().getPackageName();
        try {
            ApplicationInfo ai = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);
            value = String.valueOf(ai.metaData.get(key));
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return value;
    }
 
//调用工具类方法
(类名).getMetaDataInApp("SERVER_URL");

二、渠道包:

2.1 涉及到的配置文件
  • 渠道配置:channels_config.gradle
  • 全局应用配置:config.gradle
  • app模块配置:build.gradle(:app)
  • 项目属性配置:project_properties.gradle
2.2、马甲包依赖
// walle多渠道和马甲包配置(美团出品)
classpath 'com.meituan.android.walle:plugin:1.1.7' 
2.3 渠道配置:channels_config.gradle
`apply plugin: 'walle'

android {
    defaultConfig {  
        buildConfigField "String", "CHANNEL_TYPE", "\"${rootProject.ext.channel}\""
    }
    signingConfigs {
        company1 {
            // 签名store文件路径    
            storeFile file(rootProject.ext.android.storeFile)    
            // 签名store文件的密码    
            storePassword rootProject.ext.android.storePassword    
            // 别名    
            keyAlias rootProject.ext.android.keyAlias    
            // 别名的密码   
            keyPassword rootProject.ext.android.keyPassword
        }
        company2 {
            // 签名store文件路径    
            storeFile file(rootProject.ext.android.mtStoreFile)   
            // 签名store文件的密码    
            storePassword rootProject.ext.android.mtStorePassword    
            // 别名    
            keyAlias rootProject.ext.android.mtKeyAlias   
            // 别名的密码    
            keyPassword rootProject.ext.android.mtKeyPassword
        }
    }
}
2.4 app模块配置:build.gradle(:app)
  • gradle文件的拆分与合并
// 在build.gradle文件中引入channels_config.gradle的配置
apply from: "channels_config.gradle"
// 引入上级目录下的buildSystem.gradle
apply from: "../buildSystem.gradle"
  • manifestPlaceHolders 配置的内容在AndroidManifest可以直接获取:
<!-- AndroidManifest.xml -->
<meta-data    
    android:name="UMENG_CHANNEL"       
    android:value="${APP_CHANNEL_VALUE}" />
// build.gradle
android{
    productFlavors {
        xysp {    
            applicationId "com.mg.xyvideo"    
            versionCode rootProject.ext.android.versionCode    
            versionName rootProject.ext.android.versionName
            // 看这边!!!看这边!!!
            manifestPlaceholders = [        
                UMENG_APPKEY      : "xxxxxxxxx3xxxxxxxxxxxxxx",        
                //友盟配置        
                APP_CHANNEL_VALUE : "xxxxxxxxxx"
            ]
        }
    }
}
  • gradle文件中获取日期时间
def releaseTime() {
    return new Date().format("yyyy-MM-dd_HH-mm-ss", TimeZone.getTimeZone("GMT+8"))
}
defaultConfig {
        // company的维度优先于channel
        flavorDimensions "company","channel"
}

productFlavors{
        // 随便命名,建议根据该维度的具体信息进行命名
        companyA{
            dimension "company"
        }
        companyB{
            dimension "company"
        }
        channelA{
            dimension "channel"
        }
        channelB{
            dimension "channel"
        }
    }

打开你的terminal,构建下

// windows 
gradlew :app:assembleRelease 
// mac 
./gradlew :app:assembleRelease

好了现在打出了下面这些包

assembleCompanyAChannelA
assembleCompanyAChannelB
assembleCompanyBChannelA
assembleCompanyBChannelB

假设CompanyA和ChannelA配置了相同的属性,那么主维度的该属性会覆盖子维度的该属性

  • 生成BuildConfig.java可见的变量
buildConfigField "String","FLAVOR_NAME","\"channelB\""

厉害的来了,在Java代码中可以这么取值:

BuildConfig.FLAVOR_NAME

有点意思

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,688评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,559评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,749评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,581评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,741评论 3 271
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,684评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,122评论 2 292
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,847评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,441评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,939评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,333评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,783评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,275评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,830评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,444评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,553评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,618评论 2 249

推荐阅读更多精彩内容