【思货】kotlin协程优雅的与Retrofit缠绵-kotlin DSL简述

2,933 阅读4分钟

这是一篇辅助文章,为了辅助大家理解后面的文章,先前先简单的阐述下kotlin的DSL。

DSL是什么

DSL指的是特定领域语言,利用kotlin的DSL语言,我们可以写成更加整洁的代码,是代码的可阅读行大幅度提升。基本的概念以及好处我就不再重复阐述了,网上已经有很多类似的文章了,我就通过具体的代码示例进行展示好了。

kotlin DSL代码长什么样

EditText

我还是从最常见EditTextTextWatcher说起。

Java中当我们需要添加文本输入监听的的时候,是如下的写法:

editText.addTextChangedListener(new TextWatcher() {
	@Override
  public void beforeTextChanged(CharSequence s, int start, int count, int after) {

  }

  @Override
  public void onTextChanged(CharSequence s, int start, int before, int count) {

  }

  @Override
  public void afterTextChanged(Editable s) {

  }
});

但是往往有时,我们只需要afterTextChanged监听,并不需要其他两个,但由于接口的限制,我们不得不全部写出将其实现。导致书写体验和阅读体验下降。

那么在kotlin中我们可以骚操作:

editText.onTextChange {
	afterTextChanged { s ->

	}
}

肯定有小伙伴,是这个表情:

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/6/8/16b3554d466d9837~tplv-t2oaga2asx-image.image

那么这是如何实现的呢,首先,自定义个类,继承TextWatcher

class TextWatcherDsl : TextWatcher {
    private var afterTextChanged: ((s: Editable?) -> Unit)? = null
    private var beforeTextChanged: ((s: CharSequence?, start: Int, count: Int, after: Int) -> Unit)? =
        null
    private var onTextChanged: ((s: CharSequence?, start: Int, before: Int, count: Int) -> Unit)? =
        null

    override fun afterTextChanged(s: Editable?) {
        afterTextChanged?.invoke(s)
    }

    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
        beforeTextChanged?.invoke(s, start, count, after)
    }

    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
        onTextChanged?.invoke(s, start, before, count)
    }

    fun afterTextChanged(after: (s: Editable?) -> Unit) {
        afterTextChanged = after
    }

    fun beforeTextChanged(before: (s: CharSequence?, start: Int, count: Int, after: Int) -> Unit) {
        beforeTextChanged = before
    }

    fun onTextChanged(onChanged: (s: CharSequence?, start: Int, before: Int, count: Int) -> Unit) {
        onTextChanged = onChanged
    }
}

这里面涉及到kotlin的参数方法内容(即:方法可以直接作为参数传递进),不懂自行百度,不再讲解。

接下来,来实现一个DSL的扩展方法:

inline fun EditText.onTextChange(textWatcher: TextWatcherDsl.() -> Unit): TextWatcher {
    val watcher = TextWatcherDsl().apply(textWatcher)
    addTextChangedListener(watcher)
    return watcher
}

ok,搞定了,所有的EditText都具有了onTextChange方法,监听文本的方法需要什么写什么。

  • 1、扩展方法不再具体解释,不是本文重点

  • 2、这里的inline是指内联方法,即:在编译时期,代码会直接插入掉用处,实际上不是调用外部方法。也就不会造成调用外部方法而导致的性能开销,有兴趣的小伙伴可以百度了学习。

这里需要注意理解下参数TextWatcherDsl.() -> UnitTextWatcherDsl是我们刚刚自定义的类,然后后面跟了一个,就是说,这个参数可以接收TextWatcherDsl类里的所有方法,只要是它里面的方法都可以传递进来。

SharedPreferences

我们再来看下Google官方对于SharedPreferences的扩展。惯例,我们Java中使用SP写入数据是这样的:

SharedPreferences sp = getSharedPreferences("demo", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString("key1", "value1");
editor.putString("key2", "value2");
editor.apply();

kotlin中是这样的:

val sp = getSharedPreferences("demo", Context.MODE_PRIVATE)
sp.edit {
    putString("key1", "value1")
    putString("key2", "value2")
}

是不是简单易读。

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/6/8/16b3554dc9725c58~tplv-t2oaga2asx-image.image

我们来看下官方Google是如何实现的这个方法:

inline fun SharedPreferences.edit(
    commit: Boolean = false,
    action: SharedPreferences.Editor.() -> Unit
) {
    val editor = edit()
    action(editor)
    if (commit) {
        editor.commit()
    } else {
        editor.apply()
    }
}

是的,没错就这么点。

参数SharedPreferences.Editor后面跟了一个,可以接受SharedPreferences.Editor中的方法,例如putString

File

上面的写法,会kotlin的小伙伴应该都见过了。接下来,我来展示下另一种DSL的写法,可能你们会很少用。

这里我操作File文件的方法来展示。这里我不再写java代码。

常规写法

首先我们看一下常规拷贝文件的写法:

fun copyTo(form:File, to: File) {
    // 具体操作文件拷贝的代码
}

使用:

copyTo(file1, file2)

这种太常见了,Java里也是如此操作的,不细说了。

kotlin扩展写法

接下来改造下,写成扩展方法:

fun File.copyTo(to: File) {
    // 具体操作文件拷贝的代码
}

使用:

file1.copyTo(file2)

看起来好像舒服了那么一点

终极写法

infix fun File.copyTo(to: File) {
    // 具体操作文件拷贝的代码
}

使用:

file1 copyTo file2

注意看扩展方法前面多了一个infix来修饰方法,是不是非常接近了我们人类说话时的语言方式。

你们的表情是这样的:

https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/6/8/16b3554dc6182fed~tplv-t2oaga2asx-image.image

类似的方法,在anko库中中也有很多地方使用,多去阅读源码就会发现。

本次的简介就到此为此,希望通过直接的代码展示,能让各位找到些感觉