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:

public interface MyInterface {
    void sayYourName();
}

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

public class MyClass {

    public MyClass() {
        Log.e("WangJ", "MyClass-constructor");         //标注构造函数
    }

    /* 用接口类型的对象作为输入参数 */
    public void sayYourName(MyInterface myInterface){
        Log.e("WangJ", "MyClass-sayYourName_start");    //标注方法开始
        myInterface.sayYourName();                      //遇到不知道具体实现的时候就用接口的抽象方法
        Log.e("WangJ", "MyClass-sayYourName_finish");   //方法结束
    }
}

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

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        MyClass myClass = new MyClass();
        myClass.sayYourName(new MyInterface() {                     //实现接口并作为参数传入
            @Override
            public void sayYourName() {
                Log.e("WangJ", "callBack-interface-implementor");    //具体操作实现
            }
        });
    }
}

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

        

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


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


        参考文章:

        详细介绍Android中回调函数机制

        Android开发中Java回调机制的应用

        弄明白Android 接口回调机制

  • 71
    点赞
  • 136
    收藏
    觉得还不错? 一键收藏
  • 33
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值