Kotlin Android开发一些心得

作者: Lainn | 来源:发表于2017-05-31 00:04 被阅读7066次

    一周前开始学习kotlin,现在已经在项目中开发使用了。我目前负责的项目老代码非常多,之前的java代码已经有十多万行了。所以即便使用了kotlin,也只是在新建文件的代码里使用,老代码继续用java。kotlin的好处就是完全兼容java,java调用kotlin,kotlin基本上无阻碍。

    先说说为什么要转kotlin

    1. Google已经明确kotlin为第一官方语言。我相信Google的决意,就像当初毫不犹豫的抛弃eclipse转向as。kotlin已经是不可避免的大势所趋了。

    2. Kotlin与java代码完全兼容。使用kotlin就好像只是增加了一个库。打个比方,Rxjava比较难上手吧,不懂Rxjava的人看Rxjava像看天书,更不知从何写起。kotlin学起来比Rxjava简单的多。

    3. 作为一名程序猿,我是非常追求代码的简洁高效。在代码简洁上,kotlin简直是神器,太和我心意了。由于要兼容低版本,之前就一直在用java的lambda表达式插件,但是很多java8的特性还是用不了。kotlin全部能做到,而且更好。在易理解的基础上,能用1行代码搞定的事我绝不会写两行。用了kotlin,我自己写的很多基础函数都用不着了。真正的Enjoy Coding!

    也说一下目前遇到的一些问题

    1. 由于集成了kotlin包,apk会增大约0.5M。这看你的项目是否能接受了,我觉得还行。

    2. 目前没发现什么大坑。在java-kotlin互相调用时要小心,koltin如果不注明,是强制不为空的。如果你的java代码有良好的习惯,出入参都注明了@Nullable @Nonnull,就没什么问题了

    安装与配置

    参考kotlin官网

    我的android studio 没有升级3.0预览版,使用的还是2.3版本。

    1. 首先更新kotlin语言插件


      屏幕快照 2017-05-30 下午6.17.38.png
    2. 工程里添加如下插件配置
    //必须
    apply plugin: 'kotlin-android' 
    //快捷的findviewfindid操作,建议加上
    apply plugin: 'kotlin-android-extensions' 
    

    然后是依赖路径

    buildscript {
        //可能由于androidstudio版本原因,老提示我这里使用1.1.2-3版本,最新应该是1.1.2-4,根据提示来即可
        ext.kotlin_version = '1.1.2-3'
        repositories {
            jcenter()
            //阿里的maven仓国内镜像,如果gradle下载很慢,建议使用该仓库
            maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
            mavenCentral()
    
        }
        dependencies {
            classpath 'com.android.tools.build:gradle:2.3.2'
            classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
            classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
        }
    }
    

    最后是编译依赖的仓库,用着这个就行了。

    compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    

    注意几点:

    1. 如果之前使用了依赖于注解的库如Dragger,databinding等,需要额外的配置,详情请参考官网
    2. Anko 是一个封装了很多android操作的库,比如ui操作、异步操作、数据库操作等等。不过我目前觉得还用不着,就没加。感兴趣的同学可以自己去添加。

    ok, 可以进入kotlin世界了!


    我主要是参考了这本书 《Kotlin for Android developers》 ,加上一些自己的理解。内容不多,看几遍就记得差不多了。刚学习kotlin时可以下载下来,使用时随时查阅。

    Koltin的类

    kotlin的变量申明 val 是不可变变量,相当于java里的finalvar是可变变量,一般情况尽量使用val 。如果使用var一般都需要判空
    val i = 7 //自动类型推导
    val str:String = "hello"//也可以显式的指明类型
    var name: String? = null//可变变量一般要注意使用安全操作符? ,使用时都要加上? val length = name?.length
    这里有个小坑,如果没加传入了空参数是,kotlin会强制判空抛异常。

    只要是Java语言有交互的参数,除非是100%确定不为空,否则请务必都加上安全操作符?

    类继承
    class CustomView(context: Context?, attrs: AttributeSet?) : View(context, attrs) 构造函数
    额外添加一个属性name
    class CustomView(val name:String, context: Context?, attrs: AttributeSet?) : View(context, attrs)
    添加其他构造函数
    class CustomView(context: Context?, attrs:AttributeSet?) : TextView(context, attrs){ constructor(name: String, context: Context?, attrs:AttributeSet?):this(context, attrs) { text = name } }

    另外,kotlin不需要new操作符了,创建类直接调用其构造方法,就像使用普通函数那样。
    val view = CustomView(context, attrs)

    Kotlin的方法

    方法申明差不多,动手写一下很快就明白了。
    默认参数特别好用! 终于可以不用写一大堆同名函数了
    动态方法扩展相当于将一大堆Util换种写法

    比如异步加载图片的封装,java这样写

      public static void setImageUriAsync(@NonNull ImageView iv, String uri, boolean round) {
        ...    
      }
    
      public static void setImageUriAsync(@NonNull ImageView iv, String uri) {
            setImageUriAsync(iv, uri, false);
     }
    

    kotlin这样写:

    fun ImageView.setImageUrlAsync(url: String?, isRound: Boolean = false) {
        ...
    }
    

    调用时就当做imageview的普通方法使用即可

    iv.setImageUrlAsync(url)
    iv.setImageUrlAsync(url, true)
    

    注意一点

    在java里,所有的方法都要在声明在类里,一般一个类也只能有一个pulic方法。kotlin的方法可以独立放在类外面,左右全局使用,比如前面提到ImageView扩展方法就要单独放在类外部。kotlin一个文件可以申明多个全局方法,声明多个public类。就是说,很多东西都能放在kotlin一个文件里。

    Koltin的Null处理

    使用安全操作符?可以省略很多无意义判空操作
    不多说,直接上代码,一看就都明白了。
    以前这样写

        if (adapter != null) {
            List<CmdNotice> list = adapter.getDatas();
                    if (list != null) {
                        for (CmdNotice it :list) {
                            if (it.getId() == notice.getId()) {
                                it.setStatus(notice.getStatus());
                                adapter.notifyDataSetChanged();
                                break;
                            }
                        }
                    }
            }
    

    现在这样写

    adapter?.datas?.firstOrNull({ it.id == notice.id })?.let {
                        it.status = notice.status
                        adapter?.notifyDataSetChanged()
                    }
    

    静态常量,静态函数,单例

    如果一个类里都是静态常量和静态函数,直接在最外面写

    object Const{
        val MSG_1 = 1
        fun getCurrentDate():Date{
            return Date()
        }
    }
    

    如果是在普通类里有静态变量,以及单例,这样写

    class MyCache{
        companion object {
            val TYPE = 1 //常量
            val instance = MyCache() //单例
        }
    }
    

    这样调用
    MyCache.TYPE
    MyCache.instance

    kotlin的inline(内联)函数

    内联函数与一般的函数不同,在编译时会做替换,少了普通函数调用的压栈出栈,更高效。因为是替换,所以可以识别传入的泛型
    比如
    inline fun <reified T> getClassName():String{ return T::class.java.name }
    直接这样调用
    val name = getClassName<AppCompatActivity>()

    可以传入代码块,比如一个异步线程操作的代码块

    object MyExecutor{
        val instance: ExecutorService = Executors.newCachedThreadPool()
    }
    inline fun doSync(crossinline block: () -> Unit) {
        MyExecutor.instance.execute { block() }
    }
    
    //调用如下。 传入的block代码块可以直接放在函数后面的{}里。
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.main)
            doSync{
                //Code: Request network
            }
        }
    

    Kotlin基础库里大量的便捷操作以及Anko库里很多操作都是使用inline函数完成的。常用的when, apply, let,以及对list数组封装的各种操作firstOrNullfilter等都是内联函数完成的。

    Koltin函数需要传入的代码块可以直接放在函数名的大括号里,不需要再用小扩号包起来。这样的话,很多调用就非常简洁
    比如Anko库里的async, uiThread ,异步操作的写法比Rxjava简单的多!

            async{
                //Do step1 cost time
                uiThread { toast("complete step1") }
                //Do step2 cost time
                uiThread {  toast("complete step2")}
            }
    

    而且其写法很巧妙,只保存了外部Activity的弱引用,如果在异步执行过程中Activity销毁了uiThread则不会调用,防止内存泄漏。

    FindViewById

    由于有了插件kotlin-android-extensions.
    Activity里可以这样写,不需要在额外申明TextView变量了。所以注意xml文件里id的命名使用驼峰写法

            setContentView(R.layout.main)
            tvTitle.setOnClickListener { toast("hello") }
    

    Adapter里这样写, ViewHolder完全不需要了

       view.tvName.text = notice.name
       view.tvNumber.text = ""
       view.ivIcon.setImageUrlAsync(icon, true)
    

    其他

    1. 注意在kotlin中== 相当于java中的equal函数,=== 相当于java中的==。不过一般情况加==就够了。kotlin中没有基本类型,所有的如Int,Long都是对象。

    2. kotlin中没有三元操作符。可以用if else,或者 ?:代替。
      java: String a = result?"true":"false"
      kotlin:val a = if (result) "true" else "false"
      ?:表示先判断前面是否为空,为空的话就返回后面的
      java: String b = (a==null)?a:"true"
      kotlin: val b=a?:"true"

    因为都是基于JVM,Kotlin的所有操作java当然都能做到,但是kotlin更简洁容易理解!
    先到这里,如有问题欢迎指正,大家共同进步!

    相关文章

      网友评论

      • 小鹏_ec96:?:表示先判断前面是否为空,为空的话就返回后面的
        java: String b = (a==null)?a:"true"
        kotlin: val b=a?:"true"
        是不是写错了,java写反了是吧

        java: String b = (a==null)?"true":a
      • 过期的薯条:Good.我也准备进军kotlin,估计2-3个月开始用于项目中
      • 3008884341a1:看上去很不错
      • 0d2e6c7de473:感觉跟swift有点像,可以可以

      本文标题:Kotlin Android开发一些心得

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