Исходный контекст
Вернувшись при разработке приложений для Android / Java, я создал простой класс, который позволял бы мне синхронизировать такты вместо использования логики очереди и / или шаблона обратных вызовов.
Действительно, это позволило бы мне выполнять ряд задач линейным образом (при взгляде на код).В некоторых случаях это более полезно и намного удобнее в обслуживании и отладке.
Причина этого заключается в том, что, когда мне нужно работать с API-интерфейсами, которые предоставляют асинхронную логику с обратными вызовами для уведомления о результате или сбое,такие как Android Bluetooth LE API или Glide (чтобы назвать несколько), я хочу иметь возможность синхронизировать мой код при использовании асинхронных API.
Вот что я придумал:
Lock.java
import android.support.annotation.Nullable;
import android.util.Log;
/**
* This class is a small helper to provide feedback on lock wait and notify process
*/
public final class Lock<T> {
private final static String TAG = Lock.class.getSimpleName();
private T result = null;
private boolean isWaiting = false;
private boolean wasNotified = false;
/**
* Releases the lock on this instance to notify the thread that was waiting and to provide the result
* @param result the result of the asynchronous operation that was waited
*/
public synchronized final void setResultAndNotify(@Nullable final T result) {
this.result = result;
if (this.isWaiting) {
synchronized (this) {
this.notify();
}
} else
this.wasNotified = true;
}
/**
* This method locks on the current instance and wait for the duration specified in milliseconds
* @param timeout the duration, in milliseconds, the thread will wait if it does not get notify
*/
@Nullable
public synchronized final T waitAndGetResult(final long timeout) {
if (this.wasNotified) { // it might happen that the notify was performed even before the wait started!
this.wasNotified = false;
return this.result;
}
try {
synchronized (this) {
this.isWaiting = true;
if (timeout < 0) {
this.wait();
} else {
this.wait(timeout);
}
this.isWaiting = false;
return this.result;
}
} catch (final InterruptedException e) {
Log.e(TAG, "Failed to wait, Thread got interrupted -> " + e.getMessage());
Log.getStackTraceString(e);
}
return null;
}
/**
* This method locks on the current instance and wait as long necessary (no timeout)
*/
@Nullable
public synchronized final T waitAndGetResult() {
return waitAndGetResult(-1)
}
/**
* Tells whether this instance is currently waiting or not
* @return <code>true</code> if waiting, <code>false</code> otherwise
*/
public final boolean isWaiting() {
return this.isWaiting;
}
}
Я уверен, что это не лучшее решение, но, в некотором смысле, оно помогает в некоторых редких случаях, когда notify
может быть вызван раньше, чем wait
.Я думаю, что мог бы использовать AtomicBoolean
, но пока проблем нет.
Вот как это можно использовать:
public final boolean performLongRunningTasks() {
this.asynchronousTaskA.startAsync();
final Result taskAResult = this.taskALock.waitAndGetResult(); // wait forever
this.asynchronousTaskB.startAsync();
final Result taskBResult = this.taskBLock.waitAndGetResult(5000L); // wait up to 5 seconds
return taskAResult != null && taskBResult != null;
}
// callback
private void onTaskACompleted(@Nullable Result result) {
this.taskALock.setResultAndNotify(result);
}
// callback
private void onTaskBCompleted(@Nullable Result result) {
this.taskBLock.setResultAndNotify(result);
}
Полезно, верно?Конечно, ожидание вечности не является идеальным.
Миграция в Kotlin
Сейчас я развиваюсь в Kotlin и я начал читать о параллелизме и сопрограммах.В какой-то момент была одна статья, конвертировавшая шаблон синхронизации / блокировки / ожидания / уведомления Java в Kotlin, который помог мне перевести мой оригинальный класс в такой:
Lock.kt
import android.util.Log
class Lock<T> {
private var result : T? = null
private var isWaiting = false
private var wasNotified = false
private val lock = Object() // Java object, Urgh...
fun setResultAndNotify(value: T?) {
result = value
if (isWaiting) {
synchronized(lock) {
lock.notify()
}
} else {
wasNotified = true
}
}
fun waitAndGetResult(timeout: Long) : T? {
if (wasNotified) {
wasNotified = false
return result
}
try {
synchronized(lock) {
isWaiting = true
if (timeout < 0) lock.wait() else lock.wait(timeout)
isWaiting = false
}
} catch (e: InterruptedException) {
Log.getStackTraceString(e)
}
return this.result
}
fun waitAndGetResult() = waitAndGetResult(-1)
}
Инет проблем здесь это работает ... Но это Котлин!Конечно, я мог бы сделать лучше?Каким было бы ваше решение?