美文网首页安卓基础知识安卓Android
Android回调函数机制那点事

Android回调函数机制那点事

作者: 李牧羊 | 来源:发表于2016-04-01 23:53 被阅读7348次

引言

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

这么说可能还是有些模模糊糊,接下来我们用类比的方法一步步来看到底该怎么写一个回调函数,因为android回调中最常见的是Button的点击事件的回调,这里以此为参照:
1、在A类中定义一个接口:需要我们在类中定义出一个接口,并且给这个接口定义出一个抽象方法,就像下面这样:

public interface CallBack{
        public abstract void work()
    }

以下是View.java类中定义的响应点击事件的接口:

/**
     * Interface definition for a callback to be invoked when a view is clicked.
     */
    public interface OnClickListener {
        /**
         * Called when a view has been clicked.
         *
         * @param v The view that was clicked.
         */
        void onClick(View v);
    }

2、在A类中定义出该接口的一个成员变量:

public CallBack mCallBack

以下是View.java类中获取点击事件接口成员变量的源码:

  /**
         * Listener used to dispatch click events.
         * This field should be made private, so it is hidden from the SDK.
         * {@hide}
         */
        public OnClickListener mOnClickListener;

3、在A类中定义出一个公共方法,可以用来设置这个接口的对象,调用该方法可以给接口对象变量赋值:

public void setCallBack(CallBack callBack) {    
    this.mCallBack = callBack;    
}

这里看英文注释也看得出来是什么意思,是不是想到了我们平常使用setOnClickListener(OnClickListener l)的时候呢:

 /**
     * Register a callback to be invoked when this view is clicked. If this view is not
     * clickable, it becomes clickable.
     *
     * @param l The callback that will run
     *
     * @see #setClickable(boolean)
     */
    public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }

最后一步,如果说前面的都没问题,但这一步可能更不好理解了,不过没关系,我们先看一下
4、在A类中调用接口对象中的方法:

public void doWork(){
   mCallBack.work();
}

在View.java中的体现:

 /**
     * Call this view's OnClickListener, if it is defined.  Performs all normal
     * actions associated with clicking: reporting accessibility event, playing
     * a sound, etc.
     *
     * @return True there was an assigned OnClickListener that was called, false
     *         otherwise is returned.
     */
    public boolean performClick() {
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);//就是这里
            result = true;
        } else {
            result = false;
        }

这里附上整个项目的代码,这里A类映射到实际中使用Employee
这个类来代表:

public class Employee {    
    /*  
     * 定义回调接口的成员变量  
     */    
    private CallBack mCallBack;    
    /*  
     * 声明回调接口  
     */    
    public interface CallBack{    
        public abstract void work();    
    }    
    /*  
     * 设置回调接口对象成员变量  
     */    
    public void setCallBack(CallBack callBack) {    
        this.mCallBack = callBack;    
    }    
    /*  
     * 调用回调接口对象中的方法  
     */    
    public void doWork() {    
        mCallback.work();    
    }    
}    

我们在定义出一个B类,就用Boss
类吧:

public class Boss {    
    private Employee employee;    
    /*  
     * 为Employee设置回调函数, 在这里定义具体的回调方法  
     */    
        employee.setCallback(new Employee.Callback() {    
            @Override    
            public void work() {    
                System.out.println("work");    
            }    
        });      
}   

如果第一眼看不明白,我们附上我们最常用的Button点击事件的处理的代码,这里Employee 类类比一下就是View类:

public class TestCallBack{
    private Button button;
    button.setOnClickListener(new OnClickListener() {
        
        @Override
        public void onClick(View v) {
           //做一些操作
            doWork();
        }
    });
    
}

这时候我们在再回头关于回调的定义:
在A类中定义了一个方法,这个方法中用到了一个接口和该接口中的抽象方法,但是抽象方法没有具体的实现,需要B类去实现,B类实现该方法后,它本身不会去调用该方法,而是传递给A类,供A类去调用
这里回到我们的代码中就是:
我们在Employee(View)类中定义了一个接口,接口当中还含有一个抽象方法,这个抽象方法没有具体的实现,当我们需要时候自己去实现这个方法,比如这里的Boss (Button)类,这句话可能难以理解:B类实现该方法后,它本身不会去调用该方法,而是传递给A类,供A类去调用,有些人会想,诶,在onClick()
方法中我不是写了具体的实现嘛,其实真的是这样吗,我们接着往下看,我们就来好好的分析一下这个Button点击事件。

分析
首先,在View类中我们能找到setOnClickListener(OnClickListener l)方法:

 public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }

这里将OnClickListener赋值给了mOnClickListener,我们想要找到onClick()方法是由View回调而不是Button自己回调的证据,就在这里:

public boolean performClick() {  
     sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);  
     ListenerInfo li = mListenerInfo;  
     if (li != null && li.mOnClickListener != null) {  
         playSoundEffect(SoundEffectConstants.CLICK);  
         li.mOnClickListener.onClick(this);  
         return true;  
     }  
     return false;  
}  

对此我们的解释是:在父类中我们要用到onClick()
方法,但是父类却没有去实现该方法,而是定义了一个方法setOnClickListener(OnClickListener l),如果子类想要自己能够响应点击事件,则它就必须重写父类的该方法,实现OnClickListener接口和它的onClick()
方法。在子类实现该接口和方法后,将其通过参数传递给父类,在父类中执行onClick()
方法。那么我们是如何运行到这个OnClick()
函数的呢,这里由于涉及到View的事件分发机制不细说,想了解的话网上有很多资料,这里只给出结论,因为我们在TestCallBack这个类中没有实现OnTouchListener
这个接口,那么当点击事件发的时候必然会运行到onTouchEvent()
这个方法,我们来看一下这个方法:

public boolean onTouchEvent(MotionEvent event) {  
    // 略去无用代码...  
    if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {  
  
         switch (event.getAction()) {  
  
              case MotionEvent.ACTION_UP:  
                      
            // 略去无用代码...  
                if (!mHasPerformedLongPress) {   
                    // This is a tap, so remove the longpress check removeLongPressCallback();   
                    // Only perform take click actions if we were in the pressed state   
                    if (!focusTaken) {   
                        // Use a Runnable and post this rather than calling   
                        // performClick directly. This lets other visual state   
                        // of the view update before click actions start.   
                        if (mPerformClick == null) {   
                            mPerformClick = new PerformClick();   
                        }   
                        if (!post(mPerformClick)) {  
                            performClick(); //重点在这  
                        }   
                    }   
                }   
                //略去无用代码..  
                break;   
        }   
        return true;   
    }   
    return false;   
}  

还记得前面我们在performClick();类里面找到的关于View.java类中对于回调方法onClick的调用么

li.mOnClickListener.onClick(this);//就是这里

这样我们就完整的了解了整个OnClickListener()
接口中体现出来的回调机制
总结一下
为了实现一个回调方法,首先要先定义一个包含了接口的类,并且这个接口中要有一个抽象方法,这个抽象方法的具体实现由其他类来完成(比如我们响应Button的点击事件,onClick()
方法里写上当点击事件产生并且该方法被调用时候需要做的操作,比如显示一些文本信息等等),最后该方法的回调是之前的包含有抽象方法的那个接口所在的类去调用的,比如说onClick()方法是当点击事件产生之后经过一系列的事件分发在View
类中被调用的。这其中的奥秘就是:

回调其实是一种双向调用模式,也就说调用方在接口被调用时也会调用对方的接口,实现方法交还给提供接口的父类处理!

为什么要用回调

我们都知道Java是一门面向对象的语言,有一句很著名的话就是”万事万物皆为对象”,我们把普通事物的共性抽取出来,而这些共性之中又充斥着特性,每个不同的特性就需要交给特定的情况处理,通过暴露接口方法可以减少很多重复,代码更加优雅。

打个比方,Button、ImageButton等都具有可被点击的共性,但是被点击之后相关事件的处理是不同的,比如说我想我要点击的这个Button弹出一个消息提示,然而我希望我的ImageButton点击之后可以弹出一个Notifaction通知,这个时候回调方法的好处就体现出来了,因为android对外暴露的OnClickListener()
接口中含有一个OnClick()
方法,你需要怎样的具体实现都由你自己定义,而这个回调方法的所在类View
不会管你怎么实现的,它只负责调用这个回调方法,这就是使用回调的好处。

主要参考
Android中回调函数机制解析
android中的回调

谢谢阅读

最后,觉得写得不错的可以关注一下我的公众号呀~蟹蟹

0.jpg

相关文章

  • Android回调函数机制那点事

    引言 在Android的学习过程中经常会听到或者见到“回调”这个词,那么什么是回调呢?所谓的回调函数就是:在A类中...

  • # Android 中的回调函数揭秘

    在学习Android的过程中,经常会遇到"回调函数"这个词,那么甚么是回调函数呢? 简单地说,回调函数就是通过其指...

  • 带你了解Android接口回调机制

    Android接口回调机制 接口回调是在Android中运用广泛的一种机制,你一定会眼熟它,接下来,我们从两个方面...

  • Android 回调机制

    今天无意中看到了说java的回调机制,我感觉就和我以前用过的一样啊。。。所以就记录下来。。。 说说怎么接触到回调的...

  • 回调函数callback

    回调函数callback 回调函数:简而言之就是某个特定事件发生时被指定调用的函数。运行机制简单理解就是:现在有主...

  • 再看Promise

    Time: 2019-08-21 回调函数 是一种事件机制,某种事件完成后,会触发相应的回调函数。 Promise...

  • 程序设计-设计模式

    回调(Call back )函数是面向过程的程序设计语言中常用的一-种机制,而设计模式中的( )模式就是回调机制的...

  • JavaScript函数_08回调函数

    回调函数 回调函数(回调),当我们把某个函数作为参数传递给另一个函数的时候,这个函数就是回调函数 回调函数的基本写...

  • Promise

    回调 把一个函数A传给另一个函数B调用,那么A就是回调函数。 回调地狱 回调套回调套回调套回调套回调套回调套回调....

  • Android回调机制浅谈

    参考博客如下http://blog.csdn.net/xiaanming/article/details/8703...

网友评论

  • forAllBright:employee.setCallBack(new Employee.Callback()) ide 表明 employee 没有 setCallBack 方法呢, 位置是在 “这里附上整个项目的代码,B 中”
  • cc07e99cee9c:棒棒哒
    cc07e99cee9c:@李牧羊 嗯嗯哈哈哈哈哈~~楼主辛苦~~
    李牧羊:最近抽空更改了一下代码排版,现在效果可以了,这篇文章发的很早当初不知道怎么排版
  • while1love:哇 你这代码写的好不规范, 看的真难受
    while1love: @李牧羊 舒服了,谢谢哇~
    李牧羊:最近抽空更改了一下代码排版,现在效果可以了
    李牧羊:以前不知道简书怎么排版,见谅
  • SuperBaby酱:用了好长时间回调了,照猫画虎的,但还是不怎么理解,我笨的不行了
    李牧羊:慢慢来,有时候走过去回头看看就能理解
  • 小龍五:楼主讲的很容易理解
  • 091840a0ef9c:感觉就是多态呀!楼主我这样理解对吗?
    李牧羊:@bruce_lv 这篇文章里不是,但是还有一种回调是提出一个公共接口类,具体业务由子类执行,在回调绑定的时候只需要把子类对象的指针赋值给基类指针,通过基类指针调用公共接口就可以实现回调绑定
    bruce_lv:@李牧羊 是吗?不是吧
    李牧羊:@hiro方圆 是的
  • bcae668afdd1:终于在这理解的有那么点意思了,楼主正解!
  • 抓兔子的猫:分析的不错,两个例子一起看效果很好
    李牧羊:@抓兔子的猫 谢谢喜欢

本文标题:Android回调函数机制那点事

本文链接:https://www.haomeiwen.com/subject/qevylttx.html