Android: problém se závislostmi

Zobrazuje se vám taková to hláška a nevíte jak problém vyřešit?

Zkuste nejprve aktualizovat všechny balíčky na nejnovější verzi. Pokud problém přetrvává, zkuste následující postup:

V root složce problémového projektu spustit příkaz:

gradlew app:dependencies > out.txt

V souboru out.txt najít řádky, které nemají šipku na nastavenou verzi SDK (u mne 28):

com.android.support:exifinterface:27.1.0
com.android.support:support-v4:26.1.0

Správně má být takhle:

com.android.support:support-v4:26.1.0 -> 28.0.0

tyto řádky se správnou verzí sdk přidat do build.gradle:

implementation 'com.android.support:exifinterface:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'

 

Android: RecyclerView

LisView je mrtvý, ať žije RecyclerView. Ano, je to opravdu tak, zapomeňte na ListView a začněte používat RecyclerView – je výkonější, flexibilnější a vyvýjí se, ListView je mrtvý.

Pojďme si udělat jednoduchou ukázku.

Do souboru build.gradle je nutné přidat závislost:

dependencies {
    implementation 'com.android.support:recyclerview-v7:28.0.0'
}

1) Přidání RecyclerView do šablony activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/my_recycler_view"
        android:scrollbars="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</android.support.constraint.ConstraintLayout>

2) Model

Vytvoříme třídu s Movie.java s gettery a settery.

package cz.vencax.recyclerview.model;

public class Movie {
    private String title, genre, year;

    public Movie() {
    }

    public Movie(String title, String genre, String year) {
        this.title = title;
        this.genre = genre;
        this.year = year;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String name) {
        this.title = name;
    }

    public String getYear() {
        return year;
    }

    public void setYear(String year) {
        this.year = year;
    }

    public String getGenre() {
        return genre;
    }

    public void setGenre(String genre) {
        this.genre = genre;
    }
}

3) Adapter

Adapter slouží pro zobrazení dat v RecyclerView stejně jako v ListView. Jsou pouze přetížené jiné metody.

Vytvořte třídu s názvem MoviesAdapter.java. Zde metoda onCreateViewHolder () použije movie_list_row.xml. V metodě onBindViewHolder () jsou nastaveny příslušné údaje o filmu (titul, žánr a rok) pro každý řádek.

package cz.vencax.recyclerview.adapter;

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.List;

import cz.vencax.recyclerview.R;
import cz.vencax.recyclerview.model.Movie;

public class MoviesAdapter extends RecyclerView.Adapter<MoviesAdapter.MyViewHolder> {

    private List<Movie> moviesList;

    public class MyViewHolder extends RecyclerView.ViewHolder {
        public TextView title, year, genre;

        public MyViewHolder(View view) {
            super(view);
            title = (TextView) view.findViewById(R.id.title);
            genre = (TextView) view.findViewById(R.id.genre);
            year = (TextView) view.findViewById(R.id.year);
        }
    }


    public MoviesAdapter(List<Movie> moviesList) {
        this.moviesList = moviesList;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.movie_list_row, parent, false);

        return new MyViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        Movie movie = moviesList.get(position);
        holder.title.setText(movie.getTitle());
        holder.genre.setText(movie.getGenre());
        holder.year.setText(movie.getYear());
    }

    @Override
    public int getItemCount() {
        return moviesList.size();
    }
}

4) Layout pro řádek v RecyclerView

Vytvořte layout xml s názvem movie_list_row.xml s níže uvedeným kódem. Tento layout vykreslí jeden řádek v RecyclerView se zobrazením názvu filmu, žánru a roku vydání.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="?android:attr/selectableItemBackground"
    android:clickable="true"
    android:focusable="true"
    android:orientation="vertical"
    android:paddingBottom="10dp"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:paddingTop="10dp">

    <TextView
        android:id="@+id/title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:textColor="#222222"
        android:textSize="16dp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/genre"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/title" />

    <TextView
        android:id="@+id/year"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:textColor="#999999" />

</RelativeLayout>

5) Activity

Nakonec upravíme MainActivity.java a provedeme níže uvedené změny. Metoda prepareMovieData () přidá vzorové data do zobrazení v seznamu.

package cz.vencax.recyclerview.activity;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;

import java.util.ArrayList;
import java.util.List;

import cz.vencax.recyclerview.R;
import cz.vencax.recyclerview.adapter.MoviesAdapter;
import cz.vencax.recyclerview.model.Movie;

public class MainActivity extends AppCompatActivity {

    private List<Movie> movieList = new ArrayList<>();
    private RecyclerView recyclerView;
    private MoviesAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = (RecyclerView) findViewById(R.id.recycler_view);

        mAdapter = new MoviesAdapter(movieList);
        RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
        recyclerView.setLayoutManager(mLayoutManager);
        recyclerView.setItemAnimator(new DefaultItemAnimator());
        recyclerView.setAdapter(mAdapter);

        prepareMovieData();
    }


    private void prepareMovieData() {
        Movie movie = new Movie("Mad Max: Fury Road", "Action & Adventure", "2015");
        movieList.add(movie);

        movie = new Movie("Inside Out", "Animation, Kids & Family", "2015");
        movieList.add(movie);

        movie = new Movie("Star Wars: Episode VII - The Force Awakens", "Action", "2015");
        movieList.add(movie);

        movie = new Movie("Shaun the Sheep", "Animation", "2015");
        movieList.add(movie);

        movie = new Movie("The Martian", "Science Fiction & Fantasy", "2015");
        movieList.add(movie);

        movie = new Movie("Mission: Impossible Rogue Nation", "Action", "2015");
        movieList.add(movie);

        movie = new Movie("Up", "Animation", "2009");
        movieList.add(movie);

        movie = new Movie("Star Trek", "Science Fiction", "2009");
        movieList.add(movie);

        movie = new Movie("The LEGO Movie", "Animation", "2014");
        movieList.add(movie);

        movie = new Movie("Iron Man", "Action & Adventure", "2008");
        movieList.add(movie);

        movie = new Movie("Aliens", "Science Fiction", "1986");
        movieList.add(movie);

        movie = new Movie("Chicken Run", "Animation", "2000");
        movieList.add(movie);

        movie = new Movie("Back to the Future", "Science Fiction", "1985");
        movieList.add(movie);

        movie = new Movie("Raiders of the Lost Ark", "Action & Adventure", "1981");
        movieList.add(movie);

        movie = new Movie("Goldfinger", "Action & Adventure", "1965");
        movieList.add(movie);

        movie = new Movie("Guardians of the Galaxy", "Science Fiction & Fantasy", "2014");
        movieList.add(movie);

        mAdapter.notifyDataSetChanged();
    }
}

A máme hotovo 🙂

6) Přidání oddělovače RecyclerView

recyclerView.addItemDecoration(new DividerItemDecoration(this, LinearLayoutManager.VERTICAL));

recyclerView.setAdapter(mAdapter);

7) Přidání click listeneru

RecyclerView také nemá metodu OnItemClickListener pro kliknutí na položku. Musíte napsat svou vlastní třídu implementující RecyclerView.OnItemTouchListener. Vytvořte třídu s názvem RecyclerTouchListener.java:

package cz.vencax.recyclerview.utils;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;

public class RecyclerTouchListener implements RecyclerView.OnItemTouchListener {

    private GestureDetector gestureDetector;
    private ClickListener clickListener;

    public RecyclerTouchListener(Context context, final RecyclerView recyclerView, final ClickListener clickListener) {
        this.clickListener = clickListener;
        gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                return true;
            }

            @Override
            public void onLongPress(MotionEvent e) {
                View child = recyclerView.findChildViewUnder(e.getX(), e.getY());
                if (child != null && clickListener != null) {
                    clickListener.onLongClick(child, recyclerView.getChildPosition(child));
                }
            }
        });
    }

    @Override
    public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {

        View child = rv.findChildViewUnder(e.getX(), e.getY());
        if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) {
            clickListener.onClick(child, rv.getChildPosition(child));
        }
        return false;
    }

    @Override
    public void onTouchEvent(RecyclerView rv, MotionEvent e) {
    }

    @Override
    public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {

    }

    public interface ClickListener {
        void onClick(View view, int position);

        void onLongClick(View view, int position);
    }
}

Nakonec přidáme tento objekt jako listener k RecyclerView v activity:

recyclerView.addOnItemTouchListener(new RecyclerTouchListener(getApplicationContext(), recyclerView, new RecyclerTouchListener.ClickListener() {
            @Override
            public void onClick(View view, int position) {
                Movie movie = movieList.get(position);
                Toast.makeText(getApplicationContext(), movie.getTitle() + " is selected!", Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onLongClick(View view, int position) {

            }
        }));

 

Windows 10 – klávesové zkratky

Vylepšená schránka – stiskněte místo obvyklého Ctrl+V pro vložení zkratku Win+V. Uvidíte náhled schránky s historií. Tu můžete synchronizovat i na více zařízení.

Rychlé vkládání emoji s vyhledáváním – stiskněte během psaní Win + . (tečka) a vyjede nabídka s emoji, ve které můžete ihned začít vyhledávat požadovaný znak, a to v češtině. Pokud přispíváte na sociální sítě, toto se zatraceně hodí.

Rychlé pořízení výřezu – stiskněte Win+Shift+S a můžete vyříznout část obrazovky. Ten můžete okamžitě někam vložit, případně lze kliknutím na notifikaci v oznamovací oblasti otevřít zachycený snímek ve skicáku a doplnit ho poznámkami.

Android: Cleartext HTTP traffic to downloads.bbc.co.uk not permitted

Od Android 8 (Oreo) je defaultně zakázáno komunikovat po http – je nutné komunikovat po https. Pokud stahujete data ze zdroje, kde nemůžete ovlivnit nasazení https, lze toto pravidlo obejít přidáním níže uvedeného kódu do vaší aplikace.

Vytvoříme soubor: res/xml/network_security_config.xml

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>

Do AndroidManifest.xm přidáme řádek: http://android:networkSecurityConfig=“@xml/network_security_config“

<application
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:networkSecurityConfig="@xml/network_security_config"
...

 

Debugování Retrofit požadavků

O tom, jak zprovoznit Retrofit jsme si již psali. Dnes se podíváme na to, jak získat více informací o tom, jak Retrofit funguje a jaké posílá a přijímá požadavky.

Začneme nainstalováním závislosti: com.squareup.okhttp3:logging-interceptor

implementation 'com.squareup.okhttp3:logging-interceptor:3.11.0'
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(Level.BASIC);
OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(logging)
    .build();

Kde setLevel určuje úroveň logování: NONE, BASIC, HEADERS, BODY

Tím máme základní logování zprovozněno:

10-11 12:46:10.312 15337-15697/cz.vencax.mobilapp.mwmv2 D/OkHttp: --> POST https://vencax.local/mwm/api/login
10-11 12:46:10.312 15337-15697/cz.vencax.mobilapp.mwmv2 D/OkHttp: Content-Type: application/json; charset=UTF-8
10-11 12:46:10.312 15337-15697/cz.vencax.mobilapp.mwmv2 D/OkHttp: Content-Length: 107
10-11 12:46:10.312 15337-15697/cz.vencax.mobilapp.mwmv2 D/OkHttp: Authorization: Basic xxx
10-11 12:46:10.312 15337-15697/cz.vencax.mobilapp.mwmv2 D/OkHttp: {"actualDateTime":"2018-10-11T12:46:10.2580000+02:00","phoneId":"2c3c2a5fe5a7b529","versionNumber":"1.9.0"}
10-11 12:46:10.312 15337-15697/cz.vencax.mobilapp.mwmv2 D/OkHttp: --> END POST (107-byte body)

Pokud chcete logovat svým vlastním způsobem, můžete přetížit konstruktor:

HttpLoggingInterceptor logging = new HttpLoggingInterceptor(new 
Logger() {
@Override public void log(String message) {
    Log.d(TAG, "message: ");
    }
});

 

Jak stáhnout .apk z Google Play

Klient má Android zařízení, které jsou pouze v interní síti, bez přístupu na internet. Přál by si na tyto zařízení dostat aplikace z Google Play. Jde to?

Ano, jde 🙂 Z Google Play je možné stáhnout .apk soubor, který se nainstaluje na zařízení. Existují na to nejrůznější webové stránky – ty mi ovšem většinou nefungují. Nejlepší zkušenosti mám s doplňkem do Chrome, APK Downaloader. Stačí nainstalovat do Chrome na PC, v Chrome najít aplikaci, zkopírovat URL do pluginu a stáhnout

 

Chamsys – ArtNet

ArtNet je protokol pro přenos DMX protokolu a RDP (Remote Device Management) po UDP (User Datagram Protocol) v IP (Internet Protocol). Zjednodušeně: vaše DMX nemusíte na stage táhnout několika linkami (universama) ale všechny najednou po „počítačovém síťovém“ kabelu. Na stage máte krabičku, ko které je tonto „počítačový síťový kabel“ zapojen. Z této krabičky už klasicky rozvedete jednotlivé DMX kabely do požadovaných světel.

Začínáme s ArtNet a ChamSys

Jak tedy začít? Já začal s převodníkem ArtNet – DMX 3 universe od http://www.blackout.sk. Převodník cenově vychází na 70€ a nabízí 3 universy.

Tento převodník má defaultně nastavenou IP: 192.168.0.99 a masku 255.255.255.0  Pro přihlášení do převodníku použijte tyto defaultní přístupové údaje: user: admin heslo: 12345  Není nutné ho nastavovat, po zakoupení je již vše nastaveno. Pouze mu přenastavíme IP a masku sítě na:

  • IP: 2.139.240.99
  • Maska: 255.0.0.0

Kompletní návod k ArtNet převodníku od blackout.sk

Proč jsem vybral ChamSys MagicQ PC

Pokud začínáte, vyprdněte se na Freestyler DMX a podobné programy pro začátečníky a jděte rovnou do něčeho na úrovni. Profíci s penězma valí MA lighting. Zbytek osvětlovačů, co se nechtějí učit nic nového (podle mne 🙂 ) valí Avolites – je to rider a všichni s tím umí. Já, vyzkoušel několik programů (především zdarma) ale jakmile jsem objevil ChamSys, zamiloval jsem se. Koketoval jsem i s Avolites Titan One ale nesednul mi.

Pokud nemáte peníze, pro začátek je ChamSys super v tom, že stačí ArtNet převodník a můžete používat 1 universe zcela zdarma (na ovládání světel). Nemáte žádné omezení na funkčnost. Pak až něco vyděláte, přikoupíte PC Wing Compact, pak Extra Wing Compact a s MagicQ MQ80 už máte dospělý pult bez nutnosti PC a můžete svítit vysokou osvětlovačskou ligu 🙂

Pojďme nastavit MagicQ PC na počítači

Stáhnete na PC MagicQ PC a nainstalujte. Náš ArtNet – DMX 3 universe má IP: 2.139.240.99, našemu PC kde máme nainstalovaný MagicQ PC nastavíme IP: 2.139.240.200 a ArtNet propojíme s PC síťovým kabelem.

OK, nyní spustíme MagicQ PC. Jdeme do sekce Setup -> VIEW SETTINGS -> Network kde nastavíme tyto hodnoty:

  • IP address – stejnou jako má PC (192.168.0.2)
  • Subnet mask – defaultně 255.255.255.0
  • Gateway address – není nutná ale většinou 192.168.0.1

IP adresy máme nastavené, vyzkoušíme, že se z PC dostaneme na ArtNet převodník příkazem ping 2.139.240.99

V konzoli je vidět že IP adresa 2.139.240.99 odpovídá na ping, máme správně nastaveno.

Nyní nastavíme MagicQ aby universe 1 odesílal na ArtNet 0. Jdeme do sekce Setup -> VIEW DMX I/O

Nastavili jsme pro universe 1 status na enabled a pojmenovali jsme si ho na universe1.

Adresy Artnet převodníků můžeme vidět i na obrazovce: Settings -> View status -> Art-Net Nodes. Pomůže to, pokud máte převodník a neznáte jeho adresu.

To je vše 🙂 Nyní můžete napatchovat světla a začít používat.

Android: fragment worker

Dnes se podíváme na to, jak dělat složitější operace ve fragmentu při které se bude volat metoda z activity. Zaměříme se na to, jak správně napsat kód aby aplikace nespadla při otočení displeje.

Jednoduchá ukázka jak to nedělat:

MainActivity.java – klasická activity, která má veřejnou metodu worker, která by v praxi vykonávala nějakou operaci. Nyní pro ukázku vypíše pouze text do logu.

package cz.vencax.mobilapp.dockstatesapplication;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void worker(String text) {
        Log.d("xxx", "text: " + text);
    }
}

LoginFragment.java – jednoduchý fragment s tlačítkem. Po tapnutí na tlačítko, se spustí AsyncTask, který volá metodu worker z MainActivity

package cz.vencax.mobilapp.dockstatesapplication;

import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;

public class LoginFragment extends Fragment {

    public LoginFragment() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        //return inflater.inflate(R.layout.fragment_login, container, false);
        View view = inflater.inflate(R.layout.fragment_login, container, false);

        Button button = (Button) view.findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                new LoginAndParseInspectionTask().execute();
            }
        });

        return view;
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////

    class LoginAndParseInspectionTask extends AsyncTask<String, String, Void> {
        @Override
        protected Void doInBackground(String... strings) {
            for(int i = 0; i < 1000000000; i++) {
                ((MainActivity)getActivity()).worker("run" + i);
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
    }
}

Pokud tento kód spustíte, zjistíte, že funguje. Bohužel po otočení displeje aplikace spadne….

Jak na to lépe?

Správná cesta je vytvoření static interface TaskCallbacks ve fragmentu, který implementujeme v activity. Metodu worker následně voláme přes mCallbacks.worker(„run“ + i);

MainActivity.java

package cz.vencax.mobilapp.dockstatesapplication;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity implements LoginFragment.TaskCallbacks {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public void worker(String text) {
        Log.d("xxx", "text: " + text);
    }
}

LoginFragment.java

package cz.vencax.mobilapp.dockstatesapplication;

import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;

public class LoginFragment extends Fragment {

    static interface TaskCallbacks {
        void worker(String text);
    }

    private TaskCallbacks mCallbacks;

    public LoginFragment() {
        // Required empty public constructor
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (!(context instanceof TaskCallbacks)) {
            throw new IllegalStateException("Activity must implement the TaskCallbacks interface.");
        }

        // Hold a reference to the parent Activity so we can report back the task's
        // current progress and results.
        mCallbacks = (TaskCallbacks) context;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        //return inflater.inflate(R.layout.fragment_login, container, false);
        View view = inflater.inflate(R.layout.fragment_login, container, false);

        Button button = (Button) view.findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                new LoginAndParseInspectionTask().execute();
            }
        });

        return view;
    }


    class LoginAndParseInspectionTask extends AsyncTask<String, String, Void> {
        @Override
        protected Void doInBackground(String... strings) {
            for(int i = 0; i < 1000000000; i++) {
                //((MainActivity)getActivity()).worker("run" + i);
                mCallbacks.worker("run" + i);
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
    }
}

Teď již při otáčení displeje není problém.

Retrofit: synchronní a asynchronní požadavky

Minule jsme si ukázali, jak použít Retrofit v Android studiu. Dnes se podíváme na synchronní a asynchronní požadavky.

Synchronní požadavek

Při synchronním požadavku, je na server odeslán požadavek, aplikace se „zastaví“ a čeká, dokud neobdrží odpověď od serveru.

Asynchronní požadavek

Při asynchronním požadavku se používají tzv. callbacky. Na server se odešle požadavek (stejně jako při synchronním požadavku) ale nečeká se na odpověď a kód pokračuje dál. Na odpověď ze serveru čeká callback, který provede požadovanou operaci s odpovědí od serveru. Tento callback se může zavolat kdykoliv (odpověď od serveru trvá různě dlouho).

V kódu je vidět asynchronní volání, které je shodné jako v prvním seznámení s Retrofit, přibylo synchronní volání:

package cz.vencax.retrofit;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import java.io.IOException;

import cz.vencax.retrofit.model.Book;
import cz.vencax.retrofit.model.Books;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final Button button = findViewById(R.id.buttonAsynchronous);
        button.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                getBooksFromApiAsynchronous();
            }
        });

        final Button buttonPost = findViewById(R.id.buttonSynchronous);
        buttonPost.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                try {
                    getBooksFromApiSynchronous();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Asynchronous Retrofit request
     */
    private void getBooksFromApiAsynchronous() {

        Log.d("xxx", "getBooksFromApiAsynchronous");

        BookService bookService = RetrofitClient.getClient().create(BookService.class);
        Call<Books> books = bookService.getAllBooks();
        books.enqueue(new Callback<Books>() {

            @Override
            public void onResponse(Call<Books> call, Response<Books> response) {
                Log.d("xxx", "Asynchronous onResponse: " + response);
                if (response.isSuccessful()) {
                    Log.d("xxx", "Asynchronous response.isSuccessful()" + response.body());
                    Books books = response.body();

                    Log.d("xxx", "Asynchronous response.isSuccessful(), books count: " + books.getBooks().size());
                    for (Book book : books.getBooks()) {
                        Log.d("xxx", "Asynchronous Book: " + book.getTitle() + ", " + book.getAuthor());
                    }
                } else {
                    Log.d("xxx", "Asynchronous response NOT isSuccessful()" + response.errorBody().source());
                }
            }

            @Override
            public void onFailure(Call<Books> call, Throwable t) {
                Log.d("xxx", "Asynchronous onFailure: " + t);
            }
        });
    }

    /**
     * Synchronous Retrofit request
     */
    private void getBooksFromApiSynchronous() throws IOException {

        Log.d("xxx", "getBooksFromApiSynchronous");

        BookService bookService = RetrofitClient.getClient().create(BookService.class);
        Call<Books> call = bookService.getAllBooks();
        Books books = call.execute().body();

        Log.d("xxx", "Synchronous response.isSuccessful(), books count: " + books.getBooks().size());
        for (Book book : books.getBooks()) {
            Log.d("xxx", "Synchronous Book: " + book.getTitle() + ", " + book.getAuthor());
        }
    }
}