本文主要介绍一下Gradle的多版本编译,定制软件资源和代码等功能
1 定制不同的apk
在gradle里,Android build部分,产品的Flavors(多样化)是第一级定制,然后在此基础上build types默认再产生第二级定制debug和release两种包,得出Varints(变体),Varints数目=Flavors x build types数目,比如定制了3个公司的apk,默认buildtypes产生2种包,得到的3×2=6个apk。所以需要对产品外观和感觉行为上能满足多种用户,我们可以使用productFlavors来完成。这里,我们举的个例子不是简单的只定制公司名称的多渠道打包方法,而是定制不同资源和代码的打包。
现在我们写一个简单的功能,让用户能够通过EditText输入相应的名字。我们可以给出的app对应自定义的名称”TestA”,”TestB”,”TestC”,在app的build.gradle中android节点定义如下:
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
productFlavors { TestB { applicationId 'com.example.sk.myapplication.TestB' } TestA { applicationId 'com.example.sk.myapplication.TestA' } TestC { applicationId 'com.example.sk.myapplication.TestC' } } |
通过上面构建脚本加入,我们可以生成不同applicationId,这样三个应用都可以被安装在同一台设备上。如下图:
产品多样化能定制特有属性值,这些都是基于相同属性来自defaultConfig:
1 2 3 4 5 6 |
applicationId minSdkVersion targetSdkVersion versionCode versionName signingConfig |
2 定制软件资源和代码
2.1 定制文件夹的创建
上节我们已经可以生成3种不同名称的apk,但是这并不能满足我们订制化资源或代码的功能,只能满足多个相同应用安装在同一个设备上。然而,多样化(flavor)applicationId都可以定义自己的源码路径,也就一意味着能像app/src/main/java一样增加源码目录:
1 2 3 |
app/src/arrogant/java app/src/friendly/java app/src/obsequious/java |
你也可以增加额外的资源目录:
1 2 3 |
app/src/arrogant/res app/src/arrogant/res/layout app/src/arrogant/res/values |
像其它子工程res一样。相同的资源结构也会应用在所有的多样化应用中,通过Gradle工具将相同的资源进程合并,实现多样化产品的多样性,也就生成了变体.
as1.png
在src子目录中,不但包括了公共模块的main包也就是android默认的包,还有我们定制的TaskA,TaskB,TaskC.部署一个特定的变体,Android Studio提供了一个变体选择框。通过下拉列表选择需要生成的变体产物,如图所示。
当我们使用产品多样化的时候,使用assemble任务会生成所有的变体。使用assemble任务会只生产相应的变体。
2.2
资源合并
三种产品的图片、文本或者其它的资源是不相同的,我们应该如何使用呢?看下面,MainActivity类的OnCreate方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public class MainActivity extends AppCompatActivity { private EditText editText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); editText = (EditText) findViewById(R.id.name_edit_text); } public void sayHello(View view) { String name = editText.getText().toString(); Intent intent = new Intent(this, WelcomeActivity.class); intent.putExtra("user", name); startActivity(intent); } } |
activity使用了组件EditText,提供给用户输入姓名。sayHello方法取出相应的名字,添加到Intent的extra中,并通过Intent传递给WelcomeActivity。
MainActivity的视图很简单,只有一个垂直分布的LinearLayout,里面放入一个TextView,一个EditText和一个Button。
1 2 |
<button> </button> |
每一个变体都有自己的资源目录,在app/”flavor”/res目录结构下。在不同情况下,子文件夹增加values,并拷贝strings.xml文件到app/src/main/res/values目录中。其中Text变体内容如下所示:
1 2 3 4 5 |
TestA His/Her Royal Highness TestA I am %1$s. The operation cancel |
通过和工程目录合并res文件夹,将相同名称的文件进行合并,实现资源合并。优先级为:build type中的变体覆盖产品覆盖main公共模块的产品,这是主要的覆盖原则。
这里来看一下WelcomeActivity的onCreate方法,读取用户名称,并问候用户。
1 2 3 4 5 6 7 8 9 10 11 |
public class WelcomeActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_welcome); String name = getIntent().getStringExtra("user"); TextView greetingText = (TextView) findViewById(R.id.greeting_text); String format = getString(R.string.greeting); greetingText.setText(String.format(format, name)); } } |
WelcomeActivity的视图包含了一个TextView,TextView中底部放入了一张图片,布局如下:
1 |
每一个变体都有values.xml和animal.png文件,用于改变问候方式。我们分别来看一下TestA,TestB变体的样式:
TestA的String样式
1 2 3 4 5 |
TestA His/Her Royal Highness TestAs I am %1$s. The operation cancel |
TestB的String样式
1 2 3 4 5 |
TestB His/Her TestB TestB I am %1$s. The operation cancel |
2.3代码合并
前面我们知道,string, layout资源在变体中会覆盖main资源中的相同内容,但是Java类是不一样的。当你的main代码中涉及一个特殊类,要在每一个变体包内,实现一个在main中调用相同的类,这个类相当于一个桥梁,连接公共类和变体类。这里我们选择TestA,在变体A产品上加一个cancel button.
点击这个”the cancel operation”的button,启动CancelForHelpActivity,而这个类是不在main包里边的,我们拷贝到TestA的目录上,这里是关键,如下图,
目录一定要相同,TestB,和Test C都实现了CancelForHelpActivity,内部实现的方法可以完全不同,只要类的名字相同就可以了。
好了,定制产品变体的资源和代码就到这里,还是很简单的,1 无非是建立了和main同一级的资源文件,2 目录相同 3 产品变体代码实现main公共包里的调用类即可。
GitHub Demo的代码地址:
https://github.com/sksweet/AndroidFlavorsTest.git
Enjoy~
厉害了哥~