活用productFlavors,实现意想不到的功能

Posted 夏雨_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了活用productFlavors,实现意想不到的功能相关的知识,希望对你有一定的参考价值。

前言:

在我们平时做项目时,总会遇到一些奇葩的要求,多渠道打包算是比较简单和常见的了,例如同时安装测试版和发布版,以及一套代码维护十几个不同的APP,这种需求也不少人遇到过,如果你不懂一些技巧,那么这些需求实现起来一定非常痛苦,所以我们接下来就介绍一下如何活用productFlavors,实现这些奇葩的需求

1. 简单使用

productFlavors直译过来就是特色的产品,所以他的主要作用就是让你同一套代码生产出不同的特色产品

productFlavors是build.gradle里面配置的,主要就是通过gradle打包时可以有多种配置供选择,类似buildTypes,并且可以和buildTypes共存

使用时在android 里面增加即可,我们下面简单的定义两个产品,一个是general,一个是vip,我们在里面给这两个产品定义不同的applicationId,这样就能同时安装了

(android Studio里面,真正区分APP的是applicationId)

android 

    productFlavors
        general
            applicationId "com.xiayu.flavorsdemo.general"
        
        vip
            applicationId "com.xiayu.flavorsdemo.vip"
        
    

这个时候我们就可以产生两个不同的产品了,接下来就要介绍怎么打包这两个产品了

(注意!每次修改了build.gradle,都必须同步(sync)一下,不然是不会生效的,这一点不少新人经常会忽略)

首先如果你不去选择的话默认是会打包最上面的产品,通过左下角的Build Variants可以自己选择打包的类型

我们可以看见上图是有四种产品供我们打包选择,这是因为与buildTypes共存时相互组合的结果

2. 特色图标

我们目前只是实现了同时安装,但是既然是特色产品,总会需要一些自己的特色,比如图标不一样

我们需要先在src目录下创建两个与产品同名的文件夹(下面简称产品文件夹)

把各自产品的特色图标放在各自的目录下,需要与main里面是相同目录结构,这里的效果就是各产品会先去从自己的目录下面读代码或者资源,如果没有的话就去main里面读

(注意!如果在main里面是放在哪个目录下,那么在自己产品文件夹下也要放在同样的目录下,比如图标之前是在mipmap-hdpi和mipmap-xhdpi都有,那么自己产品文件夹下也都需要有)

3. 清单文件

有时候我们的特色产品的清单文件里面需要一些自己的特色值,比如需要广播接收者需要不同的action,比如百度推送需要不同的API Key,这些同样可以用productFlavors来实现

我们常用的多渠道打包大部分也是这个原理,我们这里就以一个简单的多渠道打包为例

首先创建相应的productFlavors,这里因为不需要同时安装,所以就不用设置特色的applicationId

productFlavors
    xiaomi

    
    huawei

    

在清单文件的application节点中加入meta-data并在修改了application的label(label属性是修改应用名的)

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="$APP_NAME"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>

            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>


    <meta-data    android:name="CHANNEL"    android:value="$CHANNEL_VALUE" />
</application>

接着我们修改productFlavors,设置特色的manifestPlaceholders

productFlavors
    xiaomi
        manifestPlaceholders = [CHANNEL_VALUE: "xiaomi",APP_NAME:"xiaomi版"]
    
    huawei
        manifestPlaceholders = [CHANNEL_VALUE: "huawei",APP_NAME:"huawei版"]
    

接着我们打包不同的产品后,应用名是不同的,并且可以在Activity里面读取当前渠道

public class MainActivity extends AppCompatActivity 

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView tv= (TextView)findViewById(R.id.tv);
        tv.setText(getChannelName(this));
    
    //获取当前渠道
    public static String getChannelName(Context context) 
        if (context == null) 
            return null;
        
        String channelName = null;
        try 
            PackageManager packageManager = context.getPackageManager();
            if (packageManager != null) 
                //注意此处为ApplicationInfo 而不是 ActivityInfo,因为设置的meta-data是在application标签中,而不是某activity标签中,所以用ApplicationInfo
                ApplicationInfo applicationInfo = packageManager.
                        getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
                if (applicationInfo != null) 
                    if (applicationInfo.metaData != null) 
                        channelName = String.valueOf(applicationInfo.metaData.get("CHANNEL"));
                    
                

            
         catch (PackageManager.NameNotFoundException e) 
            e.printStackTrace();
        
        return channelName;
    

这里我们就可以看出来,清单文件中定义定义特色的值可以通过$VALUE来占位,然后通过manifestPlaceholders在打包的时候赋予不同的值

manifestPlaceholders是键值对的形式,key要与清单文件中的占位一样,value就是设置的值.键值对可以设置多个的,中间用逗号隔开

通过简单的设置,我们就完成了多渠道打包,并且每个渠道的应用名都是不一样的

4. buildConfigField

前面我们介绍了如何通过特色清单文件,读取当前渠道,虽然设置很简单,但是读取的时候代码还是比较多的,那么我们有没有比较简单的方式能在代码中直接读取productFlavors里面设置的特色值呢?我们可以通过buildConfigField.

设置:

productFlavors
    xiaomi
        buildConfigField "int", "APP_TYPE", "1"
    
    huawei
        buildConfigField "int", "APP_TYPE", "2"
    

读取:

int      type = BuildConfig.APP_TYPE;

(这里要注意的就是BuildConfig的导包,要导入当前项目的非test包)

这样就可以很简单的读取产品的特色值,然后根据特色值做不同的处理了.

5. 应用场景

除了我们刚才举例的多渠道打包,以及同时安装线上线下两个版本的应用,我们还会遇到更加复杂的场景,需要同时应用上面的多种方法,下面以我之前做的某个应用为例,简单介绍一下如何综合应用上述几种方法

首先我们的项目除了正常版本,还有两个定制版,需求是这三个应用需要可以同时安装在一台手机上,并且主要功能是要同步的,那么我们就需要通过一套代码代码来维护三个应用.

如果每次打包不同的应用时,手动做一系列的修改,或者是通过代码中某个静态值来控制具体的效果,都容易出错,不仅麻烦,而且有很多局限性,比如代码中要加入很多if,else.比如清单文件就不能设置特色值,所以这个时候productFlavors就派上用场了

首先在productFlavors中设置特色applicationId,用于实现可以同时安装的功能

接着清单文件中设置一些特色值,比如不同应用的广播接收者的action设为不同的值

当一些图片文字不同或者布局差别特别大时,放在特色产品同名的文件夹

当一些逻辑差别比较小时,通过读取BuildConfig在代码中来做不同的处理

这样综合应用刚才介绍的几种方法,就可以很简单的完成我们的需求了


热门文章

以上是关于活用productFlavors,实现意想不到的功能的主要内容,如果未能解决你的问题,请参考以下文章

Android Gradle 插件ProductFlavor 配置 ( ProductFlavor 引入 | ProductFlavor 参考文档地址 )

Android Gradle中的productFlavors

Android Gradle 插件ProductFlavor 配置 ( ProductFlavor#resValue 方法 | ProductFlavor#dimension 维度属性 )

活用 Swift 类型推断

活用深度链接实现拉新促活,打造自己的流量池

活学活用,用python实现网页自动朗读,又节约了不少时间!