什么是接口回调?

时间:2024-03-07 10:49:56

一般来说,模块之间都存在一定的调用关系,从调用方式上看,可以分为三类:同步调用、异步调用和回调。同步调用是一种阻塞式调用,即在函数A的函数体里通过书写函数B的函数名来调用之,使内存中对应函数B的代码得以执行。异步调用是一种类似消息或事件的机制解决了同步阻塞的问题,例如 A通知 B后,他们各走各的路,互不影响,不用像同步调用那样, A通知 B后,非得等到 B走完后, A才继续走 。回调是一种双向的调用模式,也就是说,被调用的接口被调用时也会调用对方的接口,例如A要调用B,B在执行完又要调用A。

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

java接口回调机制想必大家并不陌生,其思想简单,应用广泛,如网络请求、界面的点击监听等,是一个java开发者必须要掌握的基本思想之一。

我们在做java开发时经常会遇到文件下载、请求服务器数据等基本操作,大家都知道网络请求属于耗时操作,我们如果在直接主线程执行这些逻辑时很可能会造成主线程堵塞,从而导致程序崩溃。我们通常都是开启一个子线程来执行网络请求操作。

    public void doSomeWork() {
        new Thread(new Runnable() {
            @Override
            public void run() {

                    // 执行逻辑,发起网络请求,如请求后台数据,下载文件等

            }
        }).start();
}

上面这段代码想必你已经再熟悉不过了,不过当我们在做一次开发时,经常会多次使用网络请求,比如多次请求服务器的数据,所以我们更愿意将其写成一个小框架: 

public String doRequest(String url) {
        new Thread(new Runnable() {
            @Override
            public void run() {

                    // 执行逻辑,请求后台json数据

            }
        }).start();
        //将获取的数据转化为String,并返回
    }

那么问题来了: 我们在写成框架时,网络请求是在子线程中进行的,很可能数据还没返回来的时候,doRequest方法就已经执行完了,那么这时候返回的数据就没有任何意义了,最终的结果是我们得到的String为空的,而不是我们期待的的数据。

解决此问题的一种方法: 子线程请求到数据后,直接对数据进行处理(缺陷:失去了框架的意义)

public void doRequestAndDealData(String url) {
        new Thread(new Runnable() {
            @Override
            public void run() {

                    // 执行逻辑,请求后台json数据
                    // 直接对获取到的数据进行处理

            }
        }).start();
    }

对比上一段代码,我们这里直接对返回的数据进行了处理,而没有在方法里返回数据,但是这样的处理逻辑就是唯一的了,并不能随着请求的url不同而执行不同的处理逻辑,那么有没有一种方法能将在子线程中获取到的数据"传出去",使其能得到成功的处理呢? 这就是接口回调的巧妙之处了!

  • 先定义一个接口

public abstract class CallBackListener {

    public abstract void onFinish()

    public abstract void onError(Exception ex);

}
  • 在类A中通过在子线程发起网络请求,并将接口作为参数,写到类A中!

public class A {
    public void doRequest(String url,CallBackListener backListener) {
        new Thread(new Runnable() {
            @Override
            public void run() {

                try {
                    // 执行逻辑,发起网络请求,如请求后台数据,下载文件等
                    backListener.onFinish();

                } catch (Exception ex) {
                    backListener.onError(ex);
                }
            }
        }).start();
    }
}
  • 在类B中调用类A的对象,发起请求,并且对请求得到的数据进行处理。

public class B {
    public void deal() {
        A a = new A();
        a.doRequest("http://请求的url",new CallBackListener() {
            @Override
            public void onFinish() {
                //请求成功的逻辑,如下载完成后的处理,请求到数据后的处理
            }

            @Override
            public void onError(Exception ex) {
                // 异常逻辑
            }
        });
    }
}

我们在B中调用A的方法时,重写了接口中的方法,当发起的网络请求完成时,就会调用我们重写后的方法,这就是接口回调,这样根据需要来进行不同的重写,同样保留了框架的意义。

我们在下载完成后,界面的点击事件监听,后台数据请求完成时...还有很多地方都可以用到接口回调,掌握其思想后,我们也可以写的更加规范一点了,如将A类中的接口作参数就写为单的一个方法,setListener(CallBackListener listener);