为什么学习Kotlin?

  1. 欢呼声热烈 谷歌宣布 Kotlin 成 Android 开发一级语言

  2. Kotlin on Android. Now official

  3. 17 位谷歌 Android 开发专家是如何看待 Kotlin 的?

  4. 为什么你该摒弃 Java ,全面转向 Kotlin 语言?

  5. 为什么说Kotlin值得一试

如何学习Kotlin?

官网以及相关文档

  1. Kotlin 官网

  2. Kotlin 文档

  3. Kotlin 博客

  4. Kotlin 中文文档

  5. Kotlin on GitHub

Kotlin开发实践

  1. Getting started with Android and Kotlin

  2. Kotlin Android Extensions

  3. Android Frameworks Using Annotation Processing

  4. kotlin-examples

  5. Calling Java code from Kotlin

  6. Calling Kotlin from Java

  7. 使用Kotlin开发Android应用

  8. Kotlin入门和使用讲稿(PPT)

  9. Kotlin入门和使用讲稿(文本)

  10. Kotlin从入门到『放弃』系列 视频教程

  11. Kotlin: Java 6 废土中的一线希望

  12. Kotlin 语言高级安卓开发入门

  13. 《Kotlin for android developers》中文版

  14. 深入理解 Kotlin Coroutine(一)

  15. 深入理解 Kotlin Coroutine(二)

  16. Kotlin Script 及其运行机制简析

  17. Kotlin Native 详细体验,你想要的都在这儿

Kotlin相关类库和资源

Libraries

  1. Kotlin/anko - Pleasant Android application development.
  2. JakeWharton/kotterknife - View injection library for Android.
  3. MarcinMoskala/ActivityStarter - Activity starter generator and arguments injection library for Android.
  4. MarcinMoskala/KotlinPreferences - Kotlin Android Library, that makes preference usage in Kotlin simple and fun.
  5. MarcinMoskala/PreferenceHolder - Kotlin Android Library, that makes preference usage in Kotlin simple and fun using object with fields binded to SharedPreferences.
  6. nsk-mironov/kotlin-jetpack - A collection of useful extension methods for Android.
  7. pawegio/KAndroid - Kotlin library for Android providing useful extensions to eliminate boilerplate code.
  8. chibatching/Kotpref - Android SharedPreference delegation for Kotlin.
  9. TouK/bubble - Library for obtaining screen orientation when orientation is blocked in AndroidManifest.
  10. ragunathjawahar/kaffeine - Kaffeine is a Kotlin-flavored Android library for accelerating development.
  11. mcxiaoke/kotlin-koi - Koi, a lightweight kotlin library for Android Development.
  12. BennyWang/KBinding - Android View Model binding framework write in kotlin, base on anko, simple but powerful.
  13. inaka/KillerTask - Android AsyncTask wrapper library, written in Kotlin.
  14. grandstaish/paperparcel - Boilerplate reduction library written specifically for working with Kotlin data classes on Android.
  15. andre-artus/AnvilKotlin - Minimal UI library for Android inspired by React.
  16. mathcamp/fiberglass - Easy lightweight SharedPreferences library for Android in Kotlin using delegated properties.
  17. nitrico/LastAdapter - Don’t write a RecyclerView adapter again. Not even a ViewHolder!.
  18. denisidoro/krouter - A lightweight Android activity router.
  19. metalabdesign/AsyncAwait - async/await for Android built upon coroutines introduced in Kotlin 1.1.
  20. jupf/staticlog - StaticLog - super lightweight static logging for Kotlin, Java and Android.
  21. zserge/anvil - Minimal UI library for Android inspired by React.
  22. DanielMartinus/Stepper-Touch - Fun playful Android stepper widget for counting, written in Kotlin.
  23. GlimpseFramework/glimpse-framework-android - OpenGL made simple.
  24. infotech-group/android-drawable-dsl - DSL for constructing the drawables in Kotlin instead of in XML.
  25. zsmb13/MaterialDrawerKt - A DSL for creating Material Design navigation drawers without any XML.
  26. SnowdreamFramework/ToyBricks - Android Library that provide simpler way to achieve modularity.

Frameworks

  1. nekocode/kotgo - An android development framwork on kotlin using MVP architecture.
  2. lightningkite/kotlin-core - A full framework for making Android apps. Based on Anko and Kotson.

Projects

  1. antoniolg/Bandhook-Kotlin - A showcase music app for Android entirely written using Kotlin language.
  2. antoniolg/Kotlin-for-Android-Developers - Companion App for the book “Kotlin Android Developers”.
  3. damianpetla/kotlin-dagger-example - Example of Android project showing integration with Kotlin and Dagger 2.
  4. dodyg/AndroidRivers - RSS Readers for Android.
  5. MakinGiants/banjen-android-banjo-tuner - App that plays sounds helping to tune a brazilian banjo.
  6. inaka/kotlillon - Android Kotlin Examples.
  7. MakinGiants/todayhistory - App that shows what happened today in history.
  8. RxKotlin/Pocket - This app help user to save links easily, and can export to Evernote as weekly.
  9. SidneyXu/AndroidDemoIn4Languages - Comparison between Java, Groovy, Scala, Kotlin in Android Development.
  10. inorichi/tachiyomi - Free and open source manga reader for Android.
  11. ziggy42/Blum-kotlin - A simple android Twitter client written in Kotlin
  12. TwidereProject/Twidere-Android - Material Design ready and feature rich Twitter app for Android 4.0+
  13. SimpleMobileTools/Simple-Calendar - A simple calendar with events, customizable widget and no ads.
  14. SimpleMobileTools/Simple-Camera - A camera with flash, zoom and no ads.
  15. SimpleMobileTools/Simple-Draw - A canvas you can draw on with different colors.
  16. SimpleMobileTools/Simple-File-Manager - A simple file manager for browsing and editing files and directories.
  17. SimpleMobileTools/Simple-Gallery - A gallery for viewing photos and videos without ads.
  18. SimpleMobileTools/Simple-Notes - A simple textfield for adding quick notes without ads.

Extensions

  1. ajalt/timberkt - Easy Android logging with Kotlin and Timber.

Tools

  1. kiruto/debug-bottle - Debug Bottle is an Android runtime debug / develop tools written using kotlin language.

Websites

  1. awesome-kotlin

  2. kotlin.link

联系方式

sn0wdr1am

注:阅读本文之前,建议先阅读一下:
ToyBricks简介以及原理分析

ToyBricks简介

ToyBricks是一个Android项目模块化的解决方案,主要包括四个部分,APT注解,APT注解处理器,ToyBricks插件(Gradle Plugin),ToyBricks库。

ToyBricks简介

其中:

  1. APT注解,主要定义了两个注解:Interface(接口,例如:IText),Implementation (实现,例如:TextImpl)
  2. APT注解处理器,在javac编译java源码之前。APT注解处理器会扫描Java源码中带有上面两个注解的接口和类,并且生成一个json文件, ToyBricks.json.
  3. ToyBricks插件(Gradle Plugin),负责ToyBricks.json的打包,合并,生成Java源文件等工作
  4. ToyBricks,提供对外调用方法。通过参数传入接口,返回相应的实现。

ToyBricks特性

  1. 同时支持Kotlin,Java
  2. 支持Android Build Variants
  3. Proguard免配置

ToyBricks局限性

ToyBricks具有传染性。
任何Android Application 或者 Android Library 使用包含ToyBricks.json的jar包或者aar包作为依赖,都必须继续使用ToyBricks,否则无法保证代码能正确运行。

ToyBricks规则

每个模块分为接口和实现两个部分。接口部分提供给模块外部调用,而实现部分则禁止来自外部的调用。

模块划分

接口 以@Interface进行注解,示例如下:

1
2
3
4
5
@Interface
public interface IText {
String getText();
}

实现 以@Implementation进行注解,示例如下:

1
2
3
4
5
6
7
@Implementation(IText.class)
public class NewTextImpl implements IText {
@Override
public String getText() {
return "NewTextImpl Implementation from "+ getClass().getCanonicalName();
}
}

@Implementation更详细的使用方法如下:

1
2
3
4
5
6
7
@Implementation(value = IText.class,global = true,singleton = true)
public class NewTextGobalImpl implements IText {
@Override
public String getText() {
return "NewTextImpl Implementation from "+ getClass().getCanonicalName() ;
}
}

参数说明如下:

  1. value后面应该填写接口的class
  2. global代表这个实现拥有高优先级。如果没有设置,默认取值为false。
  3. singleton代表这个实现将会以单例形式存在。如果没有设置,默认取值为false。由于单例会一直存在于APP的整个生命周期,因此,不应该滥用单例。

使用ToyBricks应该遵守以下规则:

  1. 接口命令强烈建议以I开头,例如:IText
  2. 每一个接口都必须至少有一个相应实现,否则编译出错。
  3. 每个实现类必须是public的。
  4. 每个实现类必须实现注解中标明的接口。
  5. 每个实现类必须拥有一个默认无参数的构造函数。
  6. 每个实现类不可以是abstract抽象类。
  7. 实现一共分成三类,全局实现(global=true),普通实现(global=false),替补实现(按照默认的包名和类名进行加载)。它们的优先级从前往后一直降低。每一类只能同时存在一个实现,否则编译出错。
  8. 替补实现遵循以下规则:
    假如接口为:com.github.snowdream.toybricks.app.IText,
    则替补实现为:com.github.snowdream.toybricks.app.impl.TextImpl
    实现的包名为接口的包名+”.impl”,实现的类名为接口名去掉第一个字母,第二个字母大写,然后加上”Impl”。
  9. 任何发布到Maven仓库的库,都应该将global设置为false。global设置为true,只适合Android Application测试新实现,或者替换默认实现。

ToyBricks使用方法

由于ToyBricks对Android的Gradle编译流程稍微进行了修改,因此,请注意按照下面的说明进行详细操作。
对于编译流程的修改如下:

  1. Android Application模块:首先按照上面的步骤生成ToyBricks.json,然后和依赖库中的ToyBricks.json进行合并,校验,最后按照预定规则,生成InterfaceLoaderImpl.java源代码,并参与编译。
  2. Android Library: 通过APT生成Json文件(ToyBricks.json,生成目录在“build/generated/source/apt”或者“build/generated/source/kapt”),然后打包到aar。如果你需要打包成jar包,并发布到maven仓库,请参考下面代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
task androidReleaseJar(type: Jar,dependsOn: "assembleRelease") {
from "$buildDir/intermediates/classes/release/"
from "$buildDir/generated/source/kapt/release/ToyBricks.json"
from "$buildDir/generated/source/apt/release/ToyBricks.json"
exclude '**/BuildConfig.class'
exclude '**/R.class'
exclude '**/R$*.class'
includeEmptyDirs false
}
task androidJavadocsJar(type: Jar) {
classifier = 'javadoc'
from "generateReleaseJavadoc.destinationDir"
includeEmptyDirs false
}
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.srcDirs
from "$buildDir/generated/source/kapt/release/ToyBricks.json"
from "$buildDir/generated/source/apt/release/ToyBricks.json"
includeEmptyDirs false
}

Gradle主工程

在主工程的build.gradle文件中添加ToyBricks的gradle插件。

1
2
3
4
5
6
7
8
9
10
11
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.2'
classpath 'com.github.snowdream.toybricks:android-toybricks-gradle-plugin:0.9.10'
}
}

Android Library模块

在Library模块的build.gradle文件中添加ToyBricks的相关库依赖。

1
2
3
4
5
6
7
8
9
10
11
12
13
kapt {
generateStubs = true
}
dependencies {
compile "com.github.snowdream:toybricks:0.9.10@aar"
compile "com.github.snowdream.toybricks:annotation:0.9.10"
kapt "com.github.snowdream.toybricks:processor:0.9.10"
//annotationProcessor "com.github.snowdream.toybricks:processor:0.9.10"
}
apply plugin: 'com.github.snowdream.toybricks'

注: 有两种引用方式,一种是kotlin的kapt方式,需要配置上面的generateStubs。另外一种,是Android默认支持的annotationProcessor方式。

如果你的工程中包含任何Kotlin源文件,则必须选择kapt的方式,否则,可以选择annotationProcessor方式。

Android Application模块

在Application模块的build.gradle文件中添加ToyBricks的相关库依赖。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
kapt {
generateStubs = true
}
dependencies {
compile 'com.github.snowdream:annotation:0.7@aar'
compile "com.github.snowdream:toybricks:0.9.10@aar"
compile "com.github.snowdream.toybricks:annotation:0.9.10"
kapt "com.github.snowdream.toybricks:processor:0.9.10"
//annotationProcessor "com.github.snowdream.toybricks:processor:0.9.10"
}
apply plugin: 'com.github.snowdream.toybricks'

ToyBricks使用方法

通过上面的方式,开发好接口和实现模块后。只需要引用接口,就可以通过下面方式获取接口的实现:

1
IText text = ToyBricks.getImplementation(IText.class);

其中,IText为接口。

ToyBricks最佳实践

按照替补实现的命名规则,来开发接口的实现类。

如果您对ToyBricks有什么问题或者建议,欢迎通过后面的联系方式联系我。

参考资料:

  1. SnowdreamFramework/ToyBricks
  2. SnowdreamFramework/log

联系方式

sn0wdr1am

ToyBricks背景

我始终认为,在高内聚,低耦合的原则下,进行组件化,模块化,插件化都是移动应用开发的趋势。

为什么这么说呢?下面我们举个栗子:
大家都知道,以前Android应用开发中,可以使用HttpClient或者HttpUrlConnection来进行http访问。这里假设有一个耦合严重,但代码量巨大的项目,使用了基于HttpClient封装的loopj/android-async-http来进行http访问。但是,后来,Google明确支持使用HttpUrlConnection。此时,经过调研,你们觉得square/okhttp基于HttpUrlConnection,符合你们的要求。

现在,不管是否将HttpClient替换成okhttp,你们都可能面临以下困境:

  1. 需求都做不完,根本没有排期做这个替换。于是,你们面临离google的支持越来越远,离风险越来越近的困境;
  2. 辛辛苦苦耗费人力将HttpClient替换成okhttp。但由于两者变化很大,需要投入很多测试资源,来重新确认这些接口是否正常访问。一旦出现问题,还需要安排研发资源,去一一排查。
  3. 替换的工作量太大。替换一部分之后,发现没有足够人力去继续完成。于是,替换终止。整个工程又变得混乱和臃肿,同时包含了两种http的封装库和调用。

模块化可以有效解决这些问题。通用的做法,是按照业务,功能等将整个项目分成不同的模块,由不同的研发测试小组负责。
每个模块又分为接口和实现两个部分。接口部分提供给模块外部调用,而实现部分则禁止来自外部的调用。

[apt]

那么,如何将模块的接口和实现部分关联起来呢?通过APT工具,可以轻松地将接口部分和实现部分关联起来。

APT,即Annotation Processing Tool,可以理解为“编译时注解处理器工具”。

官方说明:
“The apt tool is a command-line utility for annotation processing. It includes a set of reflective APIs and supporting infrastructure to process program annotations (JSR 175). These reflective APIs provide a build-time, source-based, read-only view of program structure. They are designed to cleanly model the Java programming language’s type system after the addition of generics (JSR 14).”

简单理解如下:
apt工具是javac工具的一部分。在编译时,apt工具首先会扫描工程下Java源码中的编译时注解,再根据预先定义的编译时注解处理工具,生成指定的Java源码文件。紧接着,生成的Java源码文件和之前项目下的Java源码一起,由javac工具来编译成class。

但是,APT有一个局限性,就是只会扫描Java源码,不会扫描jar ,aar 和class 。也就是说,所有模块需要以源码形式存在。而现在通用的做法是,将模块打包成jar或者aar,发布到Maven库,再由其他模块自行引用。

有没有办法将APT的这种功能和特性延伸到jar和aar呢?
于是,ToyBricks应运而生。

ToyBricks简介

ToyBricks简介

ToyBricks是一个Android项目模块化的解决方案,主要包括四个部分,APT注解,APT注解处理器,ToyBricks插件(Gradle Plugin),ToyBricks库。

[ToyBricks]

其中:

  1. APT注解,主要定义了两个注解:Interface(接口,例如:IText),Implementation (实现,例如:TextImpl)
  2. APT注解处理器,在javac编译java源码之前。APT注解处理器会扫描Java源码中带有上面两个注解的接口和类,并且生成一个json文件, ToyBricks.json.
  3. ToyBricks插件(Gradle Plugin),负责ToyBricks.json的打包,合并,生成Java源文件等工作
  4. ToyBricks,提供对外调用方法。通过参数传入接口,返回相应的实现。

ToyBricks原理分析

下面以接口IText和实现TextImpl为例,简单介绍下ToyBricks原理。

主要分为两个部分:

Android Library(最终可能打包成jar,aar,并发布到maven库)

如果工程是Android库模块,则主要流程如下:
1.在javac编译java源码之前,由APT注解处理器扫描Java源码中带有上面两个注解的接口和类,生成ToyBricks.json。

1
2
3
4
5
6
7
8
9
10
{
"interfaceList" : [ "com.github.snowdream.toybricks.app.IText" ],
"globalImplementation" : {
"com.github.snowdream.toybricks.app.IText" : "com.github.snowdream.toybricks.app.impl.NewTextGobalImpl"
},
"defaultImplementation" : {
"com.github.snowdream.toybricks.app.IText" : "com.github.snowdream.toybricks.app.impl.TextImpl"
},
"singletonImplementation" : [ "com.github.snowdream.toybricks.app.impl.NewTextGobalImpl" ]
}

2.在打包jar,aar的时候,由ToyBricks插件(Gradle Plugin)提前处理,保证ToyBricks.json能被拷贝进去jar包或者aar包的根目录下,并随同一起分布到maven仓库。

Android Application

如果工程是Android应用模块,则主要流程如下:

  1. 第一步,和Android Library第一步一致。
  2. Javac编译Java源代码
  3. 扫描所有依赖的库文件,过滤出所有包含ToyBricks.json文件的jar包或者aar包,并且提取出来。提取完毕后,合并所有的ToyBricks.json文件,成为一个ToyBricks.json。
  4. 将最终的ToyBricks.json按照预定规则生成一个Java源码文件.文件名为: InterfaceLoaderImpl.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package com.github.snowdream.toybricks.annotation.impl;
import com.github.snowdream.toybricks.annotation.InterfaceLoader;
import java.lang.Class;
import java.lang.Override;
import java.util.HashMap;
/**
*
* Created by snowdream
*
* This file is automatically generated by apt(Annotation Processing Tool)
* Do not modify this file -- YOUR CHANGES WILL BE ERASED!
*
* This file should *NOT* be checked into Version Control Systems,
* as it contains information specific to your local configuration.
*/
final class InterfaceLoaderImpl implements InterfaceLoader {
private static HashMap<Class, Object> sSingletonMap = new HashMap<Class, Object>();
private static HashMap<Class, Class> sGlobalMap = new HashMap<Class, Class>();
private static HashMap<Class, Class> sDefaultMap = new HashMap<Class, Class>();
public InterfaceLoaderImpl() {
addGlobalMap();
addDefaultMap();
addSingletonMap();
}
private void addGlobalMap() {
sGlobalMap.put(com.github.snowdream.toybricks.app.IText.class,com.github.snowdream.toybricks.app.impl.NewTextGobalImpl.class);
}
private void addDefaultMap() {
sDefaultMap.put(com.github.snowdream.toybricks.app.IText.class,com.github.snowdream.toybricks.app.impl.TextImpl.class);
}
private void addSingletonMap() {
sSingletonMap.put(com.github.snowdream.toybricks.app.impl.NewTextGobalImpl.class,null);
}
@Override
public <T> T getImplementation(Class<T> clazz) {
T implementation = null;
boolean isSingleton = false;
Class implClass;
implClass = sGlobalMap.get(clazz);
if (implClass == null) {
implClass = sDefaultMap.get(clazz);
}
if (implClass != null) {
isSingleton = sSingletonMap.containsKey(implClass);
if (isSingleton) {
implementation = (T) sSingletonMap.get(implClass);
if (implementation != null) {
return implementation;
}
}
try {
implementation = (T) implClass.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if (isSingleton && implementation != null) {
sSingletonMap.put(implClass, implementation);
}
}
return implementation;
}
}

5.再次使用Javac工具编译InterfaceLoaderImpl.java文件。
6.这个文件就类似字典索引,通过这个文件,就可以通过传入接口,来查找对应的实现类。

总结

与APT工具相比,ToyBricks能够将接口和实现之间的关系进行持久化,存储在jar和aar中,并随之发布到Maven仓库,实现接口和实现的彻底分离。

如果您对ToyBricks有什么问题或者建议,欢迎通过后面的联系方式联系我。

参考资料:

  1. SnowdreamFramework/ToyBricks
  2. Annotation Processing Tool (apt)
  3. ANNOTATION PROCESSING 101
  4. Annotation-Processing-Tool详解
  5. Java注解处理器
  6. 什么是高内聚、低耦合?

联系方式

sn0wdr1am

原文:http://mrhaki.blogspot.com/2017/04/gradle-goodness-enable-build-cache-for.html

Gradle 3.5 引入了构建缓存特性。通过构建缓存,我们可以在不同的电脑之间,不同的builds之间,共享Task输出结果。举个例子,持续集成服务器上构建的输出,可以在开发者的电脑上复用。要启用这项特性,我们只需要添加命令行选项--build-cache。或者,我们还可以我们工程下的gradle.properties文件中,将org.gradle.caching属性设置为true。为了对所有工程都启用构建缓存特性,我们可以在Gradle主目录下的gradle.properties文件( USER_HOME/.gradle/gradle.properties)中设置这个属性。

在下面的例子中,我们在~/.gradle/gradle.properties文件中设置org.gradle.caching属性:

1
2
# File: ~/.gradle/gradle.properties
org.gradle.caching=true

如果我们想禁用这项特性,我们可以添加命令行选项--no-build-cache

基于 Gradle 3.5 编写。

联系方式

sn0wdr1am

原文:http://mrhaki.blogspot.com/2016/12/gradle-goodness-run-task-ignoring-up-to.html

Gradle构建很快,是因为支持增量任务。简单来说,Gradle可以在运行Task前,就知道Task的输入和输出是否改变。如果什么都没有改变,那么这个Task将会被标记为up-to-date,并且不会被执行,否则则会被执行。如果不管一个Task是否是up-to-date,我们都希望能够运行它,此时我们需要增加一个命令行选项--rerun-tasks

在下面的例子中,我们为一个简单的Java工程,运行assemble任务,可以看到所有的task都运行了。当我们再次运行此Task的时候,我们看到所有的task都提示“up-to-date”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ gradle assemble
:compileJava
:processResources
:classes
:jar
:assemble
BUILD SUCCESSFUL
Total time: 1.765 secs
$ gradle assemble
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:jar UP-TO-DATE
:assemble UP-TO-DATE
BUILD SUCCESSFUL
Total time: 0.715 secs
$

为了忽略up-to-date检查,强制执行所有task,我们需要添加这个选项 –rerun-tasks:

1
2
3
4
5
6
7
8
9
10
11
$ gradle --rerun-tasks assemble
:compileJava
:processResources
:classes
:jar
:assemble
BUILD SUCCESSFUL
Total time: 1.037 secs
$

基于Gradle 3.2.1编写。

联系方式

sn0wdr1am

原文: http://mrhaki.blogspot.com/2017/02/gradle-goodness-check-operating-system.html

有时候,我们想检测下构建脚本在哪个操作系统上执行。举个例子,比如我们有一些Task,需要在Windows操作系统上执行,但是不在其他操作系统上执行。Gradle有一个内部的类org.gradle.nativeplatform.platform.internal.DefaultOperatingSystem,但是我们不应该在构建脚本中使用这个类。Gradle在内部使用这个类,并且可能毫无警告地修改它。如果我们依赖这个类,一旦这个类被修改了,我们的构建脚本可能无法正常运行。但是,我们可以使用一个来自Ant的类(org.apache.tools.ant.taskdefs.condition.Os)。这个类包含各种方法和变量,来检测操作系统名称,版本和架构。 这些取值都基于Java的系统变量,包括os.name, os.version 和 os.arch.

在下面的例子中,为了我们稍后能够直接调用这个类的方法和引用这个类的常量,我们在构建脚本中静态引入这个Os类。 我们添加了几个基于onlyif条件的Task。只有当onlyif条件为true时,Task才会被执行。Task osInfo则简单显示了当前操作系统的一些属性值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// File: build.gradle
import static org.apache.tools.ant.taskdefs.condition.Os.*
task os {
description 'Run all conditional os tasks'
}
// Create 3 tasks that simply print
// the task name that is executed
// if the build scripts runs on the
// recognized operating system.
[FAMILY_WINDOWS, FAMILY_UNIX, FAMILY_MAC].each { osName ->
// Create task.
tasks.create(osName) {
description "Run when OS is ${osName}"
// Add condition to check operating system.
onlyIf { isFamily(osName) }
doLast {
println "Execute task '${it.name}'"
}
}
// Add task as dependency for the os task.
os.dependsOn osName
}
task osInfo {
description 'Show information about the operating system'
doLast {
println "Family: ${OS_NAME}"
println "Version: ${OS_VERSION}"
println "Architecture: ${OS_ARCH}"
}
}

接下来,让我们在MacOS上运行这些Task: os and osInfo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ gradle os osInfo
mac
Execute task 'mac'
:unix
Execute task 'unix'
:windows SKIPPED
:os
:osInfo
Family: mac os x
Version: 10.12.3
Architecture: x86_64
BUILD SUCCESSFUL
Total time: 0.697 secs

基于Gradle 3.3编写。

联系方式

sn0wdr1am

原文: http://mrhaki.blogspot.com/2017/02/gradle-goodness-skip-task-when-input.html

Gradle 能够很好的支持增量构建。这就是说,Gradle可以根据Task的输入和输出来决定一个Task是否需要被执行。举个例子,假如输入和输出文件没有任何改变,那么这个Task将会被跳过。通过定义Task的输入和输出,可以让我们自定义的Task也支持增量构建。我们还可以定义一个Task,当它的输入文件集合/文件夹不存在或者为空时,跳过不执行。Gradle提供了一个注解@SkipWhenEmpty,我们可以应用于Task的输入。

下面的例子,我们将会定义一个DisplayTask,用于打印一个文件夹下所有文件的内容。当这个文件夹为空时,我们希望跳过这个Task,不执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
task display(type:DisplayTask) {
contentDir = file('src/content')
}
class DisplayTask extends DefaultTask {
@SkipWhenEmpty
@InputDirectory
File contentDir
DisplayTask() {
description = 'Show contents of files'
}
@TaskAction
void printMessages() {
contentDir.eachFile { file ->
println file.text
}
}
}

当输入文件夹下没有任何文件时,我们运行这个Task,将会在终端看到 NO-SOURCE 字样。如果我们不添加这个注解@SkipWhenEmpty,这个构建任务将会失败。

1
2
3
4
5
6
$ gradle display
:display NO-SOURCE
BUILD SUCCESSFUL
Total time: 0.866 secs

接下来,我们在这个文件夹(src/content)下添加一个文件,然后再次运行这个Task:

1
2
3
4
5
6
7
8
$ gradle display
:display
Gradle rocks!
BUILD SUCCESSFUL
Total time: 0.866 secs

使用Gradle 3.4编写。

联系方式

sn0wdr1am

翻译自: http://mrhaki.blogspot.com/2017/03/gradle-goodness-create-shortcut-key-to.html

我们可以在IntelliJ IDEA中打开一个Gradle工程,并且获得IntelliJ对Gradle的原生支持。当我们在Gradle build文件中添加一个新的依赖或者插件时,需要在 IntelliJ IDEA中刷新工程。我们需要刷新这个Gradle工程,来保证IntelliJ IDEA 能够同步这些改变。Gradle工具栏有一个Icon,点击它可以刷新所有Gradle工程。但是这样做,意味着我们需要移动鼠标,而我们现在需要一个快捷键来完成这些操作,这样,我们就不需要把手移开键盘。

刷新所有Gradle项目的操作,实际上是刷新所有外部功能的操作。我们可以通过 Preferences | Keymap 来添加键盘快捷键。我们在搜索栏搜索“refresh all external”来找到这个动作。

idea-refresh-gradle-keymap.png

我们可以右键点击这个Action,然后选择“Keyboard Shortcut”来定义新的键盘快捷键。

现在当我们修改Gradle build文件时,我们可以简单的使用快捷键来刷新Gradle工程。

下面我们介绍如何将这个刷新Gradle工程的Action添加到工具栏。右键点击工具栏,并且选择“option Customize Menus and Toolbars…. ”。现在可以将”Refresh all external projects”添加到工具栏:

idea-refresh-gradle-keymap-assign.png

联系方式

sn0wdr1am

在Gradle下,我们可以通过由Java插件添加的测试任务,来运行单元测试代码。默认情况下,项目下的所有单元测试代码都会执行。但是,如果我们只想运行一个简单的单元测试,我们可以通过Java系统属性test.single来制定这个单元测试的名字。实际上,系统属性的样式是taskName.single. 其中taskName是项目下测试类任务的名称。下面我们将展示在构建中如何实践的:

首先,我们创建一个简单的build.gradle文件,用来运行单元测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// File: build.gradle
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
testCompile 'junit:junit:[4,)'
}
test {
testLogging {
// Show that tests are run in the command-line output
events 'started', 'passed'
}
}

下一步,我们创建两个测试类,每个测试类包含一个简单的单元测试方法。稍后,我们会演示,如何运行其中的一个单元测试。

1
2
3
4
5
6
7
8
9
10
11
12
13
// File: src/test/java/com/mrhaki/gradle/SampleTest.java
package com.mrhaki.gradle;
import static org.junit.Assert.*;
import org.junit.*;
public class SampleTest {
@Test public void sample() {
assertEquals("Gradle is gr8", "Gradle is gr8");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
// File: src/test/java/com/mrhaki/gradle/AnotherSampleTest.java
package com.mrhaki.gradle;
import static org.junit.Assert.*;
import org.junit.*;
public class AnotherSampleTest {
@Test public void anotherSample() {
assertEquals("Gradle is great", "Gradle is great");
}
}

为了只运行SampleTest,我们需要在终端执行test任务的时候,添加一个Java系统属性 -Dtest.single=Sample

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ gradle -Dtest.single=Sample test
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses
:test
com.mrhaki.gradle.SampleTest > sample STARTED
com.mrhaki.gradle.SampleTest > sample PASSED
BUILD SUCCESSFUL
Total time: 11.404 secs

请注意,现在只有一个单元测试执行了。Gradle将会获取Sample的值,并且按照下面的样式 **/<Java system property value=Sample>*.class去查找单元测试类。因此,我们不需要输入单元测试的完整包名和类名。而为了仅仅执行AnotherSampleTest单元测试类,我们可以通过改变这个Java系统属性(test.single)来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ gradle -Dtest.single=AnotherSample test
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test
com.mrhaki.gradle.AnotherSampleTest > anotherSample STARTED
com.mrhaki.gradle.AnotherSampleTest > anotherSample PASSED
BUILD SUCCESSFUL
Total time: 5.62 secs

我们还可以使用Java系统样式来执行多个单元测试。例如:我们可以使用*Sample来同时运行 SampleTest 和A notherSampleTest。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$ gradle -Dtest.single=*Sample test
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:test
com.mrhaki.gradle.AnotherSampleTest > anotherSample STARTED
com.mrhaki.gradle.AnotherSampleTest > anotherSample PASSED
com.mrhaki.gradle.SampleTest > sample STARTED
com.mrhaki.gradle.SampleTest > sample PASSED
BUILD SUCCESSFUL
Total time: 5.605 secs

为了展示对于其他类型的测试任务,这种Java系统属性都有效。我们在build.gradle 文件中添加了一个新的任务。我们命名这个测试任务为sampleTest,包含了我们所有的测试类。我们同样应用了和之前一样的testLogging设置,方便跟踪单元测试输出结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// File: build.gradle
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
testCompile 'junit:junit:[4,)'
}
task sampleTest(type: Test, dependsOn: testClasses) {
include '**/*Sample*'
}
tasks.withType(Test) {
testLogging {
events 'started', 'passed'
}
}

下面我们值运行SampleTest测试类,但是我们换一种方式使用Java系统属性 -DsampleTest.single=S*:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ gradle -DsampleTest.single=S* sampleTest
:compileJava UP-TO-DATE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:compileTestJava UP-TO-DATE
:processTestResources UP-TO-DATE
:testClasses UP-TO-DATE
:sampleTest
com.mrhaki.gradle.SampleTest > sample STARTED
com.mrhaki.gradle.SampleTest > sample PASSED
BUILD SUCCESSFUL
Total time: 10.677 secs

实例使用 Gradle 1.6 编写。

联系方式

sn0wdr1am

一提到Docker,你可能想到云服务,运维等等。
今天,我们要谈谈Docker的本地应用,如何基于Docker和Debian打造一款个人专属操作系统。

简介

一个Docker镜像运行起来就相当于一个没有桌面的Linux系统。

现在,我们给一个基于Debian的Docker镜像,加上Mate桌面,就成了一个完整的Linux操作系统了。

为了保证我们可以通过网络来访问这个系统,我们再安装上OpenSSH和X2GO。

这样,一款基本的个人专属操作系统就完成了。当然,你可以在这个基础上,增加常用的软件,打造自己的个人专属操作系统。

构成

主要包含以下几个部分:

  • Debian jessie
  • Mate Desktop
  • Openssh-server
  • X2goserver

下载 && 安装

1. snowdream/desktop

下载Docker镜像:

1
docker pull snowdream/desktop

2. X2Go 客户端

以mac为例,其他参考: http://wiki.x2go.org/doku.php/doc:installation:x2goclient

先下载安装XQuartz-2.7.11.dmg(https://www.xquartz.org)

再下载安装x2goclient (https://code.x2go.org/releases/binary-macosx/x2goclient/releases/4.1.0.0/)

运行

1. 启动snowdream/desktop

通过以下Docker命令,启动镜像。
请留意提示的root和dockerx用户的密码,并记录下来。

1
2
CID=$(docker run -p 2222:22 -t -d snowdream/desktop)
docker logs $CID

2. 通过ssh访问

通过以下终端命令,连接上面的镜像。
密码见前面的提示。

1
ssh root@localhost -p 2222

3. 通过x2go访问桌面

  1. 启动x2go客户端
  2. 配置x2go客户端

点击主界面工具栏第三个按钮,看看全局设置中,XQuartz的路径和版本是否正确。

配置x2go客户端

接着,按照下面提示,创建一个会话。

其中,Host为主机IP,Login为用户名,SSH port为ssh端口,
底部的会话桌面选择Mate。

配置x2go客户端

3.启动会话,连接桌面。

联系方式

sn0wdr1am

Fork me on GitHub