什么是回调?
先来看一下维基百科的解释:
在计算机程序设计中,回调函数,或简称回调(Callback 即call then back 被主函数调用运算后会返回主函数),是指通过参数将函数传递到其它代码的,某一块可执行代码的引用。这一设计允许了底层代码调用在高层定义的子程序。
嗯。每个人我都认识,为什么连在一起就不认识了呢…
算了算了,先不管这个定义了,直接从案例出发吧,这样更容易理解一些。
案例
假设现在有这样一个场景,我需要给用户展示一个信息提示板,提示板里面需要填充信息。但是填充的信息需要经过一系列处理才能生成,所以这个处理的时间会很长。如果我直接让程序线性执行下去,那么用户要等很久才会看到这个提示板,并且在等待的过程中程序会卡顿,用户不能执行其他操作。于是,用户就摔了他的键盘(暴躁德国boy)。
有没有什么更好的解决办法呢?还是有的,我可以将面板的创建和数据处理的过程分离开,另开一个线程用于处理数据,这样用户就可以先看到面板界面,同时程序也不会卡顿。用户的键盘终于保住了。
但这样又会出现一个问题,由于信息处理是异步执行的,当信息处理完毕后我要怎么将数据填充到提示面板里呢?我们知道程序都是顺序执行的,如果只是按照顺序执行的逻辑,我们将没办法获取到处理后的数据。这时候,回调给我们提供了一种解决方案。
回调的基本构成
回调一般由三部分组成,分别为回调接口,回调的调用,以及回调的实现。还是从刚才的例子出发,使用回调前,我们必须要定义一个接口,接口如下:
interface CallBack{
fun action(name: String)
}
之后,我们需要定义一个调用类,按照案例的描述,我们将这个类定义为Panel。
class Panel(){
var panelMsg = "null"
lateinit var dataDeal: CallBack
fun createPanel() {
println("create panel, initial val is ${panelMsg}")
}
@JvmName("setDataDeal1")
fun setDataDeal(dataDeal: CallBack){
// 这里表示我要提前调用一个回调,但是并没有对回调做定义
this.dataDeal = dataDeal
val thread = Thread{
this.dataDeal.action("resultStr")
}
thread.start()
}
}
最后,我们需要在程序运行的时候定义这个回调的实现:
fun main(){
val panel = Panel()
panel.createPanel()
panel.setDataDeal(object : CallBack {
override fun action(name: String) {
TimeUnit.SECONDS.sleep(5)
panel.panelMsg = name
println("new panel val is ${panel.panelMsg}")
}
})
}
有了这三部分,一个回调的功能就算是完成了,我再贴一下完整代码:
import java.util.concurrent.TimeUnit
interface CallBack{
fun action(name: String)
}
class Panel(){
var panelMsg = "null"
lateinit var dataDeal: CallBack
fun createPanel() {
println("create panel, initial val is ${panelMsg}")
}
@JvmName("setDataDeal1")
fun setDataDeal(dataDeal: CallBack){
// 这里表示我要提前调用一个回调,但是并没有对回调做定义
this.dataDeal = dataDeal
val thread = Thread{
this.dataDeal.action("resultStr")
}
thread.start()
}
}
fun main(){
val panel = Panel()
panel.createPanel()
panel.setDataDeal(object : CallBack {
override fun action(name: String) {
TimeUnit.SECONDS.sleep(5)
panel.panelMsg = name
println("new panel val is ${panel.panelMsg}")
}
})
}
通过对上述代码的分析,我们不难看出,其实回调的内核就是将函数的使用过程和函数的实现过程分离开来。在代码中,我可以先提前使用对应的方法,帮助我们在逻辑上实现一个功能。而真正需要使用的时候,我们才需要实现回调的内容,这样就做到了异步通信的功能。