这人又在折腾自己的博客,刚折腾到一半,所以是这个鬼样子。
我的黑历史被github埋到北极了

昨天整理自己的github仓库的时候发现自己的个人主页多了个徽章:Arctic Code Vault Contributor

QQ20210126-143812@2x.png

So what happened?

阅读全文
【译】用BuildSrc和Kotlin_DSL管理Gradle依赖

多模块工程中一个更好的引入依赖的方法

翻译自Gradle Dependency Management With BuildSrc and Kotlin DSL

第一次尝试翻译英文文章……

要点

主要集中在如何用buildSrc目录和Kotlin DSL脚本构建一个Gradle依赖管理系统,你也会学到这样做相对使用传统Groovy代码的好处。

如果你倾向于通过视频来看这篇博客,文末附有一个Youtube视频。

问题

众所周知在一个快速发展的项目中维护依赖是一个乏味的工作,而传统的Groovy脚本没有code navigation、自动补全,再加上性能问题和运行时错误让这一切变得更糟糕。

更重要的是,多数安卓开发者不懂Groovy,甚至我也不知道我之前在用Groovy做啥。

感谢Gradle团队和社区的工作提供了一个顺畅安全的构建流程,他们提出的最棒的主意之一就是用Kotlin DSL脚本写buildSrc目录。

解决

依赖库引入和自定义task不应该放到构建脚本中,它们应该被声明到一个独立文件中再被构建脚本使用。在这个实现的早期,开发者习惯于创建一个Gradle文件来声明所有库并在构建脚本中使用。

这确实在一定程度上解决了问题,你可以在这篇文章读到这种方法。但这个简单方案不能解决类似自动补全和code navigation的问题,这使得在长远上看这个方案不够可靠。在这之外,buildSrc似乎有希望解决这个问题。

这个目录被当作一个included build看待。在发现这个目录之后,Gradle自动编译和测试它的代码,并将编译结果放到你的构建脚本的class path中。在一个多模块的工程中只能有一个这样的目录,并且要放到工程的顶级目录中。应该优先通过script plugins因为这样更便于管理、重构和测试代码。 ——Gradle团队

创建buildSrc目录

使用Kotlin DSL脚本不但能解决构建脚本中的这些问题,还能得到先进的IDE支持,包括code navigation、编译时错误提示等。最重要的,我们再也不用使用Groovy了。

阅读全文
gradle学习笔记

新建一个安卓项目,Android Studio已经为我们生成的模板中,build.gradle总是很显眼。我也终于下定决心来了解下这个构建整合工具。

gradle脚本使用groovy编写,最近也开始支持用Kotlin编写。Kotlin大法好,但我暂时只找到了groovy的教程1,暂时不想去啃英文,先将就学着。

gradle安装

命令

对于OSX用户来说,brew install gradle就可以搞定这个步骤了。但是这里有个问题,就是这样会把openjdk当做依赖装上。也不算很大的问题,也能将就用。

gradle wrapper

gradle有一个很方便的命令,gradle wrapper,使用后会创建如下的目录结构

.
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
└── gradlew.bat

此时即使你没有安装gradle,仍然可以使用./gradlew代替gradle执行命令。如./gradlew clean install。在实际的项目中,这种方式使用比较普遍,因为gradle脚本很多特性受版本影响比较大,一般gradle的版本也会在项目中指定。

阅读全文
java默认修饰符问题

问题描述

和工具线配合完成某个需求,我这边的改动很少,但一鼓作气搞完后却遇到了奇怪的报错。已知工具线的代码大多是java的,而我这边自然是力推Kotlin。我们的代码参考如下:

代码参考

工具线定义了一个接口用于callback

package a;

interface IPublishCallback {
    void onFinish();
}

工具线在执行完发布逻辑后无论成功还是失败都会调用我们的callback

package a;

public class PublishUtil {
    public static void publishVideo(String videoName, IPublishCallback callback) {
        Runnable runnable = () -> {
            try {
                System.out.println("[" + videoName + "]开始执行耗时发布操作……");
                Thread.sleep(1000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            callback.onFinish();
        };
        Thread thread = new Thread(runnable);
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

我们这边的实现是Kotlin的,就是调用了下工具线的方法

package b

import a.PublishUtil

class ShareFromSdkImpl {
    fun doShare() {
        println("系统分享功能测试")
        PublishUtil.publishVideo("测试视频") {
            println("发布视频完成回调")
        }
        println("完成系统分享方法")
    }
}

主函数

import b.ShareFromSdkImpl

fun main() {
    ShareFromSdkImpl().doShare()
}

问题

上述代码在执行后输出如下:

系统分享功能测试
Exception in thread "main" java.lang.NoClassDefFoundError: a/IPublishCallback
	at b.ShareFromSdkImpl.doShare(ShareFromSdkImpl.kt:8)
	at MainKt.main(main.kt:4)
	at MainKt.main(main.kt)
Caused by: java.lang.ClassNotFoundException: a.IPublishCallback
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
	... 3 more

Process finished with exit code 1

在安卓中略有不同,异常类型为

java.lang.IllegalAccessError: Interface a.IPublishCallback implemented by class com.ss.android.ugc.aweme.plugin.xground.player……

排查

注意报错中提到的a/IPublishCallback,是我们前面定义的回调接口。

我们将lambda改为匿名内部类的写法,这才发现确实是找不到。

2021-08-06_01-33.png

在使用Lambda形式实现回调时这个错误没有被编译器检查出来,运行时才报出来。

阅读全文
android项目开发:Kotlin

Kotlin

变量和函数

变量

varval定义变量,并有类型自动推导的支持。

val用来声明一个不可变的变量,var用来声明一个可变的变量。

val a = 10
var b: Int = 12

Kotlin完全抛弃了java中的基本数据类型,完全使用对象数据类型。

java基本数据类型Kotlin对象数据类型数据类型说明
intInt整型
longLong长整型
shortShort短整型
floatFloat单精度浮点型
doubleDouble双精度浮点型
booleanBoolean布尔型
charChar字符型
byteByte字节型

函数

语法:

fun main(args: Array<String>) {
    println("Hello World!")
}

Kotlin中的函数在无必要时可以省略很多东西:

import kotlin.math.max

fun largerNumber(a: Int, b: Int) = max(a, b)

fun main(args: Array<String>) {
    val a = 3
    val b = 5
    println("The larger number of a and b is ${largerNumber(a, b)}")
}

程序的逻辑控制

if

与java中的if语句相比,Kotlin中的if是可以有返回值的。

fun judge(score: Int) = if (score >= 60) "你及格了" else "你还需要多努力"

fun main() {
    println(judge(55))
    println(judge(66))
}

与此同时,Kotlin不再有java中的三元运算符1,语义上清晰了很多。

when条件语句

类似于java中的switch语句,根据变量的值执行不同的逻辑。

fun judge(score: Int) = if (score >= 60) "你及格了" else "你还需要多努力"

fun getScore(name: String) = when (name) {
    "Tom" -> 78
    "Jack" -> 35
    "Jerry" -> 84
    "Lee" -> 57
    else -> 0
}

fun main() {
    println(judge(getScore("Tom")))
    println(judge(getScore("Jack")))
    println(judge(getScore("Bill")))
}

对我来说,最令人振奋的是再也不需要在每个分支里面都写个break了。其次when也是有返回值的,这和其他特性组合写出来的代码非常简洁优雅。

写个小demo吧。

interface Speakable {
    fun speak()
}

class Dog : Speakable {
    override fun speak() {
        println("汪汪汪")
    }
}

class Cat : Speakable {
    override fun speak() {
        println("喵喵喵")
    }

    fun climb() {
        println("小猫会爬树")
    }
}

fun generateAnimal(): Speakable? = when ((1..3).random()) {
    1 -> Dog()
    2 -> Cat()
    else -> null
}

fun main() {
    when (val animal = generateAnimal()) {
        is Dog -> {
            println("生成的动物是小狗")
            animal.speak()
        }
        is Cat -> {
            println("生成的动物是小猫")
            animal.speak()
            animal.climb()
        }
        else -> println("生成动物时出现问题")
    }
}

循环语句

Kotlin中有两类循环,其中while循环与java学过的while循环非常相似,只说下有差异的for循环吧。

Kotlin的for循环只有for..in式的了,如for(i in list)

但有时对数组下标进行遍历还是有必要的。于是我们要先了解下Kotlin的区间的概念。

val range = 1..10

这表示[1,10]。但很多时候,我们需要左开右闭区间,比如数组有三个元素,我们需要[0,3)表示数组的下标。此时可以使用util关键字。

val indexRange = 0 util 3

有了range再和前面的for配合就完全可以替代之前java里面的for的作用了:

val array = arrayOf("Bob", "John", "Jackson")
for (i in 0 util array.size) {
    println("$i:${array[i]}")
}

此外,还可以用step指定步长值,实现”隔几个输出一次“的效果:

for (i in 0 util 10 step 2) {
    println(i)
}

如果需要10循环到1,则需要downTo关键字,

for (i in 10 downTo 1) {
    println(i)
}

阅读全文
安卓连连看游戏设计

字节取消大小周了,于是周末时间多起来了。虽然想着到处去玩,但现在疫情形势又不好了,于是安心呆在家里当一个肥宅。除了补一补番剧,就是把之前就想过的连连看游戏做出来了。

连连看游戏规则简单,点击两个相同的元素,如果他们能在两次拐弯以内连接起来,那么就可以消除。消除后就会出现空位,可以连接的就更多了。在规定时间内连续操作,直到消除所有元素。

虽然规则比较简单,但真正动手实现一遍还是很费工夫的。游戏既然做好了,那么我水一篇博客不过分吧:>

连连看游戏界面 1

项目的代码我放到了github。写的贼丑,轻喷。 https://github.com/zerofancy/match


  1. 本图片由笑果图床 提供支持。

阅读全文
关于

归零幻想

归零幻想

归零幻想是本人的网名啦,这里是我的博客小站。本人在其他地方帐号大多也叫这个名字。

本人2021年本科毕业于中国石油大学(华东),现在人在抖音。

博客系统至今我写过三个版本[^1],毕竟写出个CRUD就可以厚着脸皮说自己是个博客了。当前版本最初是个Kotlin练手项目,源代码发布在github。不过也就自用罢了,不会有人对我的代码感兴趣吧……

博客系统作为学生时代练手作品,问题多多,但段时间没有重构或更新的计划。毕竟人在火星,信号延迟高,更新博客都比较少了。

阅读全文
数据结构 栈应用2 表达式求值

表达式求值是程序设计语言编译中的一个最基本问题,它的实现是栈应用的一个典型例子。

Question

表达式求值是进行数据处理的最基本操作。请编写程序完成一个简单算术表达式的求值。要求如下:

  1. 运算符包括:+、-、*、-、^(乘方)、括号
  2. 运算量为数值常量,根据自己的能力可以对运算量做不同的约束,例如1位整数、多位整数、实数等(会有不同的测试用例);

阅读全文
装饰模式实现分享功能

title: 装饰模式实现分享功能 author: 归零幻想 publishDate: 2021-07-19 editDate: 2021-07-19 tags: [设计模式, Kotlin, 分享]

在看业务代码时发现了一段代码,应用了装饰模式处理了分享功能的实现,非常巧妙,共赏。

装饰模式.svg.png.lin.png

阅读全文
avatar

归零幻想

邮件:[email protected]
Telegram: @zerofancy

现在是零点,那么,归零幻想。
RSS

友情链接