开始一个 transaction,调用 “try” 代码段,然后设置 transaction 为成功,最后,结束它。我们接收一个函数作为参数,在使用这个小技巧后,我们在 “try” 代码段里面执行该函数。在我们的消费者代码里,我们会调用这个我们称作 inTransaction 的方法,在这个方法里面我们写的代码都会在一个 transaction 里面执行。你没有别的办法来错误地使用它了。
渠道化你的内部 SQLiteDatabase
一个有趣的事情是,在这样的实现方法下,你的代码里面还是会需要 database 的引用。这也是可能会出错的地方。如果你有两个数据库,你可能会在 transaction 里面引用了那个错误的数据库。
我们有多种方法来解决这个问题。一个方法就是我们可以让函数的参数包含需要发生 transaction 的数据库的引用。我们传了 this 给函数,然后我们就不需要调用 “db”,而是使用 “it” 了,它是传给表达式的第一个参数。
这个方法还是不太好。我们阻止了潜在问题的发生,但是现在我们每次访问 database 的时候都需要调用 “it”。这里有个有意思的事情是输入参数是个函数。我们可以把这个函数改写成 “SQLiteDatabase” 的一个扩展函数。
这将会使你有些迷惑,因为它是个疯狂的强大的概念。通过把 func(this) 替换成 this.func(),传入的函数参数变成了该对象的扩展函数,而且这个扩展函数已经存在 “SQLiteDatabase” 里了。因为我们并不是真正需要 this,所以我们抛弃了它。
这个函数会像在 SQLiteDatabase 里面定义的那样执行。如果我们调用删除方法,我们不需要用任何东西来证明自己有资格调用,因为我们就是个 SQLiteDatabase。现在我们可以不需要 it 了,所以每一个你放在这个代码段里面的表达式都会看起来像 SQLiteDatabase 内部的一个私有方法一样。你不需要证明自己是正确的,因为它总是能在正确的对象上执行自己。
这太棒了,它会把你的代码改编成:
[代码]csharp代码:
1
2
3
4
db.inTransaction {
it.db.delete("users", "first_name = ?", arrayOf("Jake"))
}<font face="Tahoma, Arial, Helvetica, snas-serif"><span style="font-size: 14px; white-space: normal;">
</span></font>
避免额外的垃圾回收
这仍然是安卓的一个大问题。等效的 Java 代码中,我们创建了一个函数的新的实例,然后把它传给了我们产生的静态方法。这非常不幸,因为在我们顺序执行代码之前,我们并没有真正地分配内存。
如果我们用 Java 来实现这个,我们需要在每次使用这些函数表达式的时候都分配一些极小的函数对象。这不好,因为这会触发许多的垃圾回收。
谢天谢地,我们有办法在 Kotlin 里面来避免这个问题。我们这里的函数仅仅是个定义好的函数表达式。它使用函数表达式作为入参,这会是个问题。那个函数表达式需要转成一个匿名类。
[代码]csharp代码:
1
inline fun SQLiteDatabase.inTransaction(func: SQLiteDatabase.() -> Unit) { ... }
我��可以把它变成一个 in-line 的函数,这样我们就告诉了 Kotlin 编译器不要把它作为一个静态函数调用,我需要编译器仅仅把我的函数代码替换到需要调用的地方。虽然这样会产生很多的 bytecode,但是这样产生的 Java 类文件会和容易出错的 Java 代码编译出来的类文件一样的。