AsyncTask vs RX - väikese kasutusega juhul

Hiljuti tegelesin ülesandega, kus mul oli vaja järjestikku sünkroonida 12 võrgutaotlust. RESTful JSON api taotlusi üksteise järel.

Töötasin spetsiaalselt kaamerast valikute taotlemisel, mis võõrustas kohalikku API-d, millega teie Androidi seade saaks WiFi kaudu ühenduse luua. API tagastab olemasolevad valikud ja nende väärtuste valimise.

Paindlik API - see võimaldas teil ühe päringuga korraga küsida mitmete võimaluste saadavuse kohta. Mul oli 12 huvi, mis mind huvitasid, särituse sätted ja ava ning sellised võimalused.

Ainus probleem oli juhul, kui mõni suvand polnud saadaval, tagastati kaamera API-le vastusena 404. Ja isegi siis, kui palusin mitut! Nii et kui 12-st vaid üks võimalus oleks puudu, saaksite 404-d ja teisest ei tea midagi. Noh, see on kasutu, pidin minema üle iga variandi taotlemisele ükshaaval.

Ma kasutasin kõiki neid võimalusi ja panin need RecyclerView'sse, et kasutaja saaks oma keelt kasutades oma seadeid valida.

Olen varem RX-i, eriti RXJava2, kasutanud rakendustes, mille kallal olen töötanud. Kuid mul polnud veel olnud võimalust seda oma igapäevases töös kasutada, et teha koostööd ettevõtte infotelefoni tööga.

Raamatukogude viimine minu leitud ettevõtte koodide baasi võib olla keerukam kui käivitamise või vabakutselised olukorrad. See ei tähenda, et teegid pole suured ega põhjusta probleeme. See tähendab, et otsuste tegemisse on kaasatud palju inimesi ja peate oskama erinevaid kodeerimisviise müüa.

Ma pole võib-olla veel kõige parem ideede müümisel, kuid üritan seda paremaks muuta!

Noh, siin on mul suurepärane näide olukorrast, kus RX-i olemasolu muudaks nende 12 taotluse täitmise minu jaoks lihtsamaks ja hooldatavamaks.

Tavaliselt kasutasime oma taustatööks AsyncTasksi, nagu seda on pikka aega selles rakenduses tehtud. Legacyl on kogu elu, kui olete tehnoloogia kasuks otsustanud, järgib see mõnda aega seda rakendust. Veel üks põhjus, miks neid otsuseid ei tehta kergekäeliselt.

Mulle meeldib mulle pigem uute asjade proovimine ja eesrindlik püsimine.

Veelgi parem, mis viis mind niikaugele, et ma võin tegelikult teha võrdluse ja RX-i ja AsyncTaski näite, oli asjaolu, et meie kasutatav kolmanda osapoole raamatukogu oli sõltuvuses RXJava versioonist 1.

Madal ja vaata, kogu selle aja oli see meie koodialuses istunud ja ootas kasutamist.

Nii et mina koos oma töökaaslastega kinnitasime eesmärgi teha võrdlusalus, et testida selle ülesande erinevust RX-i ja AsyncTaski kasutamise vahel.

Selgub, et ajastus on absoluutselt tühine! Loodetavasti hajutab see müüte, et väikeste taustaülesannete korral on AsyncTaski kasutamine aeglane. Mulle räägitakse sellest üsna regulaarselt mitmest allikast. Huvitav, mida ma leiaksin, kui teeksin suuremaid katseid.

Tegin väikese proovikomplekti. Jooksin oma tegevust mõlema lahendusega 6 korda ja seda ma sain:

RX:
11–17 08: 59: 00.086 12 RX Valikute taotluste lõpuleviimine: 3863ms
11–17 08: 59: 20.018 12 RX Valikute taotluste lõpuleviimine: 3816ms
11–17 08: 59: 39.143 12 RX Valikute taotluste lõpuleviimine: 3628 ms
11–17 08: 59: 57.367 12 RX Valikute taotluste lõpuleviimine: 3561ms
11–17 09: 00: 15.758 12 RX Valikute taotluste lõpuleviimine: 3713ms
11–17 09: 00: 39.129 12 RX Valikute taotluste lõpuleviimine: 3612 ms

Minu RX-lahenduse keskmine tööaeg 3698,83ms.

ATAsync:
11–17 08: 54: 49.277 12 Valikute taotlused on lõpetatud: 4085 ms
11–17 08: 55: 37.718 12 Valikute taotlused on lõpule viidud: 3980 ms
11–17 08: 55: 59.819 12 Valikute taotlused on lõpule viidud: 3925 ms
11–17 08: 56: 20.861 12 Valikute taotlused on lõpetatud: 3736 ms
11–17 08: 56: 41.438 12 Valikute taotlused on lõpule viidud: 3549 ms
11–17 08: 57: 01.110 12 Valikute taotlused on lõpule viidud: 3833ms

Minu AsyncTaski lahenduse keskmine tööaeg 3851,33 ms.

RX-i kasutamine muudab minu arvates käitusaegade vahel peaaegu erinevust. Kestus moodustab tegelikult selle taustülesande sees toimingu, mida proovite arvutada.

Mida RX teile annab, on hooldatavus. Teie koodi on palju lihtsam ajakohastada, see on vähem veatu. Võite oma lahenduse loogiliselt välja kirjutada samas järjestuses, milles see käivitatakse. See on tohutu boonus koodi loogiliseks rühmitamiseks külma hüppamise korral.

Kuigi AsyncTasksi kasutamine on endiselt ok ja kõik saavad teha seda, mida tavaliselt teevad, läheb RX-i tutvustamine kaugemale ainult taustaülesannetest. Saate maailma uute võimaluste ja võimsate viisidega, kuidas saate oma töövoogu ja toiminguid funktsionaalselt juhtida. RX-iga saate teha palju paljusid asju, mida te ei saa AysncTasksiga teha.

Vaadake ainult lisatööd, mida pean tegema, et oma AsyncTaski versioon tööle saada. Olen koodi kustutanud, et mitte näidata midagi tundlikku ettevõttele. See on pilk minu tegelikust koodist.

AsyncTaski versioon:

avaliku klassi valikudCameraRequester rakendab IOptionRepository {
    ATAsyncTask currentTask;
    tõeväärtus on tühistatud;
    lõplik HttpConnectori pistik;
    privaatne pikk algusaeg;
    avalik ValikudCameraRequester (String ipAddress) {
        this.connector = uus HttpConnector (ipAddress);
    }
    avalik tühine tühistamine () {
        isCanceled = true;
        if (currentTask! = null) {
            currentTask.cancel (tõene);
            currentTask = null;
        }
    }
    avalik void getOptions (tagasihelistamise tagasihelistamine) {
        if (isCanceled) naaseb;
        startTime = System.currentTimeMillis ();
        Log.i (MyLog.TAG, "Valikute taotlusi alustati");
        Iteraatori  iteraator =
            CameraOption.getAllPossibleOptions (). Iteraator ();
        requestOption (iteraator, tagasihelistamine);
    }
    void requestOption (lõpliku iteraatori  iteraator,
                       viimane tagasihelistamine) {
        if (! iterator.hasNext ()) {
            viimane pikk aeg = System.currentTimeMillis ();
            Log.i (MyLog.TAG, "Taotluste valikud on lõpetatud:" +
                    (System.currentTimeMillis () - startTime) +
                    "Prl");
            tagasi;
        }
        lõplik CameraOption variant = iterator.next ();
        lõplik AsyncTask  task =
                uus AsyncTask  () {
                    Kaamera valimine doInBackground (V ..) {
                        JSONObjekti tulemus =
                            connector.getOption (variant.getName ());
                        if (tulemus == null) {
                            tagasi null;
                        } veel {
                            // Tehke mõned tööd JSONObject'iga
                        }
                        tagastamise võimalus;
                    }
                    void onPostExecute (suvand CameraOption) {
                        OptionsCameraRequester.this.currentTask =
                            null;
                        if (võimalus! = null) {
                            tagasihelistamine.onOptimisvõimalus (valikuline);
                        }
                        if (! on tühistatud) {
                            requestOption (iteraator, tagasihelistamine);
                        }
                    }
                };
        ülesanne.execute ();
        currentTask = ülesanne;
    }
}

RX-versioon:

avaliku klassi valikudCameraRequester rakendab IOptionRepository {
    lõplik HttpConnectori pistik;
    Tellimus getOptionsSubscription;
    avalik ValikudCameraRequester (String ipAddress) {
        this.connector = uus HttpConnector (ipAddress);
    }
    avalik tühine tühistamine () {
        if (getOptionsSubscription! = null) {
            getOptionsSubscription.unsubscribe ();
            getOptionsSubscription = null;
        }
    }
    // Kasutan tagasihelistamist, et saaksin samast süsteemist kinni pidada
    // liides ja hoidke selles olevat RX-koodi
    // klass.

    avalik void getOptions (lõplik tagasihelistamine) {
        viimane pikk aeg = System.currentTimeMillis ();
        Logi.i (MyLog.TAG, "Suvandite vastuste päringud on käivitatud");
        getOptionsSubscription =
        Observable.from (CameraOption.getAllPossibleOptions ())
            // Taotlege iga võimalust kaamerast
            .map (uus Func1  () {
                    
                avalik CameraOption-kõne (võimalus CameraOption) {
                    JSONObjekti objekt =
                        connector.getOption (variant.getName ());
                    if (objekt == null) {
                        cameraOption.setAvable (vale);
                    } veel {
                        // Alustage valiku JSONObject abil
                    }
                    tagasi kaamera valimine;
               }
            })
            // Filtreerige suvandid, mida ei toetata
            .filter (uus Func1  () {
                    
                avalik loogiline kõne (CameraOption cameraOption) {
                    return cameraOption.isAvaila ();
                }
            })
            // Deklareerige, et niidid on tehtud ja vastu võetud
            .observeOn (AndroidSchedulers.mainThread ())
            .subscribeOn (Planeerijad.newThread ())
            // Minge iga variant ära, kuna see on valmis
            .subscribe (uus tellija  () {
         
                avalik tühine on lõpetatud () {
                   getOptionsSubscription = null;
                   Log.i (MyLog.TAG, "RX päringud valmis:" +
                        (System.currentTimeMillis () - aeg) + "ms");
                }
                public void onError (loovutatav e) {
                   MyLog.eLogErrorAndReport (MyLog.TAG, e);
                   tagasihelistamine.onError ();
                }
                avalik tühine onNext (CameraOption cameraOption) {
                    tagasihelistamine.onOptimisvõimalus (kaamera valimine);
                }
           });
    }
}

Ehkki RX-kood näeb pikemat välja, pole lootust iteraatori haldamisega enam loota. Lõimede vahetamise rekursiivne funktsioonikõne ei katke. Ülesannete tühistamise jälgimiseks pole tõeväärtust. Kõik on kirjutatud täitmise järjekorras.