Java/Android中的函数调用&回调函数&自定义回调函数

在做Android自定义控件时遇到要自定义回调函数的问题,想想自己还暂时没有那么精深的技术,赶紧返过头回来再重新研究Java中回调函数的问题。然而不幸的是,网上太多杂乱的帖子和博客都是转来转去,而且都是那一篇“C中的回调函数.....指针.....java....”,一点看不出来是自己的思路,估计都是哪哪哪抄来的!(呵呵,要么就是吐槽对了,要么就是我水平太烂读不懂还妄加评论)还有一些很不错的文章,我会在最后参考中加上链接,大家可以看看。

那么来开始我们的正题——什么是回调函数?

我们一步步深入,先从函数调用开始:

什么是函数(方法)调用?

(别的什么语言的都忘了……呵呵,只看Java)Java中的函数调用无非就是(1)一个类中方法为了完成一个业务在执行过程中调用另一个方法,另一个方法也可以是自己,那就是递归啦;(2)不同类之间的函数调用,比如Class B(调用者Caller)中要调用Class A对象的一个方法(被调用者Callee)(不管A是作为B的成员,还是作为函数参数传进来)。方法调用是我们编程中必不可少的,可能我们平时视而不见罢了,否则一个工程的不同CLASS怎么协同工作呢?虽然简单,还是画个图说一下吧,待会对比一下可以更好的解释回调的机制:

如上图Class B的函数method_B在执行过程中要调用成员a(Class A)的method_A1方法。

我们刚开始学的时候都是这样做的。但是这样做存在问题,让我们来回顾一下我们当年学习的经过,不断将这个问题解释清楚:举个例子吧,不然嘴笨表达不清楚——A类是鸟类,B就是我们的工具类,现在我们要在工具类中完成这样的需求:通过工具类中的方法method_B完成不同鸟类飞的正确动作。

第一阶段:显然就两个类完成不了这个需求!接着我们学习了继承,我们创建了“麻雀”、“大雁”、“鸵鸟”……各种“鸟”类子类,通过不同子类来完成不同的飞法,然后在工具类中创建不同子类的对象赋值给a,这样需求完成了。但是回头看看,我们创建了一大堆的子类,而且还用了隐式类型转换(子类赋值给父类),显然不够满意;

第二阶段:后来,我们又学习了重载函数,在鸟类中创建一系列同名同名的“飞”函数,传入参数类型分别为麻雀、大雁……,这样我们就不用隐式转换了,直接调用鸟类对象a的飞方法,传入不同的大雁、麻雀……,a就能正确的飞了。这样需求也完成了,虽然避免了使用隐式转换,但是那些为数众多的子类可一个也没有落下。这样做显然也不符合我们的期望(我们的期望是什么?就是干最少的活,鸟就能正确的飞)。

以上实现方式的问题就显而易见了:一方面我们要维护那些众多的子类,增加很大的工作量;另一方面,这样的编码缺乏灵活性,有多少种鸟你知道吗,每种鸟又是怎么飞的你知道吗,如果几种鸟极为相似,你是创建不同的类呢还是归为一类?所以有了下边这种实现方式,就是采用“回调函数”——

什么是回调函数?

直接上图吧:

如上图,回调函数中必然用到接口。下边是感觉写得好的一段理解:

android的学习过程中经常会听到或者见到“回调”这个词,那么什么是回调呢?所谓的回调函数就是:在A类中定义了一个方法,这个方法中用到了一个接口和该接口中的抽象方法,但是抽象方法没有具体的实现,需要B类去实现,B类实现该方法后,它本身不会去调用该方法,而是传递给A类,供A类去调用,这种机制就称为回调。

我认为,上图中B类的方法method_B在调用a对象的method_A1时,method_A1执行到interface的抽象方法不知道怎么实现,正好B在调用他的时候提供了具体实现,所以method_A1返回来调用method_B提供的实现,这就是回调。其实,回调函数就是在一个不确定实现的方法METHOD中用interface或者它的抽象方法留个口子,留给具体调用者(调用前边那个不确定的方法METHOD)在调用时提供具体实现来补上那个口子。从而达到更灵活地编码的目的,也大大减少了子类的使用。就拿上边没完的例子继续吧——

我们这样来实现:先定义一个接口,接口中声明抽象方法“飞”;在“鸟”类的“起飞”方法中把接口对象作为一个参数传进来,剩下的该怎么做就怎么做,遇到要飞的地方不知道具体怎么飞就调用接口提供的抽象方法“飞”;在工具类中调用“鸟”类的“起飞”方法时要实现了抽象方法的对象作为参数传入,然后你想让它怎么飞就怎么飞,具体实现是你调用的时候现写的。怎么样,这样的实现好吧?不用隐式转换,不用大量子类,调用的时候遇到什么鸟就怎么飞,达到了我们少干活的目的!

什么是自定义回调函数?

自定义回调函数,顾名思义,就是我们自己定义的回调函数。其实上边那个例子就是自定义回调函数!我们习惯上把别人定义好的回调函数叫作回调函数,Android系统中TextView、ImageView等和它们的子类控件的Onclick事件响应就是典型的回调机制。关于这个这位大虾讲得比我好——详细介绍Android中回调函数机制,详细看看会很有帮助的!

一个简单的自定义回调函数的例子

最后在举个简单的有代码的例子,看一下回调函数的运行过程:

首先,我们定义一个interface:

[java] view plain copy

  1. public interface MyInterface {
  2. void sayYourName();
  3. }

接着,我们定义一个类,其中一个方法以接口MyInterface类型的对象作为参数:

[java] view plain copy

  1. public class MyClass {
  2. public MyClass() {
  3. Log.e("WangJ", "MyClass-constructor");         //标注构造函数
  4. }
  5. /* 用接口类型的对象作为输入参数 */
  6. public void sayYourName(MyInterface myInterface){
  7. Log.e("WangJ", "MyClass-sayYourName_start");    //标注方法开始
  8. myInterface.sayYourName();                      //遇到不知道具体实现的时候就用接口的抽象方法
  9. Log.e("WangJ", "MyClass-sayYourName_finish");   //方法结束
  10. }
  11. }

最后,我们在Activity中调用这个类,创建对象并调用其方法,期间实现接口中抽象方法的具体实现逻辑,供回调使用:

[java] view plain copy

  1. public class MainActivity extends Activity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_main);
  6. MyClass myClass = new MyClass();
  7. myClass.sayYourName(new MyInterface() {                     //实现接口并作为参数传入
  8. @Override
  9. public void sayYourName() {
  10. Log.e("WangJ", "callBack-interface-implementor");    //具体操作实现
  11. }
  12. });
  13. }
  14. }

好了,运行一下(我们这个例子没有任何界面,即默认Activity的界面,看日志):

运行的顺序就是我们之前理解的:在B中调用A中的方法,A中方法在运行到接口中抽象方法时返回B中寻找具体实现(这就是回调),回调完成后继续执行下边未完成的步骤。

好了,以上就是我所认识的回调函数,听起来高深,在你弄懂以后发现也没有太大的难度。但是想想Java研发者在设计这种机制的时候是多么有远见啊(好崇拜,虽然不知道他是谁)!文笔有限,理解不够,如有不足或错误,欢迎指正!最后如约附上那几篇不错的文章——

时间: 2024-10-05 05:37:15

Java/Android中的函数调用&回调函数&自定义回调函数的相关文章

EL表达式 JSTL的标签库 EL的函数 自定义EL函数 自定义标签 JSP的开发模式 注册登陆案例

EL表达式 JSTL的标签库 EL的函数 自定义EL函数 自定义标签 JSP的开发模式 注册登陆案例 EL表达式 1.获取域对象中的值 2.EL表达式支持运算 ${ num1 + num2 } EL获取常用的WEB对象 1.pageScope 获取page域对象的值 2.requestScope 获取request域对象的值 3.sessionScope 获取session域对象的值 4.applicationScope 获取application域对象的值 * 例子:向某个域中来存入值 req

Android中关于内部存储的一些重要函数

一.简介 Android中,你也可以通过绝对路径以JAVA传统方式访问内部存储空间.但是以这种方式创建的文件是对私有,创建它的应用程序对该文件是可读可写,但是别的应用程序并不能直接访问它.不是所有的内部存储空间应用程序都可以访问,默认情况下只能访问"/data/data/你的应用程序的包名"这个路径下的文件. Android中,你还可以使用Context对象的和来进行数据持久化存储的这种方式,你的数据文件将存储在内部存储空间的/data/data/你的应用程序的包名/files/目录下

Java/Android中的优先级任务队列的实践

版权声明:转载必须注明本文转自严振杰的博客:http://blog.yanzhenjie.com 刚刚把公司的活干完,去群里水,有几个小伙伴问我怎么实现队列,于是乎我来写一篇吧.本篇文章适用于Java和Android开发者,会从实现一个最简单的队列过渡到实现一个带有优先级的队列,保准你可以掌握基本的队列原理. 队列的基本理解 用生活中的一个情景来举个栗子,前段时间很火爆的电视剧<人民的名义>中有一个丁义珍式的窗口大家应该都知道了,我们不说<人民的名义>也不说丁义珍,我们来说说这个办

Android中如何控制LogCat的自定义输出

在Android开发中,LogCat是一个非常重要的调试工具,可以输出很多关于项目或者手机的信息.但是正是由于LogCat功能的过于强大,输出的信息量也是极为庞大的,那么我们就需要通过一定的方式根据我们的需要限定LogCat的输出,这样才能使LogCat帮我们起到更好的调试代码的作用. LogCat输出的类型一般有五种,分别是verbose,debug,info,warn,error.其中verbose的最为宽泛,如果选择verbose,那么就会向下兼容输出verbose,debug,info,

Android中ListView与RadioButton结合----自定义单选列表

有时候我们需要制作自定义的单选列表,但是会遇到一些问题,比如多选,假选问题,所以网上找了找资料,整理一个demo出来,贴一下代码: <ListView android:id="@+id/listView1" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerHorizontal="true&qu

android中的生命周期(新增2个函数)

onPostOnCreate()和OnPostResme()这两个函数 onPostResume() Called when activity resume is complete (after onResume has been called). Applications will generally not implement this method; it is intended for system classes to do final setup after application

题目1004:Median(qsort函数自定义cmp函数)

题目链接:http://ac.jobdu.com/problem.php?pid=1004 详解链接:https://github.com/zpfbuaa/JobduInCPlusPlus 参考代码: // // 1004 Median.cpp // Jobdu // // Created by PengFei_Zheng on 26/04/2017. // Copyright © 2017 PengFei_Zheng. All rights reserved. // #include <std

Android中的Drawable菠菜bc网站搭建架设基础与自定义Drawable

6.gravity属性详情 可选项 含义top/bottom/left/right 将图片放在容器上/下/左/右,不改变图片大小center_vertical/horizontal 垂直居中/水平居中,不改变图片大小center 水平和垂直方向同时居中,不改变图片大小fill_vertical/horizontal 垂直/水平方向填充容器fill 水平和垂直方向同时填充容器clip_vertical/horizontal 垂直/水平方向的裁剪-较少使用7.NinePatchDrawable(.9

Oracle自定义聚集函数

今天工作中看见别人写的自定义聚集函数,所以一门心思的想搞懂,就在网上找资料了. 自定义聚集函数 自定义聚集函数接口简介 Oracle提供了很多预定义好的聚集函数,比如Max(), Sum(), AVG(), 但是这些预定义的聚集函数基本上都是适应于标量数据(scalar data), 对于复杂的数据类型,比如说用户自定义的Object type, Clob等, 是不支持的. 但是,幸运的是, 用户可以通过实现Oracle的Extensibility Framework中的ODCIAggregat