Week 13

Parse.com, Retrofit

Oznamy

Previously in…

  • (slide) predtavenie aplikácie Watchman

    • NFC čítačka
    • zobrazí prečítaný NFC tag na obrazovke zariadenia
  • plán - prezenčku posielať online

    • držať historické údaje o tom, kto kedy (a kde) bol
    • držať info o tom, kto je vlastník ktorej NFC značky

Parse.com

  • (slide)

  • Populárna služba na rýchle prototypovanie backend-u.

  • Hlavné výhody a vlastnosti:

    • zdarma - je možné si ju stiahnuť alebo využiť existujúce hostingy, napr. back4app
    • NoSQL databáza
    • pripravené REST API na prístup ku údajom
    • prístup prostredníctvom HTTP protokolu alebo pomocou knižnice dostupnej na niekoľkých platformách/niekoľko jazykov
  • V rámci rýchlosti sa pohráme s priamym použitím knižnice pre prácu s touto službou dostupnou na serveroch back4app.

Parse.com on Back4App Overview

  • (slide)

  • Pre naše potreby teda použijeme inštaláciu služby Parse.com na serveroch back4app

  • Vytvorím novú aplikáciu s názvom Watchman

    • rovno spustí video ohľadom použitia

    • database browser a classes

    • vytvorím dve ďalšie classy - logs a users (trošku pokazím mennú konvenciu služby, ale použijem ich tak, ako to býva zvykom v REST službách - množné číslo podstatného mena)

    • upravím vlastnosti (stĺpce) tried:

      • logs - pridám len cardId
      • users - pridám firstName, lastName a cardId

Using Parse.com in Android

  • Aby sme nainštalovali potrebnú knižnicu, do build.gradle modulu pridáme tento riadok:

    implementation "com.github.parse-community.Parse-SDK-Android:parse:1.18.5"
  • (slide) Následne do projektu pridáme triedu App, ktorá bude rozširovať triedu Application. Toto je základná trieda celej aplikácie, ktorá obsahuje všetky ostatné komponenty, ako napr. aktivity a služby. Jej inštancia sa vytvorí ako prvá pred všetkými ostatnými.

  • V nej urobíme počiatočnú inicializáciu knižnice, ktorá sa tak stane dostupnou (a nakonfigurovanou) pre celú aplikáciu:

    public class App extends Application {
        @Override
        public void onCreate() {
            super.onCreate();
    
            Parse.initialize(new Parse.Configuration.Builder(this)
                    .applicationId("0RI2hEXtZ9Blc8hXPlzSwyc6jINfX6fimVMBG1VK")
                    .clientKey("62W46EOsHOaE1LQYy81JR2a2U9Y9Nv2wSxuEv9Kj")
                    .server("https://parseapi.back4app.com/")
                    .build()
            );
        }
    }
  • Na to, aby sme vedeli inicializovať Parse.com klienta, potrebujeme o službe poznať:

    • jej URL adresu,
    • token/kľúč aplikácie, a
    • token/kľúč klienta.
  • Tieto informácie zistíme priamo z webového rozhrania služby po kliknutí na položku v bočnom menu App Settings > Security & Keys.

Writing Logs to Parse.com

  • Začneme tým, že po úspešnom prečítaní NFC značky, resp. jej id, ho zapíšeme do služby Parse.com. Za týmto účelom sme si vytvorili triedu (endpoint) /logs. Upravíme teda implementáciu metódy onResume():

    @Override
    protected void onResume() {
        super.onResume();
    
        Intent intent = getIntent();
    
        if (intent.getAction().equals("android.nfc.action.TAG_DISCOVERED")) {
    
            Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    
            Formatter formatter = new Formatter();
            for (byte b : tag.getId()) {
                formatter.format("%02X", b);
            }
    
            final String cardId = formatter.toString();
    
            // send to parse.com
            ParseObject entity = new ParseObject("logs");
    
            entity.put("cardId", cardId.toString());
    
            // Saves the new object.
            // Notice that the SaveCallback is totally optional!
            entity.saveInBackground(new SaveCallback() {
                @Override
                public void done(ParseException e) {
                    // Here you can handle errors, if thrown. Otherwise, "e" should be null
                    if (e != null) {
                        Log.e(TAG, e.getMessage());
    
                        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
    
                        builder.setTitle("Error")
                                .setMessage(e.getLocalizedMessage())
                                .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                                    public void onClick(DialogInterface dialog, int which) {
                                        // closing the application
                                        finish();
                                    }
                                });
                        AlertDialog dialog = builder.create();
                        dialog.show();
                    }
                }
            });
    }

Reading the Name

  • Druhým rozšírením bude, že sa pokúsime miesto id značky zobraziť meno jej vlastníka, ak samozrejme vlastníka má. Za týmto účelom teda budeme potrebovať získať údaje z class-y (endpoint-u) /users na základe hodnoty atribútu cardId. Budeme teda tieto údaje filtrovať.

  • Rozšírime teda metódu onResume() a pridáme potrebný kód, ktorý potrebné údaje zo služby získa:

    // presentation
    Log.i(TAG, "looking for " + cardId);
    ParseQuery<ParseObject> query = ParseQuery.getQuery("users").whereEqualTo("cardId", cardId);
    
    // The query will search for a ParseObject, given its objectId.
    // When the query finishes running, it will invoke the GetCallback
    // with either the object, or the exception thrown
    query.getFirstInBackground(new GetCallback<ParseObject>() {
        public void done(ParseObject result, ParseException e) {
            TextView tv = findViewById(R.id.card_id);
            if (e == null) {
                if(result == null) {
                    tv.setText(cardId);
                }else{
                    tv.setText(String.format("%s %s", result.get("firstName"), result.get("lastName")));
                }
                System.out.println(result.toString());
            } else {
                // something went wrong
                Log.e(TAG, "Something went wrong");
                Log.e(TAG, e.getMessage());
                tv.setText(cardId);
            }
        }
    });
  • Ak teda všetko pracuje správne, tak v prípade, že daná NFC značka má známeho majiteľa, bude zobrazené jeho meno. V opačnom prípade bude zobrazená samotná NFC značka.

Retrofit

  • Služba sa používa jednoducho a ako som spomínal - je úplne ideálna na rýchle prototypovanie vlastných služieb/aplikácií/riešení. Čo však v prípade, ak si službu poskytujúcu REST API vytvoríte sami? Ako s ňou komunikovať? Podobne ako v prípade služby openweathermap.org? Pomocou surového HTTP klienta?

  • (slide) Samozrejme, že sa to dá aj tak. Lepšie riešenie však predstavuje napr. knižnica Retrofit.

  • V krátkosti by sa dalo povedať, že Retrofit je knižnica a HTTP klient, ktorá zmení vaše HTTP API na Java rozhranie. Okamžite po stiahnutí dát vo formáte JSON alebo XML tieto sparsuje a vráti ich v podobe POJO (Plain Old Java Object) objektov, s ktorými vieme v našej aplikácii okamžite pracovať. Pred ich použitím je však potrebné príslušné triedy vytvoriť.

  • Knižnica vie pracovať v dvoch režimoch:

    • synchrónne - tento prístup sa dá použiť vtedy, ak údaje získavame pomocou vlastnej služby (implementáciou triedy Service)
    • asynchrónne - tento prístup sa dá použiť vtedy, ak údaje získavame priamo v hlavnom vlákne (v UI) ako náhrada za triedu AsyncTask
  • Táto knižnica obyčajne patrí medzi výbery typu Top 10 Android Libraries, takže určite oplatí sa na ňu pozrieť.

Conclusion

  • Končíme

  • Úvod do vývoja chytrých aplikácií na platforme Android

  • Obrovské, ale oplatí sa tomu venovať. My sme to len lizli.

  • O rok - určite opustíme Android a pôjdeme smerom k hybridným aplikáciám.

  • Šťastné a veselé, veselého silvestra, vidíme sa počas skúškového pri odovzdávaní vašich Makačov ;)

Additional Resources