android怎么截取接口返回html代码中的内容_Kotlin 怎么学 ?遇到过哪些坑?

本文作者

作者:kotlon

链接:

https://www.jianshu.com/p/dcf6cd7c59a3

本文由作者授权发布。

1 kotlin 优势

kotlin 在 17 年 google io 大会上确定为亲儿子,android studio canary 3.0 版本开始,直接支持 kotlin 语言,不需要额外安装 as plugin。

kotlin 的出现,给 android 开发者带来了极大的活力,现在的 android 工业开发,讲究的是语法糖,效率,性能,高质量,以及可拓展性,而 kotlin 的出现给 java 开发者带来了极大的福音。

kotlin 官网教程英文 

https://kotlinlang.org/docs/reference/basic-types.html

kotlin 官网教程中文 

https://www.kotlincn.net/docs/reference/

阅读此文章大约半小时~

如何阅读?

浏览kotlin 优势和kotlin 坑这两大章节,然后去学习 kotlin 基础语法,根据第三章,kotlin 普及里的建议,可以在实际开发中,开干了。

避免 npl以及非空判断更优雅

kotlin 默认是非空的,如果你需要声明一个可能为空的变量,那么如下:

private  

这里的 mName 变量声明为,String 类型;mSubName 声明为 String?。前者不能为 null,后者可以为 null。

然后,你可能经常在 java 中写这样的代码:

if (mANRPA != 

那么在 kotlin 你这要这样写就行了:

mANRPA?

使用 ?.  的效果和上面的 java 代码是一样的,表示该变量可能为空,如果不为空则执行后面的表达式。

参考地址:

https://www.kotlincn.net/docs/reference/null-safety.html

java 语法完全兼容 kotlin

你可以通过几个小时学习 kotlin 的基础语法,包括:

  1. 怎么定义变量

  2. 怎么定义方法

  3. 怎么定义类

  4. 等等......

然后就可以像 java 一样使用 kotlin,当然这不是我们的最终目的,因为 kotlin 的语法糖才是我们最后的目标。

google 发布了 kotlin 简易教程,大概只需要几个小时,就可以看完:

  • 几小时上手 kotlin

在实际的开发中,大概第一个星期内,你写代码的速度会下降一些,但是一个星期之后,完全上手了,写代码的速度是有很大的提升的。

2 kotlin 高级语法(语法糖示例)
1.extension-扩展函数和扩展属性

关于 扩展函数的说明:

https://www.kotlincn.net/docs/reference/extensions.html

android ktx 库提供了一系列的优秀的扩展函数 官方说明

https://android.github.io/android-ktx/core-ktx/index.html

简单的说,扩展函数是什么呢?先来看一个例子,在java 里面 你要 remove 一个 View 的 Parent,你可能会这样写:

if(mLoadingView.getParent() != 

但是每次都这样写,貌似比较重复,对吧;然后你就可能考虑写一个静态方法,类似如下,比如在 ViewUtils 类似命名里写一个静态函数:

public static void removeSelf(View view){

但是在 kotlin 中,使用扩展函数,可以更巧妙更直接的实现这个功能。

写在某个文件里面,编写一个顶级的扩展函数,如下:

inline 

然后在需要调用的地方,你直接这样写就行了:

mTopBar?

当然 kotlin 扩展函数的作用并不仅仅在此,它的思想主要是丰富实际类的语法。

例如,现在让你设计一个功能,点击一个按钮,然后发起一个网络请求,但是需要注意在 View 的生命周期内,如果View 销毁了,取消这个网络请求,并且不会更新UI。那么常规的 java 代码,写起来可能就是一堆的回调,但是使用 kotlin 的 扩展函数+代理,封装之后,你看到的就是下面一行代码。

//这里封装了 onClick() 方法,详细的细节,后面会介绍
mJumpBitmap.onClickAutoDisposable { 
    //这里是你的网络请求操作
}
2.Delegate And Delagate Properties -委托以及委托属性

代理在设计模式上是非常优秀的,例如 retrofit 框架里,大量的使用动态代理这种设计模式;然后 kotlin 在语法层面去支持了代理,包括代理和代理属性。

代理属性的话,kotlin 支持一些标准的代理属性,例如 by lazy,Observable,Storing;其中 by lazy,提供三种类型的懒加载,包括非线程安全,synchronized 同步线程安全,以及cas 操作线程安全。

示例如下,例如在 PushProto.java 中,需要对 ProtoBuf 解析单例进行初始化,并且要求是线程安全的,原先 java 代码如下,也就是一个 double check 的单例模式。

//double check 的单例模式,这里的 volatile 是为了防止指令重排

如果是用 kotin 则只需要使用 by lazy 懒加载委托属性便能实现类似的效果,kotlin 代码如下:

//by lazy 支持泛型,后面的泛型是 initProtoInstance 的类型

注意:实际上这里使用的并非是是 double check 的模式,而是类似的一种线程安全模式,源码如下:

//by lazy 默认是 SynchronizedLazyImpl 模式

by lazy 的三种模式如下:

  1. LazyThreadSafetyMode.SYNCHRONIZED 类似 double check 线程安全,synchronized 悲观锁

  2. LazyThreadSafetyMode.PUBLICATION 线程安全,使用 cas 锁,多个线程可以同时执行初始化代码块,但是只返回第一个执行完成的数值,作为初始化

  3. LazyThreadSafetyMode.NONE 单线程先可用,等于 java 懒汉式单例

其后,其它两种标准属性,参考官方教程。

3.语法层面支持懒加载-by lazy 和 lateinit

懒加载是经常使用的一个功能,传统的 java 懒加载,可能是在使用的是判断一下对象是否为空,但是在 kotlin 里,提供了语法层次的懒加载,表示该变量在使用之前一定会被初始化。

by-lazy 上面说到了,lateinit 简单用法如下:

var mStirng: 
4.比 java 更强大的类型判断

在 java 中,泛型和反射都是使用频率极高的语法,使用泛型或者接口编程之后,我们经常要使用 instance of 做判断,如下代码:

if (instance 

在kotlin 里面,只需要写如下代码:

//Any 类型,等于 java  Object 类型

在泛型和接口编程使用广泛的情况下,kotlin 的类型安全存在以下优势:

  • is 操作符,支持非操作,比 java 更简洁,在语法层次表面的 非该类型;其次 is 类型判断符,作用域类,类型会智能转换,也就是例子中说的。需要注意是,val 和 var 类型,支持的 is 操作不一样。

  • as 操作符,支持空类型转换,也就是 as?,可以转换为 String? 等可空类型,类似 java 操作是 先要判断该对象是否为该类型,然后强转,语法比较绕,然而 kotlin 直接支持可为空类型的转换

5.functions and lambdas - 函数和 lambdas 表达式

在 kotlin 里面函数是第一公民,java 8 也把加入了该特性,但是远远没有 kotlin 的函数功能强大,kotlin 的函数功能如下:

  • 参数值和变量可以是函数

  • 函数参数支持默认参数

  • 支持命名参数

  • 支持 lambda

  • 支持内联函数

  • 支持强大的扩展函数

  • 编译器支持内联优化

  • 函数可以有一个接收者

函数和 lambdas 表达式对实际开发带来极大的方便,也丰富了编程思想,更多的细节,可以参考官方教程。

6.强大的集合功能

在 kotlin stdlib 里面,支持了强大的集合功能,支持各种高阶函数,主要的高阶函数如下:

  • 分组 sort

  • 映射 map

  • 排序 sort

  • 等等

具体可以参考一个专栏 kotlin 学习之路

https://zhuanlan.zhihu.com/LearningKotlin

或者可以直接在 AS 里面搜索类 _Collection.kt,里面可以看到该类的所有的高阶函数.

下面是一个简单的示例,比如你在 java 中需要对一个后端返回的 List 进行排序,java 中常见的实现方案有

1. Collections 中static > void sort(List list)传入该 List,但是 List 中的元素需要实现 Comarable 接口,并且重写 compareTo()方法

2. Collection中,static void sort(List list, Comparator super T> c) 入该 List 和 一个Comparator。

3. java 中,支持 lambda 表达式之后,你可以这样写

ballList

表示使用 Ball 的 hits 字段进行比较

4. java 8 中可以使用 stream

如果使用 kotlin,这里支持各种各样的sort() 方式,如下 api:

41d1f19d75d707b6a02b92987230ccbf.png

那么上面的例子中,kotlin 中这个排序只要使用 Collections 里面的扩展函数就可以做了。

//使用 hits 字段,做降序处理,Collections.sort() 中默认是升序的

所以其实写到这里,我们已经见识到了,kotlin 的开发者帮助我们考虑了各种语法场景,我们站立在一个伟大的开发团队上,自然而然能更快的写出更高质量的代码。

7.Standard Lib标准库

Kotlin的标准库提供了一系列提高开发效率的函数,例如 apply{} ,run{},let{} 等,这里的解释都比较简单,具体自己看一下 api 就行了,位于 Standard.kt 文件中,使用例子如下:

new EnterRoomConfig.RoomlistEventBean();

你可能不断的为访问 java bean 或者某个对象的属性或者方法,则需要不停的写 对象实例.属性 或者 对象实例.方法。在 kotlin 中,你完全可以释放自己的双手,如下:

"").apply {

apply() 的源码如下:

public 

这里包含,contract 用法和 带接受者的函数,block() 函数的接收者是T,kotlin 这里其实是提供了一种方便的建造者模式,在 java 里面,你实现建造者模式,需要自己手动去编写接口和 Builder 实现,在 kotlin 则使用这些高阶函数,方便你随意的实现 Builder 模式。

8.编写单例更快了

你只需要使用 Object  就可以编写单例了,如下:

object CloUtils {

那么kotlin 上的单例和 java 的各种单例模式,性能上有什么区别吗?kotlin 使用 object 关键字声明的单例,翻译成 java 如下:

public 

也就是 java 常见的类似 饿汉式的单例,这种单例是线程安全的,可能存在一定问题:

1. 在类加载的时候,单例会被初始化,所以不要在初始化代码块,做太多操作

2. 不能直接在构造函数传递参数,其实是 object 不能够有一个 constractor

    26115db0cdd9e9cf8bd1eee9cc5277c0.png

那么实际上我们可以使用其它方式实现我们需要的效果,例如 懒汉式和 double check 等模式,类似 by lazy。

9.kotlin 和 android-android extension插件

实际上,kotlin 是一门全栈的语言,可以写基于 jvm 的,例如 android 和 后端,也可以写前端,但是在 android 上的表现,可能是最亮眼的,如是说:

在某个 module 的 gradle 文件中:

apply plugin: 

然后你就可以使用 android extension 全家桶了,可以帮你做啥呢?

View Binding 取代繁琐的 findViewById()方法

例如某段 java 代码:

//数据域声明各种View
private YYTextView titleTv, roomNameTv, onlineTv, tagTv;
private YYImageView moreIv, lockIv;
private YYLinearLayout titleLayout;
private ViewGroup onlineCountLayout;
private BubblePopupWindow mPopupWindow;

//然后各种 findViewByID
titleTv = findViewById(R.id.tv_title_when_popup);
titleLayout = findViewById(R.id.layout_title_main);
roomNameTv = findViewById(R.id.tv_room_name);
onlineTv = findViewById(R.id.tv_online_count);
tagTv = findViewById(R.id.tv_tag);
moreIv = findViewById(R.id.iv_more);
lockIv = findViewById(R.id.iv_lock);
onlineTv.setTypeface(FontUtils.getTypeFace(FontUtils.FontType.DINMittelschriftAlternate));
roomNameTv.setOnClickListener(this);
tagTv.setOnClickListener(this);
findViewById(R.id.iv_back).setOnClickListener(this);
moreIv.setOnClickListener(this);

在 kotlin 中,你使用 extension 插件,就可以在这样访问控件:

//如果你使用了 extension 插件,这句话会自动 import 进来

原理很简单,就是 extension 插件帮你 findViewById 了类似其它注入框架。

这里有几点注意的,你可以在 Activity 或者 Fragment 或者自定义View 或者 ViewHolder 里面使用这个特性,无需任何其它操作。其次 这个是有Cache 的,每次直接访问id,并不会每次都去 findViewById,只要第一次才会 findViewByID,然后后面会存起来。

使用注解实现 Pracelable

在实际开发中,你可以给一个类实现系列化,但是中途可能增加字段,传统的 java 编写中,或者使用 ide 插件编写,增加字段也是非常辛苦,需要在 writeToParcel() 和  createFromParcel() 方法添加代码,如果使用 extension 插件,只需要开启这个设置:

'com.android.application'

然后在实现 Parcelable 的类上增加一个注解 @Parcelize,代码如下:

@Parcelize

实际上,gradle 插件会为你自动加上  Parcelable 的实现。

还有其它 experimental 的特性等待你去挖掘

参考文档:https://kotlinlang.org/docs/tutorials/android-plugin.html

3kotlin Coroutine -kotlin 协程库

Coroutine 协程,是kotlin 上的一个轻量级的线程库,对比 java 的 Executor,主要有以下特点:

  1. 更轻量级的 api 实现协程

  2. async 和 await 不作为标准库的一部分

  3. suspend 函数,也就是挂起函数是比 java future 和 promise 更安全并且更容易使用

那么实际本质上和线程池有什么区别呢?我的理解是这样的,协程是在用户态对线程进行管理的,不同于线程池,协程进一步管理了不同协程切换的上下文,协程间的通信,协程挂起,对于线程挂起,粒度更小,而且一般不会直接占用到CPU 资源,所以在编程发展的过程中,广义上可以认为 多进程->多线程->协程。

协程并不会映射成内核线程或者其他这么重的资源,它的调度在用户态就可以搞定,任务之间的调度并非抢占式,而是协作式的。

协程库的强大,不言而喻,这里是我之前写过的关于协程库的分享,文档链接:

https://www.jianshu.com/p/c531eefef4c6

4Kotlin -anko 在代码中使用 dsl 编写布局

anko 库,你可以在在代码中编写布局,如下:

//这里是一个垂直布局的LinearLayout 

同样的 anko 这边有个插件,可以支持实时预览-你需要在编写代码之后,build 一下,然后就可以预览布局。

由于anko 存在预览问题,并且层级嵌套的时候,不好优化,所以暂时不建议使用 anko 去编写布局.

5如何理解 kotlin 语法糖

对于 java 的程序员,kotlin 开发者提供了一种方式,帮助开发者去理解 kotlin,也就是可以把 kotlin 编译出来的字节码,反翻译成 java 代码,具体操作 tools-show kotlin bytecod-然后在右侧会看到 kotlin 文件编译之后的字节码文件-然后点击Decompile ,接着就可以看到 kotlin "翻译出"java 的文件,之前讲述 编写单例模块里面,就是一个例子。

旧的 java 代码怎么办

一段时间之后,大家写 kotlin 都写得很爽了,那么总是有一个旧的代码是用 java 写的,那怎么办?如果你想把 java 改成 kotlin,也很简单:

选中你的 java 文件,点击 AS 导航栏 code-covert Java File To Kotlin File,这样就可以转过去了,然后进行简单的修改,也就是把一些报错和 waring 去掉(一般不会有报错)。转换之后的文件可以完美的运行的,这个你不用担心。

6d90ddb714bf0c6e7457fc604c39d17c.png

6Kotlin 的坑

当然 在使用 kotlin 过程中,会发现一些问题或者成本。

成本问题

对个人而言:

  • 你需要花费一天时间学习 kotlin 语法

  • 你需要花费一两周的时间学习 kotlin 对应的 api

  • 使用高阶函数或者 Coroutine 库,等于新使用的一个框架的成本

第一周使用 kotlin 在你的工作中,你会觉得效率下降,而且有点不知所措,甚至有点抵抗 kotlin,但是在两三个星期之后,你一定是这样的:

00d463dfa51f2af69b716ec138952522.png

对于团队而言

  • 包大小变大了

  • 构建时间会增加一点点

针对问题二,kotlin 的开发者正在优化,同时我们可以在构建的时候,监听构建task 花费的时间,让数据去评测,是否值得引入 kotlin。

选取了某个项目中的 module ,该 module 的基本为 kotlin 代码编写,使用 kotlin 构建时间增加了:

:app:kaptGener…

如果是 java 代码的话,上述的时间可能是一半(猜测是这样的),但是从实际数据看,该 module 有几百个 kotlin 文件,编译所花费的时间,也不过是增加了十几秒,所以并不是特别大的问题。

7java 和 kotlin 互调
1.java null 问题

习惯写 java 的同学,一般是不会写这样的声明的,去声明一个参数是可为空的:

fun printSome(content:String?){

一般你是这样声明的:

fun printSome1(content:String){

在 kotlin 里面,是没问题的,即便是你传入 null,编译器也会提示你,不可以传入null,但是一旦在 java 去调用这个 printSome1() 方法,一旦你传入了 null,如下:

//java 调用 kotlin 

依旧是ok的,但是一旦运行的时候,就会报错:


2019-05-05 11:09:05.705 11674-11674/com.yy.yylite.kotlinshare E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.yy.yylite.kotlinshare, PID: 11674
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.yy.yylite.kotlinshare/com.yy.yylite.kotlinshare.bitmapcompress.BitmapAcitivty}: java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter content
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2740)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2801)
        at android.app.ActivityThread.-wrap12(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1548)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:163)
        at android.app.ActivityThread.main(ActivityThread.java:6368)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:901)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:791)
     Caused by: java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkParameterIsNotNull, parameter content
        at com.yy.yylite.kotlinshare.collectionutils.CloUtils.printSome1(CloUtils.kt)
        at com.yy.yylite.kotlinshare.flow.ff.testJavaCallKotlin(ff.java:31)
        at com.yy.yylite.kotlinshare.bitmapcompress.BitmapAcitivty.onCreate(BitmapAcitivty.kt:29)
        at android.app.Activity.performCreate(Activity.java:6861)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2693)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2801) 
        at android.app.ActivityThread.-wrap12(ActivityThread.java) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1548) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:163) 
        at android.app.ActivityThread.main(ActivityThread.java:6368) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:901) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:791) 

具体的原因在于,编译器层会帮我们加这样一行代码:

Intrinsics.checkParameterIsNotNull(content, 

如果传入的参数为 null,则会直接抛出一个异常。

同样的,在 kotlin 里面,如果执行这样的代码:

var nonNullStr:
2.java 方法 Exception 问题

在之前某次迭代中,A  同学写了这样一行代码:

.....
val gson = Gson()
fastSpeech!!.words = gson.fromJson(words, object : TypeTokenString>>() {
}.type)
 hotWordPopupWindow?.setData(fastSpeech!!.words, fastSpeech!!.classId)
....

作为一个老道的程序员都知道,使用 GSON 的方法,没有 try catch 是不安全的,何况 fromJson 方法签名就有 throw 异常的,如下:

public 

然而你使用 java 的时候,编译器会提示你需要 catch 异常,但是在 kotlin 里面,确实不会有的。

原因是因为 kotlin 开发者,认为所有的 Exception 都是 unchecked 的,编译器不会提示你 catch 住,但是在过去的 java 开发里面,effect java 认为,处理 checked Exception 是优雅合理的;

原文如下:

Checked Exceptions
In Kotlin, all exceptions are unchecked, meaning that the compiler does not force you to catch any of them. So, when you call a Java method that declares a checked exception, Kotlin does not force you to do anything。

在过去的实践里发现,在处理 json,网络,IO  的时候,需要开发者关注,是否需要 try catch,当然了,这可能也是一个好处,毕竟写代码的人关心的细节更多了。

关于 java 和 kotlin 互调参考 

https://kotlinlang.org/docs/reference/java-interop.html

8如何推动kotlin普及

需要团队成员去学习官网教程 或者 google 提供的快速学习的教程,当然这里只是快速浏览就行了,主要还是得靠实践,先熟悉基本的语法,然后找时间对语法糖进行了解,最后在实践开发中使用高阶特性。

官网教程-中文

https://www.kotlincn.net/docs/reference/

官网教程-英文

https://kotlinlang.org/docs/reference/basic-types.html

快速入门教程

几小时上手 kotlin

然后可以在一段时间内需要通过 review 代码,提高大家对 kotlin 的认识和用法,大概持续一个开发周期,之后基本可以很顺畅的写代码了。

最后就是规则~怎么让 java 和 kotlin  共存

  • 新文件,尽量用 kotlin 写

  • 旧的 java 文件,可以先用 java 写,有时间再把 java 转成 kotlin

最后就是 IDE 对 kotlin 的友好支持,就是你在 commit 的时候,会对代码进行一个 增量的 code analyse,然后虎会给出你一个更好的建议,其中就包括 kotlin 语法的纠正。如下展示:

e2452bf9543e089895b1cac959b75e6c.png

然后点击 commit 之后,点击review

381fbff4603d84b26e155d71d8816d9e.png

最后 IDE 会给出更 合理的代码提示,一般可以改的尽量改一下,如下,这里提示这一行命名是多余的:

f0fda5293bbf881345a41c1ea641a77c.png


最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!

推荐阅读:

J 神的ButterKnife竟然还隐藏着这样的黑科技?

Android Q 要来了,一个影响国内 90% App 的适配项! ConstraintLayout 2.0新特性 MotionLayout制作炫酷动画

d52d35fe1b4032f4b3a20c4b7985dd5c.png

扫一扫 关注我的公众号

如果你想要跟大家分享你的文章,欢迎投稿~

┏(^0^)┛明天见!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值