美文网首页KotlinKotlinKotlin编程
使用Kotlin高效地开发Android App(二)

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

作者: fengzhizi715 | 来源:发表于2018-04-27 20:26 被阅读812次
    旋转的楼梯.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(一)

    相关文章

      网友评论

      • 全汪汪:apply不是也可以返回自身嘛。那样的话就没必要用run了
        JarryLeo:apply返回自身,run返回的是最后一行值
      • IT人故事会:老铁,写的很用心,你的文章我收藏了啊
        fengzhizi715:谢谢🙏

      本文标题:使用Kotlin高效地开发Android App(二)

      本文链接:https://www.haomeiwen.com/subject/qceslftx.html