- class A实现接口InA
- class A中包含一个class B的引用b
- class B有一个参数为InA的方法test(InA a)
- A的对象a调用B的方法传入自己,test(a) ——这一步相当于you call me
- 然后b就可以在test方法中调用InA的方法 ——这一步相当于i call you back
再来定义实现了CallBack接口的class A,定义为Boss类,我们模拟的场景是,Boss手下有一个员工Employee,Boss在某个时刻想要让员工去帮他买东西,当员工买完后将把结果告知给Boss,也就是我们所说的回调,通过回调函数的solve()方法将结果给Boss。
Boss类在创建的时候需要传入一个员工对象Employee,在需要的时候,会调用Employee的方法,让他来处理逻辑,Employee如下:
当想要调用Employee的buySomething方法时,需要传入Callback接口的实现类,逻辑处理完毕后则调用Callback接口的方法回传结果,编写测试类,测试结果如下:
其实在Java中,线程Thread的原理也是使用了回调机制,我们可以来看下面这个最简单的线程实例:
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
}).start();
我们先进入Runnable代码中去看,可以发现他是个接口:
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
我们将Runnable的实现类当做参数传入给Thread类,Thread的start()方法会回调Runnable实体类的run方法来进行逻辑处理,但是我们却发现了一个奇怪的事情,那就是Thread类已经实现了Runnable接口,却还要传入Runnable的实现类,这是为什么?原来这是java中的代理模式!!!
继承Thread类方式和通过Runnable接口的方式
继承Thread类方式的缺点:如果我们的类已经继承了其他类,那就无法再继承Thread类了
实现Runnable接口的方式优点:避免了单继承,方便共享资源,同一份资源可以有多个代理访问
代理就比如是找房子可以找中介,结婚可以找婚庆公司。
在静态代理模式中有两个角色
1:真实角色
2:代理角色
两个角色通过实现相同的接口来实现关联
下面可以看一个例子://真实角色
class You implements IMarry{
@Override
public void marry() {
}
}
class WeddingCompany implements IMarry{
@Override
public void marry() {
}
}
让代理模式持有真实角色的引用
class WeddingCompany implements IMarry {
private IMarry you;
public WeddingCompany(){}
public WeddingCompany(IMarry you){
this.you=you;
}
@Override
public void marry() {
you.marry();
}
}
使用实例代码
public static void main(String[] args) {
//创建真实角色
IMarry you=new You();
//创建代理角色加入真实角色的引用
WeddingCompany weddingCompany=new WeddingCompany(you);
}
此时让我们进入Thread的构造函数来去看看:
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
继续往下找,就能看到初始化Runnable对象的代码(中间省略):
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
...
this.target = target;
setPriority(priority);
...
}
好了,我们可以看到Thread的构造函数将Runnable的实例对象初始化给了成员变量,我们都知道要启动一个线程需要调用start()方法,我们看下start()方法做了什么:
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
具体代码不做研究,start()方法调用操作系统本地的线程启动方法,使得run()方法内部的代码执行在异步线程中,我们都知道线程启动有两种方法,start()和run(),start方法正常启动异步线程,而run方法虽然同样执行了内部的所有操作,但确实跟主线程同步执行的,无法进行异步操作,我们看下Thread的run方法就知道为什么了:
/**
* If this thread was constructed using a separate
* <code>Runnable</code> run object, then that
* <code>Runnable</code> object's <code>run</code> method is called;
* otherwise, this method does nothing and returns.
* <p>
* Subclasses of <code>Thread</code> should override this method.
*
* @see #start()
* @see #stop()
* @see #Thread(ThreadGroup, Runnable, String)
*/
@Override
public void run() {
if (target != null) {
target.run();
}
}
这样我们就能看出所以然了,Thread的run方法居然只是简单地调用了传入的Runnable对象的run方法,并没有开启异步线程。
关于Thread的几种常用方式可以看下网上的博客,一般都是两种方法:直接继承Thread重写run方法或者实现Runnable接口写run方法,两种区别就是,实现Runnable接口适用于多线程对于同一个数据进行操作,例如我们常见的卖票系统等。
本篇主要是说回调的使用,有些跑偏了,那么android中是否存在回调呢?
本篇主要是说回调的使用,有些跑偏了,那么android中是否存在回调呢?
必须得啊,我们天天都在使用的OnClickListener,OnLongClickListener等一系列方法都是回调方法,首先我们看下回调接口:
/**
* 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);
}
对,没错,就是View类中的OnClickListener接口,这个接口唯一的方法:onClick也就是回调函数用于我们处理点击事件的逻辑,当一个View发生点击事件时,就会回调onClick方法,我们就可以在Activity进行逻辑处理。下面我们模拟一下这个场景,首先定义抽象接口和view如下:
//点击事件的接口
public interface OnClickListener {
public void onclick();
}
//view类,保存实现事件接口的实体对象,在适当的时候调用接口方法,回传点击事件
public class View{
private OnClickListener listener;
public void performClick(){
listener.onclick();
}
public void SetOnClickListener(OnClickListener l){
listener=l;
}
}
实际情况中OnClickListener是定义在View类的内部,但这里我们为了更好地看清楚回调函数的使用,将其分开来了。View中定义了两个方法,SetOnClickListener用来将onClickListener的实例对象与其绑定,也就是获得实例对象,当用户进行点击操作时performClick方法就会被调用,然后回调接口的onclick方法。
下面是Activity的定义:
//Activity实现点击事件的接口,用来接收view的点击事件
public class Activity implements OnClickListener{
private View view;
public Activity(){
view=new View();
view.SetOnClickListener(this);
}
public void onclick(){
System.out.println("view点击一次");
}
public void clickOnce(){
view.performClick();
}
}
Activity实现了接口OnClickListener,并定义了一个view,调用view的SetOnClickListener方法将自己传入,然后我们模拟点击事件,clickOnce调用时,就会调用view的performClick方法,然后view会回调onclick(),如下:
/**
* @param args
*/
public static void main(String[] args) {
Activity activity=new MyActivity().new Activity();
activity.clickOnce();
}
当然我们只是非常简单地模拟点击事件,不过原理就是我们上面所讲述的:回调!!!可见回调在编程中是非常重要的一个知识点。在android开发中要结合java的特性来思考,以前只会简单地调用view.setOnClickListener(),不明白其中的原理,现在一定要多想多思考。