使用Kotlin高效地开发Android App(二)总结

旋转的楼梯.jpg

上一篇文章介绍了项目中所使用的Kotlin特性,本文继续整理当前项目所用到的特性。

一.apply 函数 和 run 函数

with、apply、run函数都是Kotlin标准库中的函数。with在第一篇文章中已经介绍过。

1.1 apply函数

apply函数是指在函数块内可以通过 this 指代该对象,返回值为该对象自己。在链式调用中,可以考虑使用它来不破坏链式。

/**
 * Calls the specified function [block] with `this` value as its receiver and returns `this` value.
 */
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

举个例子:

/**
 * Created by tony on 2018/4/26.
 */
object Test {

    @JvmStatic
    fun main(args: Array<String>) {

        val result ="Hello".apply {


            println(this+" World")

            this+" World"
        }

        println(result)
    }
}

执行结果:

Hello World
Hello

第一个字符串是在闭包中打印的,第二个字符串是result的结果,它仍然是“Hello”。

1.2 run函数

run函数类似于apply函数,但是run函数返回的是最后一行的值。

/**
 * Calls the specified function [block] with `this` value as its receiver and returns its result.
 */
@kotlin.internal.InlineOnly
public inline fun <T, R> T.run(block: T.() -> R): R {
    contract {
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

举个例子:

/**
 * Created by tony on 2018/4/26.
 */
object Test {

    @JvmStatic
    fun main(args: Array<String>) {

        val result ="Hello".run {


            println(this+" World")

            this + " World"
        }

        println(result)
    }
}

执行结果:

Hello World
Hello World

第一个字符串是在闭包中打印的,第二个字符串是result的结果,它返回的是闭包中最后一行的值,所以也打印“Hello World”。

1.3 项目中的使用

在App的反馈页面中,需要输入邮箱、主题、内容才能完成反馈按钮的提交。

最初的写法是这样:

        if (viewModel.email.value!!.isEmpty()) {
            toast(resources.getString(R.string.you_have_not_completed_the_email_address)).show()
            return@onClickRight
        }
        if (!Util.checkEmail(viewModel.email.value!!)) {
            toast(resources.getString(R.string.the_email_format_you_have_filled_is_incorrect)).show()
            return@onClickRight
        }
        if (viewModel.subject.value!!.isEmpty()) {
            toast(resources.getString(R.string.you_have_not_completed_the_feedback_subject)).show()
            return@onClickRight
        }
        if (viewModel.content.value!!.isEmpty()) {
            toast(resources.getString(R.string.you_have_not_completed_the_details)).show()
            return@onClickRight
        }

修改成只使用apply函数

       viewModel.apply {

            if (email.value!!.isEmpty()) {
                toast(resources.getString(R.string.you_have_not_completed_the_email_address)).show()
                return@onClickRight
            }
            if (!Util.checkEmail(email.value!!)) {
                toast(resources.getString(R.string.the_email_format_you_have_filled_is_incorrect)).show()
                return@onClickRight
            }
            if (subject.value!!.isEmpty()) {
                toast(resources.getString(R.string.you_have_not_completed_the_feedback_subject)).show()
                return@onClickRight
            }
            if (content.value!!.isEmpty()) {
                toast(resources.getString(R.string.you_have_not_completed_the_details)).show()
                return@onClickRight
            }
        }

感觉不够cool,可以结合run和apply函数一起使用

        viewModel.email.run {

            if (value!!.isEmpty()) {
                toast(resources.getString(R.string.you_have_not_completed_the_email_address)).show()
                return@onClickRight
            }
            if (!Util.checkEmail(value!!)) {
                toast(resources.getString(R.string.the_email_format_you_have_filled_is_incorrect)).show()
                return@onClickRight
            }

            viewModel
        }.subject.run {

            if (value!!.isEmpty()) {
                toast(resources.getString(R.string.you_have_not_completed_the_feedback_subject)).show()
                return@onClickRight
            }

            viewModel
        }.content.apply {

            if (value!!.isEmpty()) {
                toast(resources.getString(R.string.you_have_not_completed_the_details)).show()
                return@onClickRight
            }
        }

二.data class

Kotlin的data class有点类似于Scala的case class。

以下的Java Bean代码

/**
 * Created by tony on 2018/4/27.
 */
public class User {

    public String userName;
    public String password;
}

等价于

data class User (var userName: String? = null,var password: String? = null)

可以看到采用data class能够简化Java Bean类。我们的App采用了MVVM的架构,因此对应Model类全部使用data class。

三.无需使用findViewById 或者 butterknife

使用Kotlin Android Extensions插件即可实现该功能,它是Kotlin 插件的组成之一,无需再单独安装插件。

我们在各个modules的build.gradle中添加该插件,即可使用。

apply plugin: 'kotlin-android-extensions'

布局文件中的id,可以直接在代码中使用。 首先,按照import kotlinx.android.synthetic.main.布局文件名*的方式导入。

例如MainActivity,它的布局文件是activity_main.xml 则按照如下的方式进行import

import kotlinx.android.synthetic.main.activity_main.*

那么activity_main.xml中控件的id,可以直接在MainActivity中使用,无需使用findViewById 或者 butterknife。是不是特别方便?

四.点击事件的埋点处理

App的埋点,使用自己家的产品--魔窗的sdk来做事件的埋点。

如果使用Java来开发App,可以使用AOP来实现埋点。由于我们的App采用Kotlin编写,Kotlin可以将事件的点击简化成如下的形式

        view.setOnClickListener {
             ....
        }

这种简化了的lambda表达式,所以我还是老老实实的使用传统方式进行埋点。

使用Kotlin的通常做法:

        view.setOnClickListener {

             TrackAgent.currentEvent().customEvent(eventName)
             ....
        }

或者

        view.setOnClickListener {

             TrackAgent.currentEvent().customEvent(eventName, trackMap)
             ....
        }

后来,我写了一个View的扩展函数click,后来经过同事的优化。可以查看简书的这篇文章 利用扩展函数优雅的实现“防止重复点击”

目前,已经将该扩展函数放入我的Kolin的工具类库 https://github.com/fengzhizi715/SAF-Kotlin-Utils

此时,埋点的代码变成这样

        view.click {

             TrackAgent.currentEvent().customEvent(eventName)
             ....
        }

或者

        view.click {

             TrackAgent.currentEvent().customEvent(eventName, trackMap)
             ....
        }

进一步的优化处理,对于View增加扩展函数clickWithTrack专门用于埋点的点击事件。

package cn.magicwindow.core.ext

import android.view.View
import cn.magicwindow.TrackAgent
import com.safframework.ext.clickWithTrigger

/**
 *
 * @FileName:
 *          cn.magicwindow.core.ext.ViewExt.java
 * @author: Tony Shen
 * @date: 2018-04-24 17:17
 * @version V1.0 <描述当前版本功能>
 */

fun <T : View> T.clickWithTrack(eventName: String, time: Long = 600, block: (T) -> Unit) = this.clickWithTrigger(time) {

    TrackAgent.currentEvent().customEvent(eventName)
    block(it as T)
}

fun <T : View> T.clickWithTrack(eventName: String, trackMap: HashMap<String, String>, time: Long = 600, block: (T) -> Unit) = this.clickWithTrigger(time) {

    TrackAgent.currentEvent().customEvent(eventName, trackMap)
    block(it as T)
}

此时埋点可以这样使用:

        view.clickWithTrack(key) {
            ....
        }

或者

        view.clickWithTrack(key,trackMap) {
            ....
        }

总结

Kotlin有很多的语法糖,使用这些语法糖可以简化代码以及更优雅地实现功能。

该系列的相关文章: 使用Kotlin高效地开发Android App(五)完结篇 使用Kotlin高效地开发Android App(四) 使用Kotlin高效地开发Android App(三) 使用Kotlin高效地开发Android App(一)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏我是攻城师

Java如何调用Linux命令

4886
来自专栏青玉伏案

iOS逆向工程之Hopper中的ARM指令

虽然前段时间ARM被日本软银收购了,但是科技是无国界的,所以呢ARM相关知识该学的学。现在看ARM指令集还是倍感亲切的,毕竟大学里开了ARM这门课,并且做了不少...

3397
来自专栏chenssy

这些Spring中的设计模式,你都知道吗?

设计模式作为工作学习中的枕边书,却时常处于勤说不用的尴尬境地,也不是我们时常忘记,只是一直没有记忆。

961
来自专栏mukekeheart的iOS之旅

Android基础总结(5)——数据存储,持久化技术

瞬时数据:指那些存储在内存当中,有可能会因为程序广播或其他原因导致内存被回收而丢失的数据。 数据持久化:指将那些内存中的瞬时数据保存到存储设备中,保证即使在手机...

2997
来自专栏求索之路

Android源码设计模式解析与实战笔记

1.单一职责原则:比如说一个ImageLoader,需要加载图片的缓存图片,此时如果将这两个功能都放在一个类中,就违反了这个原则, 我们需要将不同的功能用类精...

4645
来自专栏郭霖

Android图片加载框架最全解析(四),玩转Glide的回调与监听

大家好,今天我们继续学习Glide。 在上一篇文章当中,我带着大家一起深入探究了Glide的缓存机制,我们不光掌握了Glide缓存的使用方法,还通过源码分析对缓...

6136
来自专栏牛肉圆粉不加葱

[Spark源码剖析]Task的调度与执行源码剖析

一个Spark Application分为stage级别和task级别的调度,stage级别的调度已经用[DAGScheduler划分stage]和[DAGSc...

1792
来自专栏IT杂记

通过Java程序提交通用Mapreduce无法回收类的问题

问题描述 上次发布的博客 通过Java程序提交通用Mapreduce,在实施过程中发现,每次提交一次Mapreduce任务,JVM无法回收过程中产生的MapRe...

3116
来自专栏IT杂记

通过Java程序提交通用Mapreduce任务并获取Job信息

背景 我们的一个业务须要有对MR任务的提交和状态跟踪的功能,须要通过Java代码提交一个通用的MR任务(包括mr的jar、配置文件、依赖的第三方jar包),并且...

1.1K5
来自专栏向上的小草

mybati缓存机制之一级缓存

  在月黑风高的某天夜晚,boss chen语重心长的发条了消息给小草说:“小草啊,是时候写写博客来记录平常自己积累的东西了......”。小草一听,平常没有写...

2820

扫码关注云+社区

领取腾讯云代金券