91
Использование обратных вызовов
для реакции на действия
Самый прямолинейный способ передачи сообщения в Java и Android – обраще
-
ние к обратному вызову. Обратный вызов – это экземпляр объекта, созданный
исключительно для получения результата и его обработки, и часто это простой
анонимный экземпляр функционального интерфейса. Например:
Java
public class Callbacks {
public static void requestData(Callback callback) {
// выполнить некоторый ввод/вывод с файлом или сетевым соединением...
Object data = // результат операции
if (data != null && callback != null) {
callback.onSuccess(data);
}
}
public interface Callback {
void onSuccess(Object data);
}
}
Kotlin
object Callbacks {
fun requestData(callback: (data: Any?) > Unit) {
// выполнить некоторый ввод/вывод с файлом или сетевым соединением...
val data = Any()
callback(data)
}
}
Какой-то другой код может вызвать его, используя интерфейс
Callback
, лямб
-
да-выражение или ссылку на метод:
В лямбда-выражении в Java и затем в Kotlin
Callbacks.requestData(data -> Log.d("MyApp", "result received: " + data));
Callbacks.requestData { data-> Log.d("MyApp", "result received: $data") }
Ссылка на метод в Java и затем в Kotlin
private void handleResult(Object data) {
Log.d("MyApp", "result received: " + data);
}
...
Callbacks.requestData(this::handleResult);
private fun handleResult(data: Any?) {
Log.d("MyApp", "result received: $data")
}
...
Callbacks.requestData(::handleResult)
92
Передача сообщений
Интерфейс, только в Java
Callbacks.requestData(new Callback(Object data) {
Log.d("MyApp", "result received: " + data);
});
В Kotlin нет прямого эквивалента, потому что вместо интерфейсов парамет-
ры функции определяются как лямбда-выражения. Вместо этого используйте
синтаксис лямбда-выражений и обязательно загляните в описание функций
высшего порядка в документации Kotlin (
https://oreil.ly/lr8Ja
).
Как видите, обратные вызовы предлагают довольно прямолинейный способ
определения реакции программы на результат операции.
Вот тривиальный, но вполне рабочий пример. На этот раз мы используем об
-
ратный вызов для обработки успешного и неудачного выполнения операции:
Java
public class FiftyFifty {
public static void play(SuccessHandler successHandler, FailureHandler failureHandler) {
float random = new Random().nextFloat();
if (random >= 0.5f) {
successHandler.onSuccess();
} else {
failureHandler.onFailure();
}
}
public interface SuccessHandler {
void onSuccess();
}
public interface FailureHandler {
void onFailure();
}
}
Kotlin
fun playFiftyFifty(success: () -> Unit, failure: () -> Unit) {
val random = Random().nextFloat()
if (random >= 0.5f) {
success()
} else {
failure()
}
}
Вот как можно их использовать:
Java
public class PlayFiftyFifty {
public PlayFiftyFifty() {
FiftyFifty.play(this::onSuccess, this::onFailure);
}
Android
93
private void onSuccess() {
Log.d("FiftyFifty", "We won!");
}
private void onFailure() {
Log.d("FiftyFifty", "We lost :(");
}
}
Kotlin
class PlayFiftyFifty {
init {
playFifityFifty({ this.onSuccess() }, { this.onFailure() })
}
private fun onSuccess() {
Log.d("FiftyFifty", "We won!")
}
private fun onFailure() {
Log.d("FiftyFifty", "We lost :(")
}
}
А так можно вызвать этот код:
Java
new PlayFiftyFifty();
Kotlin
PlayFiftyFifty()
Более практичным примером может служить выполнение множества опе
-
раций, способных генерировать исключения, например сетевых запросов, тре
-
бующих аутентификации и возвращающих ответы JSON, которые необходи
-
мо проанализировать, а затем сохранить результаты на локальный диск или
в базу данных. У вас может быть экземпляр
Ac ti vi ty
, выполняющий всю эту
логику, и вам нужна общая логика обработки сбоев (например, показывающая
пользователю
Snackbar
с описанием сбоя и не прерывающая работу пользовате
-
ля). В таком случае вам может пригодиться, например, такой класс:
Java
public class Safely {
public static void handle(Attempter attempter, SuccessHandler successHandler,
ErrorHandler errorHandler) {
try {
attempter.attempt();
successHandler.onSuccess();
} catch (Exception e) {
if (errorHandler != null) {
errorHandler.onException(e);
}
94
Передача сообщений
}
}
public interface Attempter {
void attempt();
}
public interface SuccessHandler {
void onSuccess();
}
public interface ErrorHandler {
void onException(Exception e);
}
}
Kotlin
object Safely {
fun handle(attempter: () -> Unit, success: () -> Unit, error: (e: Exception) -> Unit) {
try {
attempter()
success()
} catch (e: Exception) {
error(e)
}
}
}
Вот как можно использовать эти утилиты в контроллере:
Java
public class MainAc ti vi ty extends Ac ti vi ty {
@Override
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
Safely.handle(this::fetchAndSave, this::handleSuccess, this::handleFailure);
}
private void fetchAndSave() throws Exception {
// представьте, что этот метод устанавливает HTTP-соединение,
// передает учетные данные, запрашивает ответ в формате JSON,
// анализирует этот ответ и записывает получившиеся значения
// в локальную базу данных SQLIte
}
private void handleSuccess() {
// здесь можно организовать вывод сообщения о благополучном
// получении и сохранении данных;
// но пока мы просто запишем сообщение в журнал ;)
Log.d("MyApp", "just fetched and saved some data!");
}
private void handleFailure(Exception e) {
// здесь можно организовать вывод Snackbar с описанием
// возникшей проблемы или дать возможность повторить
Android
95
// попытку...
Log.d("MyApp", "Oops! Something went wrong: " + e.getMessage());
}
}
Kotlin
class MyAc ti vi ty : Ac ti vi ty() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Safely.handle(::fetchAndSave, ::handleSuccess, ::handleFailure)
}
@Throws(Exception::class)
private fun fetchAndSave() {
// представьте, что этот метод устанавливает HTTP-соединение,
// передает учетные данные, запрашивает ответ в формате JSON,
// анализирует этот ответ и записывает получившиеся значения
// в локальную базу данных SQLIte
}
private fun handleSuccess() {
// здесь можно организовать вывод сообщения о благополучном
// получении и сохранении данных;
// но пока мы просто запишем сообщение в журнал ;)
Log.d("MyApp", "just fetched and saved some data!")
}
private fun handleFailure(e: Exception) {
// здесь можно организовать вывод Snackbar с описанием
// возникшей проблемы или дать возможность повторить
// попытку...
Log.d("MyApp", "Oops! Something went wrong: $e.getMessage()")
}
}
Это были основы использования обратных вызовов! Это довольно простой
шаблон, и вы наверняка уже видели или даже использовали нечто подобное.
Do'stlaringiz bilan baham: |