AsyncTask vs. RX - В случай на малка употреба

Наскоро работех по задача, при която трябваше да синхронизирам 12 мрежови заявки последователно. RESTful JSON иска api, едно след друго.

Работих конкретно по искане на опции от камера, която хостваше локален API, с който устройството ви с Android може да се свърже през wifi. API ще върне кои опции са налични и какви стойности могат да бъдат избрани за всяка от тези опции.

Гъвкав API, той ви позволява да питате за наличието на множество опции едновременно с едно искане. Имах 12 опции, които ме интересуваха, настройки на експозицията и бленда и такива опции.

Единственият проблем беше, ако не беше налична опция API на камерата върна 404 като отговор. И дори когато поисках множество! Така че ако липсваше само една опция от 12-те, ще получите 404 и не знаете нищо за другите 11. Е, това е безполезно, трябваше да премина към заявяването на всяка опция, една по една.

Взех всяка от тези опции и ги въведох в RecyclerView, за да може потребителят да избере настройките им чрез центрофуга.

Преди съм използвал RX, по-специално RXJava2, в приложения, върху които съм работил. Но все още не съм имал възможността да го използвам в ежедневната си работа за корпоративни бюра.

Превръщането на библиотеки в база данни за корпоративен код, която открих, може да бъде по-голямо предизвикателство от ситуации при стартиране или освобождаване. Не е, че библиотеките не са големи или не създават проблеми. Това е, че има много хора, участващи в решенията и трябва да бъдете добри в продажбата на различни начини на кодиране.

Може би не съм най-добрият в продажбата на идеи досега, но се опитвам да подобря!

Е, тук имам перфектния пример за ситуация, при която наличието на RX би направило извършването на тези 12 молби по-лесно и по-поддържано за мен.

Обикновено използвахме AsyncTasks за нашата основна работа, както се правеше в това приложение от много време. Legacy има цял живот, след като решите дадена технология, тя ще следва това приложение за известно време. Друга причина тези решения не се вземат леко.

Аз съм склонен да харесвам да опитвам нови неща и да оставам на преден план.

Още по-добре това, което ме накара дотам, че всъщност мога да направя сравнение и пример за RX и AsyncTask, беше фактът, че библиотека на трети страни, която използвахме, имаше зависимост от RXJava версия 1.

Ниско и ето през цялото това време, там той седеше в нашата кодова база и чакаше да бъде използван.

И така, с одобрението на моите колеги се поставих да направя еталон за тестване на разликата за тази една задача между използването на RX и AsyncTask.

Оказва се, че времето е абсолютно нищожно! Дано това разсее всички митове, че за малки фонови задачи използването на AsyncTask е бавно. Разказвам това доста редовно от множество източници. Чудя се какво бих намерил, ако направя по-големи тестове.

Направих малък набор от проби. Пускам дейността си с двете решения 6 пъти и ето какво получих:

RX:
11–17 08: 59: 00.086 12 RX Заявки приключиха за опции: 3863ms
11–17 08: 59: 20.018 12 RX Заявките са завършени за опции: 3816ms
11–17 08: 59: 39.143 12 RX Заявките са завършени за опции: 3628ms
11–17 08: 59: 57,367 12 RX Заявките са завършени за опции: 3561ms
11–17 09: 00: 15.758 12 RX Заявки приключиха за опции: 3713ms
11–17 09: 00: 39.129 12 RX Заявките са завършени за опции: 3612ms

Средно време на изпълнение 3698.83ms за моето RX решение.

ATAsync:
11–17 08: 54: 49,277 12 Заявките са завършени за опции: 4085ms
11–17 08: 55: 37,718 12 Заявките са завършени за опции: 3980ms
11–17 08: 55: 59.819 12 Заявките са завършени за опции: 3925 ms
11–17 08: 56: 20.861 12 Завършени заявки за опции: 3736ms
11–17 08: 56: 41.438 12 Завършени заявки за опции: 3549ms
11–17 08: 57: 01.110 12 Завършени заявки за опции: 3833ms

Средно време на изпълнение 3851.33ms за моето решение AsyncTask.

Използването на RX по мое мнение прави почти никаква разлика по време на изпълнение. Наистина това, което съставя време на изпълнение е операцията вътре в тази фонова задача, която се опитвате да изчислите.

Това, което ви дава RX, е поддръжността. Кодът ви е много по-лесен за обновяване, по-малко предразположен към грешки. Можете логично да изпишете решението си в същия последователен ред, в който се изпълнява. Това е огромен бонус за логично опипване на кода, когато скачате на студено.

Въпреки че все още е добре просто да използвате AsyncTasks и всеки може да прави това, което обикновено прави, въвеждането на RX надхвърля само фоновите задачи. Получавате свят от нови възможности и мощни начини, по които можете да функционално да работите с работния си процес и операциите си. Има много много неща, които можете да направите с RX, които не можете да направите с AysncTasks.

Просто погледнете допълнителната работа, която трябва да свърша, за да задействам версията си AsyncTask. Заблудих кода, за да не покажа нещо чувствително за компанията. Това е макет на моя действителен код.

Версия на AsyncTask:

public class OptionsCameraRequester внедрява IOptionRepository {
    ATAsyncTask currentTask;
    булева е отменена;
    окончателен конектор HttpConnector;
    частен дълъг старт;
    обществени опцииCameraRequester (String ipAddress) {
        this.connector = нов HttpConnector (ipAddress);
    }
    публична невалидна анулация () {
        isCanceled = true;
        ако (currentTask! = null) {
            currentTask.cancel (истина);
            currentTask = null;
        }
    }
    публични невалидни getOptions (обратно извикване на повикване) {
        ако (isCanceled) връщане;
        startTime = System.currentTimeMillis ();
        Log.i (MyLog.TAG, „Заявки започнаха за опции“);
        Итератор  итератор =
            CameraOption.getAllPossibleOptions () итератор ().
        requestOption (итератор, обратно извикване);
    }
    void requestOption (окончателен итератор  итератор,
                       окончателен обратен сигнал за обратно извикване) {
        ако (! iterator.hasNext ()) {
            окончателно дълго време = System.currentTimeMillis ();
            Log.i (MyLog.TAG, "Заявките са готови за опции:" +
                    (System.currentTimeMillis () - startTime) +
                    "Госпожица");
            се върне;
        }
        окончателна опция CameraOption = iterator.next ();
        final AsyncTask  задача =
                нов AsyncTask  () {
                    CameraOption doInBackground (V ..) {
                        JSONObject резултат =
                            connector.getOption (option.getName ());
                        ако (резултат == нула) {
                            връща нула;
                        } else {
                            // Направете някаква работа с JSONObject
                        }
                        опция за връщане;
                    }
                    void onPostExecute (опция CameraOption) {
                        OptionsCameraRequester.this.currentTask =
                            нула;
                        ако (опция! = нула) {
                            callback.onOptionAvailable (опция);
                        }
                        ако (! е отменено) {
                            requestOption (итератор, обратно извикване);
                        }
                    }
                };
        task.execute ();
        currentTask = задача;
    }
}

RX версия:

public class OptionsCameraRequester внедрява IOptionRepository {
    окончателен конектор HttpConnector;
    Абонамент getOptionsSubscription;
    обществени опцииCameraRequester (String ipAddress) {
        this.connector = нов HttpConnector (ipAddress);
    }
    публична невалидна анулация () {
        ако (getOptionsSubscription! = null) {
            getOptionsSubscription.unsubscribe ();
            getOptionsSubscription = null;
        }
    }
    // Използвам обратния разговор, за да мога да се придържам към същата система
    // интерфейс и запази RX кода, съдържащ се само до това
    // клас.

    публични невалидни getOptions (окончателно обратно извикване на повикване) {
        окончателно дълго време = System.currentTimeMillis ();
        Log.i (MyLog.TAG, "RX заявки започнаха за опции");
        getOptionsSubscription =
        Observable.from (CameraOption.getAllPossibleOptions ())
            // Поискайте всяка опция от камера
            .map (нов Func1  () {
                    
                обществено обаждане на CameraOption (опция CameraOption) {
                    JSONObject обект =
                        connector.getOption (option.getName ());
                    ако (обект == нула) {
                        cameraOption.setAvailable (фалшива);
                    } else {
                        // Работете с JSONObject to init опция
                    }
                    опция за връщане;
               }
            })
            // Филтрирайте опциите, които не се поддържат
            .filter (нов Func1  () {
                    
                публичен булев разговор (CameraOption cameraOption) {
                    връщане cameraOption.isAvailable ();
                }
            })
            // Обявяването на работа с нишки се извършва и се получава на
            .observeOn (AndroidSchedulers.mainThread ())
            .subscribeOn (Schedulers.newThread ())
            // Предайте всяка опция, както е готова
            .пишете абонамент (нов абонат  () {
         
                публична невалидност onCompleted () {
                   getOptionsSubscription = null;
                   Log.i (MyLog.TAG, "RX Заявките са завършени:" +
                        (System.currentTimeMillis () - време) + "ms");
                }
                публично void onError (Throwable e) {
                   MyLog.eLogErrorAndReport (MyLog.TAG, e);
                   callback.onError ();
                }
                публично void onNext (CameraOption cameraOption) {
                    callback.onOptionAvailable (cameraOption);
                }
           });
    }
}

Докато RX кодът изглежда по-дълъг, вече не се разчита на управление на итератор. Няма прекъсване на рекурсивно извикване на функция, което превключва нишки. Няма булева стойност, която да проследява, че задачите са анулирани. Всичко е написано в реда, в който е изпълнено.