在做项目的过程中,做了很多回调,好像是一种自然而然的事。回过头来品味,还是十分有趣的。在Android中为什么定义那么多接口,很大一部分都是用来接口回调的,包括那些OnListener等系统给出接口都是这种用法。
理解一个东西,必须从它的本源入手,再实例化到生活事例中,加深理解,毕竟程序是对现实生活的一种抽象。
而Android中的回调,遵循的基本思想是Java中的回调函数。
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
Java 中没有指针的概念,通过接口和内部类的方式实现回调的功能:
1. 定义接口 Callback ,包含回调方法 callback()
2. 在一个类Caller 中声明一个Callback接口对象 mCallback
3. 在程序中赋予 Caller对象的接口成员(mCallback) 一个内部类对象如
new Callback(){
callback(){
//函数的具体实现
}
这样,在需要的时候,可用Caller对象的mCallback接口成员 调用callback()方法,完成回调.
Talk is cheap,show me the code!
现在就上代码来讲解,这是基于本例子的讲解。
public class Test{
// 方法是fun
public static void fun(Callbackinterface ci){
ci.callbackfun();
}
//这就是接口
public interface Callbackinterface{
public void callbackfun();
}
}
public static void main(){
// 调用fun,传入接口对象并构造内部类
Test.fun(new Callbackinterface(
public void callbackfun(){
//你的实现
}
)
);
}
看完这个,大家可能还会比较模糊,没关系。我再讲一个实例:
一项工程由程序员A和B共同完成,分别负责不同的模块,模块之间有交叉,A和B可能用到对方的方法,这时需要进行回调。
假设我是程序员A,写了一个程序a:
public class Caller {
private MyCallInterface mc;
//构造函数
public Caller() {
}
public setI(MyCallInterface mc) {
this.mc = mc;
}
//Caller的调用方法
public call() {
mc.fuc();
}
}
这里需要定义一个接口,以便程序员B根据我的定义编写程序实现接口。
public interface MyCallInterface {
public void fuc();
}
于是,程序员B只需要实现这个接口就能达到回调的目的了:
public class callee implements MyCallInterface {
public void fuc() {
//do something
}
}
下面是调用过程:
public class callbacks {
public static void main(String args[]) {
Callee c1 = new Callee();
Caller caller = new Caller();
caller.setI(c1);
caller.call();
}
}
在以上代码中,caller是调用者,callee是被调用者,callbacks表示调用过程。
先产生了Callee对象(已经实现Caller提供的接口),利用这个callee对象产生的Caller对象则携带了一些信息(即与Callee对象的关联,因为Callee对象已经作为参数传入),所以Caller对象可以利用自己的call方法调用Callee的方法。——这就是整个回调过程。
看了这个例子,想必大家已经清楚了Java回调函数的机制了吧。
现在来总结一下,一般来说分为以下几步:
1. 声明回调函数的统一接口interface A,包含方法fuc();
2. 在调用类caller内将该接口设置为私有成员private A XXX;
3. 在caller内提供一个public方法,可以将外部“该接口A的实现类的引用”通过形参传给XXX;
4. caller的某个方法call()中会用到XXX.fuc()方法;
5. 在caller的实例中,将实现了A接口的对象的引用传给caller,后调用call()方法
Android中回调是用得非常多的。比如点击事件,Activity的生命周期等等,这里的回调大多更是一种触发机制,可以说回调也是一种触发吧。
例如Button是设置了接口,接口就是OnListener,在onClick中我们写入自己的实现,然后系统在事件被触发后调用。我们自己不会显式地去调用onClick方法。用户触发了该按钮的点击事件后,它会由Android系统来自动调用。
下面模拟一下Activity生命周期,基本都是回调函数在作用:
1. Activity接口
//定义接口
public interface Activity{
//创建时调用的方法
public void onCreate();
//启动时调用的方法
public void onStart();
//销毁时调用的方法
public void onDestory();
}
2. Activity接口的实现类MyActivity
//定义一个类实现Activity接口
public void MyActivity implements Activity{
//实现创建方法,简单输出提示信息
@Override
public void onCreate(){
System.out.println("onCreate....");
}
//实现启动方法,简单输出提示信息
@Override
public void onStart(){
System.out.println("onStart....");
}
//实现销毁方法,简单输出提示信息
@Override
public void onDestory(){
System.out.println("onDestory....");
}
}
3. 系统运行环境类AndroidSystem
//系统运行环境类
public class AndroidSystem{
//定义创建常量
public static final int CREATE=1;
//定义启动常量
public static final int START=2;
//定义销毁常量
public static final int DESTORY=3;
//运行方法
public void run(Activity a,int state){
switch(state){
//创建
case CREATE:
a.onCreate();
break;
//启动
case START:
a.onStart();
break;
//销毁
case DESTORY:
a.onDestory();
break;
}
}
}
测试类:
//测试类
public class Test{
//主方法
public static void main(String[] args){
//实例化AndroidSystem
AndroidSystem system = new AndroidSystem();
//实例化MyActivity
Activity a = new MyActivity();
//创建
system.run(a,AndroidSystem.CREATE);
//启动
system.run(a,AndroidSystem.START);
//销毁
system.run(a,AndroidSystem.DESTORY);
}
}
通过上述代码我们可以看出,接口(系统框架)是系统提供的,接口的实现是用户实现的。这样可以达到接口统一,实现不同。系统通过在不同的状态“回调”我们的实现类,来达到接口和实现的分离。
这里引用其它人博客的一个事例吧:
让我们从一个小故事开始。
某天,我打电话向你请教问题,当然是个难题,你一时想不出解决方法,我又不能拿着电话在那里傻等,于是我们约定:等你想出办法后打手机通知我,这样,我就挂掉电话办其它事情去了。过了XX分钟,我的手机响了,你兴高采烈的说问题已经搞定,应该如此这般处理。
OK,这个故事我们先告一段落,其实,这就是一个典型的回调过程。
而在程序代码中,则可以抽象成以下这张图的形式:
C不会自己调用b,C提供b的目的就是让S来调用它,而且C不得不提供。S并不知道C提供的b是什么,因此S会约定b的接口规范(函数原型),然后由C提前通过S的一个函数r告诉S自己将要使用b函数(即注册),比如注册监听器就是其中一种典型。其中r为注册函数。
对上图的一个完善是这样的:
Android中还有很多其它的消息回调机制,我整理下后面会和大家分享。