巧妙理解接口回调

article/2025/10/8 13:00:41

接口回调目的和用法解析

一、为什么会有接口回调?什么是接口回调?

其实这两个问题是一个问题,知道了接口回调的原理自然就知道了为什么会有这么个东西。我们知道java中接口是不可以直接创建实例的,那么问题来了,假如我把一个接口声明为一个变量,那么我执行这个接口中的方法,接口没有实例它该怎么办呢?啊哈,这里自然又改出现java中的另一个特性—“多态”,这时java虚拟机自然会去找其子类,调用其子类中已经重载的该方法,这里就是接口回调的本质!!我们只需要给该变量指向其子类的地址就可以在调用的时候知道调用子类的方法。那么我们就可以在A类中创建接口的子类实例,在B类中创建一个接口的变量,把A类的地址传给B类的变量,在变量执行接口中的方法的时候就会调用A类中重写的方法,这就是接口回调的执行步骤。我们在网络请求等耗时的操作的时候会使用到该机制,用来把得到的数据传回主线程中。

二、接口回调(Java代码)

一、回调的含义和用途

1. 什么是回调

一般来说,模块之间都存在一定的调用关系,从调用方式上来看,可分为三类:

  1. 同步调用:同步调用是一种阻塞式调用,即在函数A的函数体里通过书写函数B的函数名来调用之,使内存中对应函数B的代码得以执行。

  2. 异步调用:异步调用是一种类似消息或事件的机制解决了同步阻塞的问题,例如A通知B后,他们各走各的路,互不影响,不用像同步调用那样,A通知B后,非得等到B走完后,A才继续走。

  3. 回调:回调是一种双向的调用模式,也就是说,被调用的接口被调用时也会调用对方的接口,例如A要调用B,B在执行完又要调用A。
    在这里插入图片描述

2.回调的用途

回调一般用于层间协作,上层将本层函数安装在下层,这个函数就是回调,而下层在一定条件下触发回调。例如作为一个驱动,是一个底层,他在收到一个数据时,除了完成本层的处理工作外,还将进行回调,将这个数据交给上层应用层来做进一步处理,这在分层的数据通信中很普遍。

二、为什么会存在回调机制

举例1:

有一位老板(上层模块)很忙,他没有时间盯着员工(下层模块)干活,然后他告诉自己的雇员,干完当前这些事情后,告诉他干活的结果。这个例子其实是一个回调+异步的例子,再举一个例子,A程序员写了一段程序a,其中预留了回调函数接口,并封装好了该程序,程序员B让a调用自己的程序b中的一个方法,于是,他通过a中的接口回调自己b中的方法。下面把上面的例子变成代码

1.首先创建一个回调接口,让老板得告知干完活如何找到他的方式:留下老板办公室地址:

/*** 此接口为联系的方式,不论是电话号码还是联系地址,作为* 老板都必须要实现此接口*/// 创建一个回调接口
public interface CallBackInterface {public void execute();
}  

2.创建回调对象,就是老板本人,因为员工干完活后要给他打电话,因此老板必须实现回调接口,不然员工去哪里找老板?

/*** 老板是作为上层应用身份出现的,下层应用(员工)是不知道* 有哪些方法,因此他想被下层应用(员工)调用必须实现此接口*/
// 创建回调接口的实现类
public class Boss implements CallBackInterface {@Overridepublic void execute() {System.out.println("收到了!!" + System.currentTimeMillis());}
}

3.创建控制类,也就是员工对象,他必须持有老板的地址(回调接口),即使老板换了一茬又一茬,办公室不变,总能找到对应的老板。

/*** 员工类,必须要记住,这是一个底层类,底层是不了解上层服务的*/
// 创建控制类
public class Employee {// 引用回调对象private CallBackInterface callBack = null;// 在这里我们声明了一个接口变量,在类的初始化方法中把接口的子类的地址赋给该变量// 完成之后调用接口变量的方法把数据传给该方法,执行该方法实际是执行子类的该方法,这就是接口回调真正做的事。// 暴露设置接口的方法// 告诉老板的联系方式,也就是注册public void setCallBack(CallBackInterface callBack) {this.callBack = callBack;}//工人干活public void doSome() {// 1.开始干活了for (int i = 0; i < 10; i++) {System.out.println("第【" + i + "】事情干完了!");}// 2.告诉老板干完了// 完成之后调用接口变量的方法把数据传给该方法,执行该方法实际是执行子类的该方法,这就是接口回调真正做的事。callBack.execute();}
}

4.测试类代码:

// 然而Java中没有指针,不能传递方法的地址,一般采用接口回调实现:把实现某一接口的类创建的对象的引用赋给该接口声明的接口变量,那么该接口变量就可以调用被类实现的接口的方法。
public class Client {public static void main(String[] args) {Employee emp = new Employee();//将回调对象(上层对象)传入,注册emp.setCallBack(new Boss());//开启控制器对象运行emp.doSome();}}

举例2:

对于回调函数的理解可以参照C或C++中对回调函数的定义:

程序在调用一个函数时,将自己的函数的地址作为参数传递给程序调用的函数时(那么这个自己的函数称回调函数)
在这里插入图片描述

然而Java中没有指针,不能传递方法的地址,一般采用接口回调实现:把实现某一接口的类创建的对象的引用赋给该接口声明的接口变量,那么该接口变量就可以调用被类实现的接口的方法。

例如:

一读者想借《软件技术学习与实践》这本书,但这本书已被其他读者借走了。于是,读者与图书馆管理员间发生了以下对话:

读者:“我把我的电话号码告诉你,等书一到就马上通知我。”

管理员:“好的。另一读者把书还回来后,马上给您打电话,书我先帮您留着。”

在上述这个场景中,读者就是“回调对象”,管理员就是“控制器对象”,读者的电话号码就是“回调对象的方法”。

在控制器类中引用了回调对象,因此就能调用回调方法,当控制器进行某些判断之后(如:监听鼠标单击操作)就会自动调用回调方法!简易流程图如下:
在这里插入图片描述

总结:

  • 在三层中,当业务层调用数据层时,是不需要把业务层自身传递到数据层的,并且这是一种上层调用下层的关系,比如我们在用框架的时候,一般直接调用框架提供的API就可以了,但回调不同,当框架不能满足需求,我们想让框架来调用自己的类方法,怎么做呢?总不至于去修改框架吧。许多优秀的框架提几乎都供了相关的接口,我们只需要实现相关接口,即可完成了注册,然后在合适的时候让框架来调用我们自己的类。

  • 最后,再举一例,为了使我们写的函数接近完美,就把一部分功能外包给别人,让别人个性化定制,至于别人怎么实现不管,我唯一要做的就是定义好相关接口,这一设计允许了底层代码调用高层定义的子程序,增强程序灵活性,和反射有着异曲同工之妙,这才是回调的真正原因!

  • 用一段话来总结下回调:上层模块封装时,很难预料下层模块会如何实现,因此,上层模块只需定义好自己需要但不能预料的接口(也就是回调接口),当下层模块调用上层模块时,根据当前需要的实现回调接口,并通过注册或参数方式传入上层模块即可,这样就实现下层调用上层,并且上层还能根据传入的引用来调用下层的具体实现,将程序的灵活性大大的增加了。

二、接口回调基础理解(Android)

  • 接口回调听起来好像很厉害的样子,但其实只要能够搞清楚代码的执行过程,多看几遍,并且认真思考,再加上勤奋的练习,熟练掌握简单的接口回调并不是难题,接下来,我会用一个简单的例子,来带大家一起分析代码的执行过程,以及这样写的好处,以及这样写的思路。

  • 我们知道recyclerView是没有自带点击事件的,所以这里我们就拿给recyclerView添加点击事件为例。
    在这里插入图片描述
    ok 我们做好了一个简单的recycerView,但是此时点击他的条目是无效的,接下来我们为他添加自己定义的条目点击事件。

在适配器中加入:

 //自定义公开接口,定义抽象方法public interface onItemClickListener{void onItemClick(int position,View itemView);}//创建接口类型变量(接受传值并设置事件)private onItemClickListener mOnItemClickListener;//暴露设置接口的方法public void setOnItemClickListener(onItemClickListener mOnItemClickListener){this.mOnItemClickListener = mOnItemClickListener;}

在适配器的onBindViewHolder方法中开始监听事件:

 @Overridepublic void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {final MyViewHolder viewHolder = (MyViewHolder) holder;viewHolder.textView.setText(datas.get(position));if (position % 2 == 0){viewHolder.textView.setTextColor(Color.BLUE);}else {viewHolder.textView.setTextColor(Color.RED);}if (mOnItemClickListener != null){viewHolder.itemView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {int pos = holder.getLayoutPosition();mOnItemClickListener.onItemClick(pos,holder.itemView);}});}}

最后一步就是在MainActivity中,实现onItemClickListener接口,设置监听,并实现抽象方法:

public class MainActivity extends AppCompatActivity implements MyAdapter.onItemClickListener
adapter.setOnItemClickListener(this);
  @Overridepublic void onItemClick(int position, View itemView) {Toast.makeText(MainActivity.this, "条目位置 :" + ( position + 1 ), Toast.LENGTH_SHORT).show();}

OK,点击事件添加成功
在这里插入图片描述
很简单,很快,但是这个自己添加的点击事件,其中代码的执行过程大家清楚么?

按照代码的执行顺序,我们添加的代码第一句执行的就是:

adapter.setOnItemClickListener(this);

这是调用了适配器设置监听的方法,this代表当前类,我们这里指的就是MainActivity,而这个方法定义的参数要求是一个我们自己定义的接口,所以我们要通过MainActivity去实现OnItemClickListener接口,那么此时我们的MainActivity就也可以称作是OnItemClickListener类型。

将MainActivity传入后,就是将我们传入的值赋值给了自己定义的接口类型变量

//暴露设置接口的方法public void setOnItemClickListener(onItemClickListener mOnItemClickListener){this.mOnItemClickListener = mOnItemClickListener;}

然后我们再来看一看这个自己定义的变量去做了什么:

if (mOnItemClickListener != null){viewHolder.itemView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {int pos = holder.getLayoutPosition();mOnItemClickListener.onItemClick(pos,holder.itemView);}});}

这是添加在onBindViewHolder中的,在调用了setOnItemClickListener方法后,mOnItemClickListener就不为空,然后当viewholder的条目点击之后,我们的mOnItemClickListener就调用了自己的onItemClick方法。

开始思考:我们这里的mOnItemClickListener不为空,那么他的值是哪里来的呢?是谁呢? 没有错,就是我们的MainActivity,那么这里调用了onItemClick方法,实际上调用的就是MainActivity中实现的抽象方法onItemClick

 @Overridepublic void onItemClick(int position, View itemView) {Toast.makeText(MainActivity.this, "条目位置 :" + ( position + 1 ), Toast.LENGTH_SHORT).show();}

至此,我们得以解惑,只要在MainActivity中,实现一个接口,就实现了RecyclerView的点击事件,也了解了这个代码的执行过程,不过有人可能会疑惑,点击事件我直接写在适配器中不好吗,这样MainActivity中的代码不是会更少,更简洁吗?
在这里插入图片描述
好,根据质疑,我将代码做了如上修改,确实,点击事件依然好用,但是接口回调就没有作用吗?那还要他干嘛。好了,我要提出需求了。

如果我们的需求是点击了一条目之后,修改MainActivity中一个控件(例如Button)的背景颜色,想一想,此时怎么操作?

可能还真有头铁的愣头青会说:可以在MainActivity中通过适配器的构造方法,把控件(例如Button)对象传到Adapter里面,很好,很有思想。但是我这里首先不说你违背了通俗的编程习惯(我没怎么见过把一个控件对象传来传去的),如果我要有100个控件要改变呢?

显然 100个夸张了一点,但是基本的设计模式,我们还是要遵循的,所以接口回调的好处,大家体会到了吗?

参考:

https://blog.csdn.net/Adonis044/article/details/80183335

https://blog.csdn.net/qq_39085422/article/details/78453788?depth_1-utm_source=distribute.pc_relevant_right.none-task-blog-BlogCommendFromMachineLearnPai2-1&utm_source=distribute.pc_relevant_right.none-task-blog-BlogCommendFromMachineLearnPai2-1


http://chatgpt.dhexx.cn/article/712uy150.shtml

相关文章

[Java]什么是接口回调?

什么是接口回调&#xff1f; 1&#xff0e;接口回调是什么[2]&#xff1f; 接口回调是指&#xff1a;可以把使用某一接口的类创建的对象的引用赋给该接口声明的接口变量&#xff0c;那么该接口变量就可以调用被类实现的接口的方法。实际上&#xff0c;当接口变量调用被类实现…

自定义动画animate

开发工具与关键技术&#xff1a;VS&#xff0c;MVC 作者&#xff1a;陈梅 撰写时间&#xff1a;2019年6月2 日 所有代码来源与老师教学 这次分享一个好玩的自定义动画效果&#xff0c;这次还是用jQuery做出来的小功能。这次我们先直接看最后已经布局好的效果。 把所想写的内容…

Android自定义动画专题一

Android自定义动画 在目前的移动端产品中&#xff0c;不管是app还是网页一个好看酷炫的页面总是会第一时间吸引人的眼球&#xff0c;那么对于android开发人员来说&#xff0c;要想实现一个好看的页面必然需要掌握自定义控件以及自定义动画这门技术。 1.Android原生动画 Androi…

百度地图添加自定义图标标注以及自定义动画效果

百度地图添加自定义图标标注以及自定义动画效果 1、添加自定义图标标注2、添加自定义动画效果2.1、标注对象marker的构成2.2、自定义动画效果实现过程2.3、最终实现效果 上次写的是添加自定义图标&#xff0c;但是用的是添加自定义覆盖物方法&#xff0c;结果不支持点聚合&…

Vue中如何进行自定义动画与动画效果设计

Vue中如何进行自定义动画与动画效果设计 在Vue中&#xff0c;动画效果是非常有用的&#xff0c;它可以使用户界面变得更加生动、有趣&#xff0c;从而提高用户体验。Vue提供了一套非常方便的动画系统&#xff0c;使得我们可以非常容易地实现动画效果。 在本文中&#xff0c;我…

Android 自定义动画(实现类似分享动画)

最近在开发app中,要实现点击进入分享动画页面,然后照着每个Item的功能,来实现各自的功能 效果图如下: 首选自定义动画Activity import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import andr…

UE4 创建自定义动画节点

创建自定义动画节点需要两个类&#xff1a; 一个是您在编辑器中看到的图表节点 一个是真正在运行时工作的行为节点 动画图表节点&#xff0c;派生自&#xff1a;UAnimGraphNode_Base 例如&#xff1a;class UAnimGraphNode_SequencePlayer : public UAnimGraphNode_Base 动画…

ViewPager2添加自定义动画

此篇为ViewPager2的拓展篇&#xff0c;具体可查看ViewPager2的使用 ViewPager2自定义动画的核心是使用PageTransformer来实现&#xff0c;他是ViewPager2中的一个接口 原理 要显示非默认屏幕滑动动画&#xff0c;请实现 ViewPager2.PageTransformer 接口并将其提供给 ViewPa…

jQuery(五)--自定义动画、动画

目录 一、自定义动画 1.1 animate() 1.2 animate()动画执行顺序 1.3 animate()动画回调函数和匀速运动 1.4 animate动画之stop()/delay() 二、动画 2.1 show()/hide()/toggle() 2.2 slideDown()/slideUp()/slideToggle() 2.3 fadeIn()/fadeOut()/fadeTo()/fadeToggle(…

Android ProgressBar 自定义动画

源码地址&#xff1a; https://download.csdn.net/download/dreams_deng/12236355 1. 自定义圆形动画 1.1 布局 <!-- indeterminateDrawable 旋转图片indeterminateDuration 旋转速度--><ProgressBarandroid:id"id/pb_welcome_loading"android:layout_…

Qt自定义动画移动路径

在使用Qt实现动画时&#xff0c;一般使用QPropertyAnimation来实现&#xff0c;一般我们实现控件的移动动画都是走直线&#xff0c;我们如何实现自己想要的移动路径呢&#xff0c;比如走圆弧。下面介绍通过QPropertyAnimation实现自定义动画移动路径&#xff0c;比如走圆曲线、…

Flutter开发之——动画-自定义动画

一 为什么要进行自定义动画 当系统提供的动画不满足业务需求时&#xff0c;就需要我们自己进行自定义动画通过自定义动画&#xff0c;可以提高自定义组件的能力 二 自定义动画过程 继承StatefulWidget &#xff0c;完成动画界面的绘制setState 中动画执行及状态监听并刷新UId…

JQuery自定义动画——animate()学习

JQuery自定义动画——animate()学习 可以使 用animate 方法创建和实现自定义动画&#xff0c;animate() 方法通过执行 CSS 属性集的自定义动画&#xff0c;满足更多复杂多变的要求。 该方法通过CSS样式将元素从一个状态改变为另一个状态。即通过CSS属性值逐渐改变的&#xff0…

搞定动画之 JQuery 中的自定义动画

古之立大事者&#xff0c;不惟有超世之才&#xff0c;亦必有坚忍不拔之志——苏轼 写在前面 所谓的自定义动画就是通过 jQuery 提供的方法来完成我们自己想要的动画效果 animate()方法 jQuery 提供了 animate() 方法完成自定义动画效果&#xff0c;该方法具有两种用法。 用…

animate.css 自定义动画

在使用animate.css 的时候 动画的高度超过了预期的高度 在这种情况下肯定是过不了测试的&#xff0c;怎么办的&#xff1f; <div class"fold-div animated " :class"{slideInDown:foldDivHeightShow}" </div>上边代码可以知道 用的是 slideInDown…

JQuery中的animate自定义动画

animate()方法用于创建自定义动画。 概述 .animate( properties [, duration ] [, easing ] [, complete ] ) properties 类型: PlainObject 一个CSS属性和值的对象,动画将根据这组对象移动。 duration (默认: 400) 类型: Number or String 一个字符串或者数字决定动画将运…

jQuery自定义动画

首先创建一个新的html&#xff0c;在body下面输入源代码&#xff1b;div标签代表的是颜色的面板&#xff0c;button标签代表的是按钮当我们输入好了源代码之后呢&#xff0c;那我们的源代码部分已经完成了&#xff0c;接下来剩下的就只有css样式和jQuery函数了 接下来我们先完成…

自定义动画

自定义动画非常简单&#xff0c;只需要实现它的applyTransformation的逻辑就可以了&#xff0c;不过通常情况下&#xff0c;还需要覆盖父类的initialize方法来实现一些初始化工作。applyTransformation方法有如下两个参数。 applyTransformation(float interpolatedTime, Trans…

自定义控件三部曲之动画篇(四)——ValueAnimator基本使用

前言&#xff1a;不要让别人的无知断送了你的梦想&#xff0c;永远坚信你所坚信的。 相关文章&#xff1a; 《Android自定义控件三部曲文章索引》&#xff1a;http://blog.csdn.net/harvic880925/article/details/50995268 一、概述 long long ago&#xff0c;我写过几篇有关…

[HTML/CSS]动画效果以及自定义动画效果

一&#xff0c;实现div或者文字的当鼠标浮于其上时的动画效果&#xff1a; 1&#xff0c;transition加在div中&#xff0c;实现动画效果的过渡效果&#xff0c;transition: all 3s;其中all表示所有样式都参与过渡&#xff0c;3s表示实现效果的时间&#xff1b;linear为使动画匀…