No need findViewById
Defined in the layout
<TextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!" />
Directly in the text TextView
tv_content.text = "改变文本"
Lambda
Although Java JDK Lambda expressions have been added, but did not catch on, now with Kotlin is a good choice
tv_content.setOnClickListener(View.OnClickListener(
fun(v : View) {
v.visibility = View.GONE
}
))
It can now be simplified with Lambda expressions
tv_content.setOnClickListener { v -> v.visibility = View.GONE }
Function variables
In Kotlin function syntax can be passed as a variable
var result = fun(number1 : Int, number2 : Int) : Int {
return number1 + number2
}
Using this function variables
println(result(1, 2))
Space Security
In the case of Java without forcing us to handle null object, often cause NullPointerException null pointer appears, for now Kotlin empty object has been limited, must whether the processing object is empty at compile time, or will not be compiled by
In the case where the object is not empty, the object can be used directly
fun getText() : String {
return "text"
}
val text = getText()
print(text.length)
In the case where the object can be empty, and must be determined whether the object is empty
fun getText() : String? {
return null
}
val text = getText()
if (text != null) {
print(text.length)
}
// 如果不想判断是否为空,可以直接这样,如果 text 对象为空,则会报空指针异常,一般情况下不推荐这样使用
val text = getText()
print(text!!.length)
// 还有一种更好的处理方式,如果 text 对象为空则不会报错,但是 text.length 的结果会等于 null
val text = getText()
print(text?.length)
Methods support default parameters added
On Java, we may expand to a method and multiple overloads
public void toast(String text) {
toast(this, text, Toast.LENGTH_SHORT);
}
public void toast(Context context, String text) {
toast(context, text, Toast.LENGTH_SHORT);
}
public void toast(Context context, String text, int time) {
Toast.makeText(context, text, time).show();
}
toast("弹个吐司");
toast(this, "弹个吐司");
toast(this, "弹个吐司", Toast.LENGTH_LONG);
But in Kotlin above, we do not need to be heavy-duty, can be defined directly from the default value of the parameter in the above method
fun toast(context : Context = this, text : String, time : Int = Toast.LENGTH_SHORT) {
Toast.makeText(context, text, time).show()
}
toast(text = "弹个吐司")
toast(this, "弹个吐司")
toast(this, "弹个吐司", Toast.LENGTH_LONG)
Extended class method
The method can extend the original class, for example, to extend the String class method without the use of inheritance
fun String.handle() : String {
return this + "Android轮子哥"
}
// 需要注意,handle 方法在哪个类中被定义,这种扩展只能在那个类里面才能使用
print("HJQ = ".handle())
HJQ = Android轮子哥
Operator overloading
Kotlin used in the final operator corresponding to the object method is also called, so that we can rewrite this object by methods support these operators, this is no longer the demo code
Operators | Call the method |
---|---|
+a | a.unaryPlus() |
-a | a.unaryMinus() |
!a | a.not() |
Operators | Call the method |
---|---|
a++ | a.inc() |
a-- | a.dec() |
Operators | Call the method |
---|---|
a + b | a.plus(b) |
a - b | a.minus(b) |
a * b | a.times(b) |
a / b | a.div(b) |
a % b | a.rem(b), a.mod(b) (deprecated) |
a..b | a.rangeTo (b) |
Operators | Call the method |
---|---|
a in b | b.contains(a) |
a !in b | !b.contains(a) |
Operators | Call the method |
---|---|
a[i] | a.get(i) |
a[i, j] | a.get(i, j) |
a[i_1, ..., i_n] | a.get(i_1, ..., i_n) |
a[i] = b | a.set(i, b) |
a[i, j] = b | a.set(i, j, b) |
a[i_1, ..., i_n] = b | a.set(i_1, ..., i_n, b) |
Operators | Call the method |
---|---|
a() | a.invoke() |
a(i) | a.invoke(i) |
a(i, j) | a.invoke(i, j) |
a(i_1, ..., i_n) | a.invoke(i_1, ..., i_n) |
Operators | Call the method |
---|---|
a += b | a.plusAssign(b) |
a -= b | a.minusAssign(b) |
a *= b | a.timesAssign(b) |
a /= b | a.divAssign(b) |
a %= b | a.remAssign(b), a.modAssign(b) (deprecated) |
Operators | Call the method |
---|---|
a == b | a?.equals(b) ?: (b === null) |
a != b | !(a?.equals(b) ?: (b === null)) |
Operators | Call the method |
---|---|
a > b | a.compareTo(b) > 0 |
a < b | a.compareTo(b) < 0 |
a >= b | a.compareTo(b) >= 0 |
a <= b | a.compareTo(b) <= 0 |
Spread Function
Kotlin spread function is generated in a simplified writing some code, which let, with, run, apply, also five functions
- let function
In the function block it may refer to the object by it. The return value of the function block designated row or the last return expression
General wording
fun main() {
val text = "Android轮子哥"
println(text.length)
val result = 1000
println(result)
}
let wording
fun main() {
val result = "Android轮子哥".let {
println(it.length)
1000
}
println(result)
}
The most common scenario is to use the let function processing required for a null object do unified sentenced empty processing
mVideoPlayer?.setVideoView(activity.course_video_view)
mVideoPlayer?.setControllerView(activity.course_video_controller_view)
mVideoPlayer?.setCurtainView(activity.course_video_curtain_view)
mVideoPlayer?.let {
it.setVideoView(activity.course_video_view)
it.setControllerView(activity.course_video_controller_view)
it.setCurtainView(activity.course_video_curtain_view)
}
Or is the need to define a variable which can be used within a certain scope range
- with function
Some functions use the foregoing embodiment is slightly different, because it is not present in the extended form. It is an object as a parameter in the function block may refer to the object by this. The return value of the function block designated row or the last return expression
The definition of the Person class
class Person(var name : String, var age : Int)
General wording
fun main() {
var person = Person("Android轮子哥", 100)
println(person.name + person.age)
var result = 1000
println(result)
}
with writing
fun main() {
var result = with(Person("Android轮子哥", 100)) {
println(name + age)
1000
}
println(result)
}
适用于调用同一个类的多个方法时,可以省去类名重复,直接调用类的方法即可,经常用于Android中RecyclerView中onBinderViewHolder中,数据model的属性映射到UI上
override fun onBindViewHolder(holder: ViewHolder, position: Int){
val item = getItem(position)?: return
holder.nameView.text = "姓名:${item.name}"
holder.ageView.text = "年龄:${item.age}"
}
override fun onBindViewHolder(holder: ViewHolder, position: Int){
val item = getItem(position)?: return
with(item){
holder.nameView.text = "姓名:$name"
holder.ageView.text = "年龄:$age"
}
}
- run 函数
实际上可以说是let和with两个函数的结合体,run函数只接收一个lambda函数为参数,以闭包形式返回,返回值为最后一行的值或者指定的return的表达式
一般写法
var person = Person("Android轮子哥", 100)
println(person.name + "+" + person.age)
var result = 1000
println(result)
run 写法
var person = Person("Android轮子哥", 100)
var result = person.run {
println("$name + $age")
1000
}
println(result)
适用于let,with函数任何场景。因为run函数是let,with两个函数结合体,准确来说它弥补了let函数在函数体内必须使用it参数替代对象,在run函数中可以像with函数一样可以省略,直接访问实例的公有属性和方法,另一方面它弥补了with函数传入对象判空问题,在run函数中可以像let函数一样做判空处理,这里还是借助 onBindViewHolder 案例进行简化
override fun onBindViewHolder(holder: ViewHolder, position: Int){
val item = getItem(position)?: return
holder.nameView.text = "姓名:${item.name}"
holder.ageView.text = "年龄:${item.age}"
}
override fun onBindViewHolder(holder: ViewHolder, position: Int){
val item = getItem(position)?: return
item?.run {
holder.nameView.text = "姓名:$name"
holder.ageView.text = "年龄:$age"
}
}
- apply 函数
从结构上来看apply函数和run函数很像,唯一不同点就是它们各自返回的值不一样,run函数是以闭包形式返回最后一行代码的值,而apply函数的返回的是传入对象的本身
一般写法
val person = Person("Android轮子哥", 100)
person.name = "HJQ"
person.age = 50
apply 写法
val person = Person("Android轮子哥", 100).apply {
name = "HJQ"
age = 50
}
整体作用功能和run函数很像,唯一不同点就是它返回的值是对象本身,而run函数是一个闭包形式返回,返回的是最后一行的值。正是基于这一点差异它的适用场景稍微与run函数有点不一样。apply一般用于一个对象实例初始化的时候,需要对对象中的属性进行赋值。或者动态inflate出一个XML的View的时候需要给View绑定数据也会用到,这种情景非常常见。特别是在我们开发中会有一些数据model向View model转化实例化的过程中需要用到
mRootView = View.inflate(activity, R.layout.example_view, null)
mRootView.tv_cancel.paint.isFakeBoldText = true
mRootView.tv_confirm.paint.isFakeBoldText = true
mRootView.seek_bar.max = 10
mRootView.seek_bar.progress = 0
使用 apply 函数后的代码是这样的
mRootView = View.inflate(activity, R.layout.example_view, null).apply {
tv_cancel.paint.isFakeBoldText = true
tv_confirm.paint.isFakeBoldText = true
seek_bar.max = 10
seek_bar.progress = 0
}
多层级判空问题
if (mSectionMetaData == null || mSectionMetaData.questionnaire == null || mSectionMetaData.section == null) {
return;
}
if (mSectionMetaData.questionnaire.userProject != null) {
renderAnalysis();
return;
}
if (mSectionMetaData.section != null && !mSectionMetaData.section.sectionArticles.isEmpty()) {
fetchQuestionData();
return;
}
kotlin的apply函数优化
mSectionMetaData?.apply {
//mSectionMetaData不为空的时候操作mSectionMetaData
}?.questionnaire?.apply {
//questionnaire不为空的时候操作questionnaire
}?.section?.apply {
//section不为空的时候操作section
}?.sectionArticle?.apply {
//sectionArticle不为空的时候操作sectionArticle
}
- also 函数
also函数的结构实际上和let很像唯一的区别就是返回值的不一样,let是以闭包的形式返回,返回函数体内最后一行的值,如果最后一行为空就返回一个Unit类型的默认值。而also函数返回的则是传入对象的本身
fun main() {
val result = "Android轮子哥".let {
println(it.length)
1000
}
println(result) // 打印:1000
}
fun main() {
val result = "Android轮子哥".also {
println(it.length)
}
println(result) // 打印:Android轮子哥
}
适用于let函数的任何场景,also函数和let很像,只是唯一的不同点就是let函数最后的返回值是最后一行的返回值而also函数的返回值是返回当前的这个对象。一般可用于多个扩展函数链式调用
- 总结
通过以上几种函数的介绍,可以很方便优化kotlin中代码编写,整体看起来几个函数的作用很相似,但是各自又存在着不同。使用的场景有相同的地方比如run函数就是let和with的结合体
协程
子任务协作运行,优雅的处理异步问题解决方案
协程实际上就是极大程度的复用线程,通过让线程满载运行,达到最大程度的利用CPU,进而提升应用性能
在当前 app module 中配置环境和依赖(因为现在协程在 Kotlin 中是实验性的)
kotlin {
experimental {
coroutines 'enable'
}
}
dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.20'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:0.20'
}
协程的三种启动方式
runBlocking:T
launch:Job
async/await:Deferred
- runBlocking
runBlocking 的中文翻译:运行阻塞。说太多没用,直接用代码测试一下
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
runBlocking {
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试延迟开始")
delay(20000) // 因为 Activity 最长响应时间为 15 秒
println("测试延迟结束")
}
println("测试结束")
17:02:08.686 System.out: 测试是否为主线程true
17:02:08.686 System.out: 测试开始
17:02:08.688 System.out: 测试是否为主线程true
17:02:08.688 System.out: 测试延迟开始
17:02:28.692 System.out: 测试延迟结束
17:02:28.693 System.out: 测试结束
runBlocking 运行在主线程,过程中 App 出现过无响应提示,由此可见 runBlocking 和它的名称一样,真的会阻塞当前的线程,只有等 runBlocking 里面的代码执行完了才会执行 runBlocking 外面的代码
- launch
launch 的中文翻译:启动。甭管这是啥,直接用代码测试
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
launch {
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试延迟开始")
delay(20000)
println("测试延迟结束")
}
println("测试结束")
17:19:17.190 System.out: 测试是否为主线程true
17:19:17.190 System.out: 测试开始
17:19:17.202 System.out: 测试结束
17:19:17.203 System.out: 测试是否为主线程false
17:19:17.203 System.out: 测试延迟开始
17:19:37.223 System.out: 测试延迟结束
- async
async 的中文翻译:异步。还是老套路,直接上代码
测试的时候是主线程,但是到了 launch 中就会变成子线程,这种效果类似 new Thread(),有木有?和 runBlocking 最不同的是 launch 没有执行顺序这个概念
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
async {
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试延迟开始")
delay(20000)
println("测试延迟结束")
}
println("测试结束")
17:29:00.694 System.out: 测试是否为主线程true
17:29:00.694 System.out: 测试开始
17:29:00.697 System.out: 测试结束
17:29:00.697 System.out: 测试是否为主线程false
17:29:00.697 System.out: 测试延迟开始
17:29:20.707 System.out: 测试延迟结束
这结果不是跟 launch 一样么?那么这两个到底有什么区别呢?,让我们先看一段测试代码
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
val async = async {
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试延迟开始")
delay(20000)
println("测试延迟结束")
return@async "666666"
}
println("测试结束")
runBlocking {
println("测试返回值:" + async.await())
}
17:50:57.117 System.out: 测试是否为主线程true
17:50:57.117 System.out: 测试开始
17:50:57.120 System.out: 测试结束
17:50:57.120 System.out: 测试是否为主线程false
17:50:57.120 System.out: 测试延迟开始
17:51:17.131 System.out: 测试延迟结束
17:51:17.133 System.out: 测试返回值:666666
看到这里你是否懂了,async 和 launch 还是有区别的,async 可以有返回值,通过它的 await 方法进行获取,需要注意的是这个方法只能在协程的操作符中才能调用
- 线程调度
啥?协程有类似 RxJava 线程调度?先用 launch 试验一下
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
launch(CommonPool) { // 同学们,敲重点
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试延迟开始")
delay(20000)
println("测试延迟结束")
}
println("测试结束")
18:00:23.243 System.out: 测试是否为主线程true
18:00:23.244 System.out: 测试开始
18:00:23.246 System.out: 测试结束
18:00:23.246 System.out: 测试是否为主线程false
18:00:23.247 System.out: 测试延迟开始
18:00:43.256 System.out: 测试延迟结束
你:你怕不是在逗我?这个跟刚刚的代码有什么不一样吗?
我:当然不一样,假如一个网络请求框架维护了一个线程池,一个图片加载框架也维护了一个线程池.......,你会发现其实这样不好的地方在于,这些线程池里面的线程没有被重复利用,于是乎协程主动维护了一个公共的线程池 CommonPool,很好的解决了这个问题
你:你说得很有道理,还有刚刚不是说能线程调度吗?为什么还是在子线程运行?
我:因为我刚刚只用了 CommonPool 这个关键字,我再介绍另一个关键字 UI,光听名字就知道是啥了
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试开始")
launch(UI) {
println("测试是否为主线程" + (Thread.currentThread() == Looper.getMainLooper().thread))
println("测试延迟开始")
delay(20000)
println("测试延迟结束")
}
println("测试结束")
18:07:20.181 System.out: 测试是否为主线程true
18:07:20.181 System.out: 测试开始
18:07:20.186 System.out: 测试结束
18:07:20.192 System.out: 测试是否为主线程true
18:07:20.192 System.out: 测试延迟开始
18:07:40.214 System.out: 测试延迟结束
转载自:https://www.jianshu.com/p/884ca0a49e5e