コールバック関数とは
* あるクラスに、コールバックしてもらう関数を教えて、その関数が呼び出されるのを待つこと。
* 3つの方法があるかと。
[1] 自作インターフェースで使って、実装する
[2] CountDownLatch を使って、実装する
[3] Future インターフェースを使って、実装する
今回は、「[1] 自作インターフェースで使って、実装する」を取り上げる(他は下記【関連記事】を参照のこと)
実装ポイント
[1] インターフェースを用意する(ICallback)
[2] そのインターフェースを実装したクラスのオブジェクトごと、処理実行先(Task)に渡す
[3] 処理実行先で処理が完了したら、コールバック関数を実行してもらう
⇒コールバック関数を実現
サンプル1
ICallback.java
public interface ICallback {
// Point[1]!
public void callback();
}
TaskHandler.java
import java.util.Calendar;
public class TaskHandler implements ICallback {
private ITask task;
public TaskHandler(ITask task) {
this.task = task;
}
public void start() {
// Point[2]!!
this.task.setCallback(this);
this.task.execute();
}
// Callback
@Override
public void callback() {
System.out.println("callback " + Calendar.getInstance().getTime().toString());
}
}
public interface ITask {
public void setCallback(ICallback callback);
public void execute();
}
import java.util.Calendar;
public class Task implements ITask {
private ICallback callback;
public void setCallback(ICallback callback) {
this.callback = callback;
}
public void execute() {
// 処理実行
System.out.println("execute() " + Calendar.getInstance().getTime().toString());
try {
Thread.sleep(3000);
} catch (InterruptedException ignore) {
}
// 処理実行終了通知
System.out.println("execute() done " + Calendar.getInstance().getTime().toString());
this.callback.callback();
}
}
import java.util.Calendar;
public class Main {
public static void main(String[] args) {
System.out.println("Start " + Calendar.getInstance().getTime().toString());
TaskHandler handler = new TaskHandler(new Task());
System.out.println("Call start() " + Calendar.getInstance().getTime().toString());
handler.start();
System.out.println("Done " + Calendar.getInstance().getTime().toString());
}
}
出力結果
Start Fri Nov 14 23:48:56 JST 2014
Call start() Fri Nov 14 23:48:56 JST 2014
execute() Fri Nov 14 23:48:56 JST 2014
execute() done Fri Nov 14 23:48:59 JST 2014
callback Fri Nov 14 23:48:59 JST 2014
Done Fri Nov 14 23:48:59 JST 2014
サンプル2
* 処理が完了したことを通知するコールバック。
ただし、処理が重かった時を考慮して、Timeout(10000L=10秒)できるようにしておく。
=> カウントダウンラッチ(CountDownLatch) で実装可能。
http://blogs.yahoo.co.jp/dk521123/33538428.html
import java.util.Calendar;
public class Main {
public static void main(String[] args) {
System.out.println(Calendar.getInstance().getTime().toString()
+ " START");
SampleTaskManager sampleService = new SampleTaskManager();
sampleService.start();
System.out.println(Calendar.getInstance().getTime().toString()
+ " END");
}
}
public interface ICallback {
void done();
}
SampleTaskManager.java
import java.util.Calendar;
import java.util.concurrent.atomic.AtomicBoolean;
public class SampleTaskManager implements ICallback {
private AtomicBoolean isDone;
public SampleTaskManager() {
this.isDone = new AtomicBoolean(false);
}
@Override
public void done() {
this.isDone.set(true);
}
public void start() {
System.out.println(Calendar.getInstance().getTime().toString()
+ " start() START");
Thread sampleTask = new Thread(new SampleTask(this));
long now = System.currentTimeMillis();
long timeout = now + 10000L;
sampleTask.start();
while (System.currentTimeMillis() < timeout) {
try {
if (this.isDone.get()) {
System.out.println(Calendar.getInstance().getTime().toString()
+ " Done");
break;
}
Thread.sleep(50L);
} catch (InterruptedException e) {
}
}
System.out.println(Calendar.getInstance().getTime().toString()
+ " Normal Done? : " + this.isDone.get());
System.out.println(Calendar.getInstance().getTime().toString()
+ " start() END");
}
}
SampleTask.java
import java.util.Calendar;
public class SampleTask implements Runnable {
private ICallback callback;
SampleTask(ICallback callback) {
this.callback = callback;
}
@Override
public void run() {
System.out.println(Calendar.getInstance().getTime().toString()
+ " SampleTask START");
long sleepTime = (long) (1000L * (Math.random() * 9 + 1));
// long sleepTime = (long) (10000L * (Math.random() * 9 + 1)); // ★ここ★
try {
System.out.println("SampleTask.sleepTime = " + sleepTime);
System.out.println(Calendar.getInstance().getTime().toString()
+ " SampleTask.Sleeping ... ");
Thread.sleep(sleepTime);
this.callback.done();
} catch (Exception ex) {
ex.printStackTrace();
}
System.out.println(Calendar.getInstance().getTime().toString()
+ " SampleTask END");
}
}
出力結果1
Thu Nov 20 23:49:46 JST 2014 START
Thu Nov 20 23:49:46 JST 2014 start() START
Thu Nov 20 23:49:46 JST 2014 SampleTask START
SampleTask.sleepTime = 9311
Thu Nov 20 23:49:46 JST 2014 SampleTask.Sleeping ...
Thu Nov 20 23:49:55 JST 2014 SampleTask END
Thu Nov 20 23:49:55 JST 2014 Done
Thu Nov 20 23:49:55 JST 2014 Normal Done? : true
Thu Nov 20 23:49:55 JST 2014 start() END
Thu Nov 20 23:49:55 JST 2014 END
出力結果2
SampleTask.java の「★ここ★」の行のコメントアウトし、上の行をコメントした場合
Thu Nov 20 23:55:46 JST 2014 START
Thu Nov 20 23:55:46 JST 2014 start() START
Thu Nov 20 23:55:46 JST 2014 SampleTask START
SampleTask.sleepTime = 34161
Thu Nov 20 23:55:46 JST 2014 SampleTask.Sleeping ...
Thu Nov 20 23:55:56 JST 2014 Normal Done? : false // 処理(Task)が完了できず。
Thu Nov 20 23:55:56 JST 2014 start() END
Thu Nov 20 23:55:56 JST 2014 END
Thu Nov 20 23:56:20 JST 2014 SampleTask END