Retrofit is een typevaste HTTP-client voor Android en Java. Retrofit maakt het eenvoudig om verbinding te maken met een REST-webservice door de API te vertalen naar Java-interfaces. In deze zelfstudie laat ik u zien hoe u een van de meest populaire en vaak aanbevolen HTTP-bibliotheken gebruikt die beschikbaar zijn voor Android.
Deze krachtige bibliotheek maakt het gemakkelijk om JSON- of XML-gegevens te verbruiken die vervolgens worden geparseerd in gewone oude Java-objecten (POJO's). KRIJGEN
, POST
, LEGGEN
, PATCH
, en DELETE
aanvragen kunnen allemaal worden uitgevoerd.
Net als de meeste open-source software, is Retrofit gebouwd op een aantal andere krachtige bibliotheken en hulpmiddelen. Achter de schermen maakt Retrofit gebruik van OkHttp (van dezelfde ontwikkelaar) om netwerkverzoeken af te handelen. Retrofit heeft ook geen ingebouwde JSON-converter om te analyseren van JSON naar Java-objecten. In plaats daarvan wordt ondersteuning voor de volgende JSON-converterbibliotheken ter beschikking gesteld:
com.squareup.retrofit:-converter gson
com.squareup.retrofit: converter-jackson
com.squareup.retrofit:-converter moshi
Voor protocolbuffers ondersteunt Retrofit:
com.squareup.retrofit2:-converter protobuf
com.squareup.retrofit2: converter-wire
En voor XML ondersteunt Retrofit:
com.squareup.retrofit2:-converter simpleframework
Het ontwikkelen van uw eigen typevaste HTTP-bibliotheek voor interface met een REST-API kan heel lastig zijn: u moet veel functies gebruiken, zoals verbindingen maken, caching, mislukte aanvragen opnieuw proberen, threading, parsering van reacties, foutafhandeling en meer. Retrofit is daarentegen zeer goed gepland, gedocumenteerd en getest - een door oorlog geteste bibliotheek die u veel kostbare tijd en hoofdpijn bespaart.
In deze tutorial leg ik uit hoe u Retrofit 2 kunt gebruiken om netwerkverzoeken af te handelen door een eenvoudige app te maken om recente antwoorden van de Stack Exchange-API te bevragen. We zullen presteren KRIJGEN
aanvragen door een eindpunt op te geven-/ antwoorden
, toegevoegd aan de basis-URL https://api.stackexchange.com/2.2/-then de resultaten te krijgen en deze weer te geven in een weergave voor recyclers. Ik zal je ook laten zien hoe je dit doet met RxJava voor eenvoudig beheer van de stroom van toestanden en gegevens.
Start Android Studio en maak een nieuw project met een lege activiteit genaamd Hoofdactiviteit
.
Nadat u een nieuw project hebt gemaakt, declareert u de volgende afhankelijkheden in uw build.gradle
. De afhankelijkheden zijn onder meer een weergave voor recyclers, de Retrofit-bibliotheek en ook Google's Gson-bibliotheek om JSON in POJO (Plain Old Java Objects) en Retrofit's Gson-integratie om te zetten.
// Retrofit compile 'com.squareup.retrofit2: retrofit: 2.1.0' // JSON Parsing compileer 'com.google.code.gson: gson: 2.6.1' compile 'com.squareup.retrofit2: converter-gson: 2.1 .0 '// recyclerview compile' com.android.support:recyclerview-v7:25.0.1 '
Vergeet niet om het project te synchroniseren om deze bibliotheken te downloaden.
Om netwerkbewerkingen uit te voeren, moeten we de INTERNET
toestemming in het toepassingsmanifest: AndroidManifest.xml.
We gaan onze modellen automatisch maken op basis van onze JSON-responsgegevens door gebruik te maken van een zeer nuttige tool: jsonschema2pojo.
Kopieer en plak https://api.stackexchange.com/2.2/answers?order=desc&sort=activity&site=stackoverflow in de adresbalk van je browser (of gebruik Postman als je bekend bent met die tool). Druk vervolgens op invoeren-dit zal een GET-aanvraag uitvoeren op het gegeven eindpunt. Wat u als reactie ziet, is een array van JSON-objecten. De onderstaande schermafbeelding is het JSON-antwoord met behulp van Postman.
"items": ["owner": "reputation": 1, "user_id": 6540831, "user_type": "registered", "profile_image": "https://www.gravatar.com/avatar/6a468ce8a8ff42c17923a6009ab77723 ? s = 128 & d = identicon & r = PG & f = 1 "," display_name ":" bobolafrite "," link ":" http://stackoverflow.com/users/6540831/bobolafrite "," is_accepted ": false," score " : 0, "last_activity_date": 1480862271, "creation_date": 1480862271, "answer_id": 40959732, "question_id": 35931342, "owner": "reputation": 629, "user_id": 3054722, "user_type": "registered", "profile_image": "https://www.gravatar.com/avatar/0cf65651ae9a3ba2858ef0d0a7dbf900?s=128&d=identicon&r=PG&f=1", "display_name": "jeremy-denis", "link": "http : //stackoverflow.com/users/3054722/jeremy-denis "," is_accepted ": false," score ": 0," last_activity_date ": 1480862260," creation_date ": 1480862260," answer_id ": 40959731," question_id " : 40959661, ...], "has_more": true, "backoff": 10, "quota_max": 300, "quota_remaining": 241
Kopieer deze JSON-reactie vanuit uw browser of postbode.
Ga nu naar jsonschema2pojo en plak de JSON-reactie in het invoervak.
Selecteer een brontype van JSON, annotatiestijl van Gson, en haal het vinkje weg Sta extra eigenschappen toe.
Klik vervolgens op de Voorbeeld om de Java-objecten te genereren.
Je vraagt je misschien af wat de @SerializedName
en @Expose
annotaties doen in deze gegenereerde code. Maak je geen zorgen, ik zal het allemaal uitleggen!
De @SerializedName
annotatie is nodig voor Gson om de JSON-sleutels toe te wijzen aan onze velden. In overeenstemming met Java's camelCase-benamingconventie voor eigenschappen van klassenleden, wordt het niet aanbevolen underscores te gebruiken om woorden in een variabele te scheiden. @SerializedName
helpt bij de vertaling tussen de twee.
@SerializedName ("quota_remaining") @Expose private Integer quotaRemaining;
In het bovenstaande voorbeeld vertellen we Gson dat onze JSON-sleutel quota_remaining
moet worden toegewezen aan het Java-veld quotaRemaining
. Als beide waarden dezelfde waren, d.w.z. als onze JSON-sleutel was quotaRemaining
net als het Java-veld, dan zou er geen noodzaak zijn voor de @SerializedName
annotatie op het veld, omdat Gson ze automatisch toewijst.
De @Expose
annotatie geeft aan dat dit lid moet worden getoond voor JSON-serialisatie of deserialisatie.
Laten we nu teruggaan naar Android Studio. Maak een nieuw subpakket in het hoofdpakket en noem het gegevens. Binnen het nieuw gecreëerde gegevenspakket maakt u nog een pakket en geeft u het een naam model-. Maak in het modelpakket een nieuwe Java-klasse en geef deze een naam Eigenaar
. Kopieer nu het Eigenaar
klasse die is gegenereerd door jsonschema2pojo en plak deze in de Eigenaar
klas die je hebt gemaakt.
import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; publieke klasse Eigenaar @SerializedName ("reputatie") @Expose private Integer reputatie; @SerializedName ("user_id") @Expose private Integer userId; @SerializedName ("user_type") @Expose private String userType; @SerializedName ("profile_image") @Expose private String profileImage; @SerializedName ("display_name") @Expose private String displayName; @SerializedName ("link") @Expose private String link; @SerializedName ("accept_rate") @Expose private Integer acceptRate; public Integer getReputation () retourreputatie; public void setReputation (Integer reputation) this.reputation = reputation; public Integer getUserId () return userId; public void setUserId (Integer userId) this.userId = userId; public String getUserType () return userType; public void setUserType (String userType) this.userType = userType; public String getProfileImage () return profileImage; public void setProfileImage (String profileImage) this.profileImage = profileImage; public String getDisplayName () return displayName; public void setDisplayName (String displayName) this.displayName = displayName; public String getLink () retourlink; public void setLink (String link) this.link = link; public Integer getAcceptRate () return acceptRate; public void setAcceptRate (Integer acceptRate) this.acceptRate = acceptRate;
Doe hetzelfde voor een nieuwe Item
klas, gekopieerd van jsonschema2pojo.
import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; public class-item @SerializedName ("owner") @Expose owner van privé-eigenaar; @SerializedName ("is_accepted") @Expose private Boolean isAcccepted; @SerializedName ("score") @Expose private Geheel getal-score; @SerializedName ("last_activity_date") @Expose private Geheel getal integerActivityDate; @SerializedName ("creation_date") @Expose private Integer creationDate; @SerializedName ("answer_id") @Expose private Integer answerId; @SerializedName ("question_id") @Expose private Integer questionId; @SerializedName ("last_edit_date") @Expose private Integer lastEdDate; public Owner getOwner () return owner; public void setOwner (Eigenaar van de eigenaar) this.owner = owner; public Boolean getIsAccepted () return isAccepted; public void setIsAccepted (Boolean isAcccepted) this.isAcccepted = isAcccepted; public Integer getScore () return score; public void setScore (Integer score) this.score = score; public Integer getLastActivityDate () retourneer lastActivityDate; public void setLastActivityDate (Integer lastActivityDate) this.lastActivityDate = lastActivityDate; public Integer getCreationDate () return creationDate; public void setCreationDate (Integer creationDate) this.creationDate = creationDate; public Integer getAnswerId () return answerId; public void setAnswerId (Integer answerId) this.answerId = answerId; public Integer getQuestionId () return questionId; public void setQuestionId (Integer questionId) this.questionId = questionId; public Integer getLastEditDate () return lastEdDatum; public void setLastEditDate (Integer lastEditDate) this.lastEditDate = lastEditDate;
Maak ten slotte een klasse met de naamSOAnswersResponse
voor de geretourneerde StackOverflow-antwoorden. Je vindt de code voor deze klasse in jsonschema2pojo als Voorbeeld
. Zorg ervoor dat u de klassenaam bijwerkt SOAnswersResponse
waar het ook voorkomt.
import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; importeer java.util.List; public class SOAnswersResponse @SerializedName ("items") @Expose private List- items = null; @SerializedName ("has_more") @Expose private Boolean hasMore; @SerializedName ("backoff") @Expose private Integer backoff; @SerializedName ("quota_max") @Expose private Integer quotaMax; @SerializedName ("quota_remaining") @Expose private Integer quotaRemaining; openbare lijst
- getItems () items retourneren; public void setItems (lijst
- items) this.items = items; public Boolean getHasMore () return heeftMeer; public void setHasMore (Boolean hasMore) this.hasMore = hasMore; public Integer getBackoff () return backoff; public void setBackoff (Integer backoff) this.backoff = backoff; public Integer getQuotaMax () return quotaMax; public void setQuotaMax (Integer quotaMax) this.quotaMax = quotaMax; public Integer getQuotaRemaining () return quotaRemaining; public void setQuotaRemaining (Integer quotaRemaining) this.quotaRemaining = quotaRemaining;
Als u netwerkverzoeken wilt indienen bij een REST-API met Retrofit, moeten we een exemplaar maken met behulp van de Retrofit.Builder
klasse en configureer het met een basis-URL.
Maak een nieuw subpakketpakket in de gegevens
pakket en noem het afgelegen
. Nu binnen afgelegen
, een Java-klasse maken en een naam geven RetrofitClient
. Deze klasse creëert een singleton van Retrofit. Retrofit heeft een basis-URL nodig om zijn exemplaar te bouwen, dus we zullen een URL doorgeven tijdens het bellen RetrofitClient.getClient (String baseUrl)
. Deze URL wordt dan gebruikt om de instantie in regel 13 te bouwen. We specificeren ook de JSON-converter die we nodig hebben (Gson) in regel 14.
import retrofit2.Retrofit; import retrofit2.converter.gson.GsonConverterFactory; openbare klasse RetrofitClient private static Retrofit retrofit = null; public static Retrofit getClient (String baseUrl) if (retrofit == null) retrofit = new Retrofit.Builder () .baseUrl (baseUrl) .addConverterFactory (GsonConverterFactory.create ()) .build (); retrofit retourneren;
In het externe pakket maakt u een interface en belt u deze SOService
. Deze interface bevat methoden die we gaan gebruiken om HTTP-verzoeken uit te voeren zoals KRIJGEN
, POST
, LEGGEN
, PATCH
, en DELETE
. Voor deze zelfstudie gaan we een KRIJGEN
verzoek.
import com.chikeandroid.retrofittutorial.data.model.SOAnswersResponse; importeer java.util.List; import retrofit2.Call; import retrofit2.http.GET; openbare interface SOService @GET ("/ antwoorden? order = desc & sort = activity & site = stackoverloop")getAnswers (); @GET ("/ antwoorden? Order = desc & sort = activity & site = stackoverflow") Bellen getAnswers (@Query ("tagged") String-tags);
De @KRIJGEN
annotatie definieert dat expliciet KRIJGEN
aanvraag die wordt uitgevoerd zodra de methode wordt aangeroepen. Elke methode in deze interface moet een HTTP-annotatie hebben die de verzoekmethode en relatieve URL biedt. Er zijn vijf ingebouwde annotaties beschikbaar: @KRIJGEN
, @POST
, @LEGGEN
, @DELETE
, en @HOOFD
.
In de tweede methodedefinitie hebben we een queryparameter voor ons toegevoegd om de gegevens van de server te filteren. Retrofit heeft de @Query ( "key")
annotatie gebruiken in plaats van hard coderen in het eindpunt. De sleutelwaarde vertegenwoordigt de parameternaam in de URL. Het wordt door Retrofit aan de URL toegevoegd. Bijvoorbeeld als we de waarde doorgeven "Android"
als een argument voor de getAnswers (String-tags)
methode, zal de volledige URL zijn:
https://api.stackexchange.com/2.2/answers?order=desc&sort=activity&site=stackoverflow&tagged=android
Parameters van de interfacemethoden kunnen de volgende annotaties hebben:
@Pad | variabele substitutie voor het API-eindpunt |
@Query | specificeert de naam van de vraagsleutel met de waarde van de geannoteerde parameter |
@Lichaam | payload voor de POST-oproep |
@Header | specificeert de header met de waarde van de geannoteerde parameter |
Ga nu een utility class maken. We zullen het een naam geven ApiUtils
. Deze klasse heeft de basis-URL als een statische variabele en biedt ook de SOService
interface naar onze applicatie via de getSOService ()
statische methode.
public class ApiUtils public static final String BASE_URL = "https://api.stackexchange.com/2.2/"; openbare statische SOService getSOService () retourneer RetrofitClient.getClient (BASE_URL) .create (SOService.class);
Omdat de resultaten in een weergave voor recyclers worden weergegeven, hebben we een adapter nodig. In het volgende codefragment wordt het AnswersAdapter
klasse.
public class AnswersAdapter breidt RecyclerView.Adapter uitprivélijst - mItems; private context mContext; private PostItemListener mItemListener; public class ViewHolder breidt RecyclerView.ViewHolder implementeert View.OnClickListener uit public TextView titleTv; PostItemListener mItemListener; public ViewHolder (item bekijkenView, PostItemListener postItemListener) super (itemView); titleTv = (TextView) itemView.findViewById (android.R.id.text1); this.mItemListener = postItemListener; itemView.setOnClickListener (deze); @Override public void onClick (View view) Item item = getItem (getAdapterPosition ()); this.mItemListener.onPostClick (item.getAnswerId ()); notifyDataSetChanged (); public AnswersAdapter (Context-context, Lijst
- berichten, PostItemListener itemListener) mItems = berichten; mContext = context; mItemListener = itemListener; @Override public AnswersAdapter.ViewHolder onCreateViewHolder (ViewGroup-bovenliggende, int viewType) Context-context = parent.getContext (); LayoutInflater inflater = LayoutInflater.from (context); View postView = inflater.inflate (android.R.layout.simple_list_item_1, parent, false); ViewHolder viewHolder = new ViewHolder (postView, this.mItemListener); return viewHolder; @Override public void onBindViewHolder (AnswersAdapter.ViewHolder-houder, int-positie) Itemartikel = mItems.get (positie); TextView textView = holder.titleTv; textView.setText (item.getOwner () getDisplayName ().); @Override public int getItemCount () return mItems.size (); public void updateAnswers (lijst
- items) mItems = items; notifyDataSetChanged (); private Item getItem (int adapterPosition) return mItems.get (adapterPosition); openbare interface PostItemListener void onPostClick (long id);
Binnen in de onCreate ()
methode van de Hoofdactiviteit
, we initialiseren een instantie van de SOService
interface (regel 9), de weergave van de recycler en ook de adapter. Ten slotte noemen we het loadAnswers ()
methode.
private AnswersAdapter mAdapter; private RecyclerView mRecyclerView; privé SOService mService; @Override protected void onCreate (Bundle savedInstanceState) super.onCreate (savedInstanceState); setContentView (R.layout.activity_main); mService = ApiUtils.getSOService (); mRecyclerView = (RecyclerView) findViewById (R.id.rv_answers); mAdapter = new AnswersAdapter (this, new ArrayList- (0), nieuwe AnswersAdapter.PostItemListener () @Override public void onPostClick (long id) Toast.makeText (MainActivity.this, "Post id is" + id, Toast.LENGTH_SHORT) .show (); ); RecyclerView.LayoutManager layoutManager = nieuwe LinearLayoutManager (this); mRecyclerView.setLayoutManager (layoutManager); mRecyclerView.setAdapter (mAdapter); mRecyclerView.setHasFixedSize (true); RecyclerView.ItemDecoration itemDecoration = new DividerItemDecoration (this, DividerItemDecoration.VERTICAL_LIST); mRecyclerView.addItemDecoration (itemDecoration); loadAnswers ();
De loadAnswers ()
methode maakt een netwerkverzoek door te bellen enqueue ()
. Wanneer het antwoord terugkomt, helpt Retrofit ons om de JSON-reactie te analyseren op een lijst met Java-objecten. (Dit wordt mogelijk gemaakt door het gebruik van GsonConverter
.)
public void loadAnswers () mService.getAnswers (). wachtrij (nieuwe terugbelopdracht() @Override public void onResponse (Call oproep, antwoord antwoord) if (response.isSuccessful ()) mAdapter.updateAnswers (response.body (). getItems ()); Log.d ("MainActivity", "posts loaded from API"); else int statusCode = response.code (); // verwerkingsverzoekfouten afhankelijk van de statuscode @Openride openbare ongeldig onFailure (Oproep call, Throwable t) showErrorMessage (); Log.d ("MainActivity", "fout bij laden vanuit API"); );
enqueue ()
enqueue ()
verzendt asynchroon het verzoek en stelt uw app op de hoogte met een terugroep wanneer een reactie terugkomt. Aangezien dit verzoek asynchroon is, verwerkt Retrofit het op een achtergrondthread, zodat de hoofd UI-thread niet wordt geblokkeerd of verstoord.
Gebruiken enqueue ()
, je moet twee callback-methoden implementeren:
onResponse ()
onFailure ()
Slechts één van deze methoden zal worden gebruikt als antwoord op een gegeven verzoek.
onResponse ()
: aangeroepen voor een ontvangen HTTP-reactie. Deze methode vraagt om een antwoord dat correct kan worden afgehandeld, zelfs als de server een foutmelding geeft. Dus als u een statuscode van 404 of 500 krijgt, wordt deze methode nog steeds gebeld. Om de statuscode op volgorde te krijgen zodat u de situaties op basis daarvan kunt afhandelen, kunt u de methode gebruiken Reactiecode()
. U kunt ook de is succesvol()
methode om te achterhalen of de statuscode binnen het bereik 200-300 ligt, met vermelding van succes.onFailure ()
: wordt aangeroepen wanneer een netwerkuitzondering optreedt die communiceert met de server of wanneer er een onverwachte uitzondering is opgetreden bij het verwerken van het verzoek of het verwerken van het antwoord. Als u een synchrone aanvraag wilt uitvoeren, gebruikt u de execute ()
methode. Houd er rekening mee dat synchrone methoden op de hoofd- / UI-thread elke actie van een gebruiker blokkeren. Dus voer geen synchrone methoden uit op de hoofd- / UI-thread van Android! Voer ze in plaats daarvan uit op een achtergrondthread.
U kunt nu de app uitvoeren.
Als u een fan bent van RxJava, kunt u Retrofit eenvoudig implementeren met RxJava. In Retrofit 1 was het standaard geïntegreerd, maar in Retrofit 2 moet u enkele extra afhankelijkheden opnemen. Retrofit wordt geleverd met een standaard-adapter voor uitvoering telefoontje
instances. U kunt dus het uitvoeringsmechanisme van Retrofit wijzigen om RxJava op te nemen door de RxJava op te nemen CallAdapter
.
Voeg de afhankelijkheden toe.
compileer 'io.reactivex: rxjava: 1.1.6' compileer 'io.reactivex: rxandroid: 1.2.1' compileer 'com.squareup.retrofit2: adapter-rxjava: 2.1.0'
Voeg de nieuwe CallAdapter toe RxJavaCallAdapterFactory.create ()
bij het bouwen van een Retrofit-instantie.
public static Retrofit getClient (String baseUrl) if (retrofit == null) retrofit = new Retrofit.Builder () .baseUrl (baseUrl) .addCallAdapterFactory (RxJavaCallAdapterFactory.create ()) .addConverterFactory (GsonConverterFactory.create ()) .build (); retrofit retourneren;
Update nu de getAnswers ()
methoden om terug te keren waarneembaar
s:
@GET ("/ antwoorden? Order = desc & sort = activity & site = stackoverloop") WaarneembaargetAnswers (); @GET ("/ antwoorden? Order = desc & sort = activity & site = stackoverloop") Waarneembaar getAnswers (@Query ("tagged") String-tags);
Bij het indienen van de verzoeken reageert onze anonieme abonnee op de stream van de observeerbare waar in ons geval gebeurtenissen worden uitgezonden SOAnswersResponse
. De onNext
methode wordt dan aangeroepen wanneer onze abonnee een uitgezonden gebeurtenis ontvangt die vervolgens wordt doorgegeven aan onze adapter.
@Override public void loadAnswers () mService.getAnswers (). SubscribeOn (Schedulers.io ()). ObservedOn (AndroidSchedulers.mainThread ()) .subscribe (nieuwe abonnee() @Override public void onCompleted () @Override public void onError (Throwable e) @Override public void onNext (SOAnswersResponse soAnswersResponse) mAdapter.updateAnswers (soAnswersResponse.getItems ()); );
Bekijk Aan de slag met ReactiveX op Android door Ashraff Hathibelagal voor meer informatie over RxJava en RxAndroid.
In deze tutorial hebt u geleerd over Retrofit: waarom zou u het gebruiken en hoe. Ik heb ook uitgelegd hoe RxJava-integratie met Retrofit kan worden toegevoegd. In mijn volgende bericht zal ik je laten zien hoe je moet optreden POST
, LEGGEN
, en DELETE
, hoe te verzenden Form-urlencoded
gegevens en hoe u aanvragen annuleert.
Raadpleeg de officiële documentatie voor meer informatie over Retrofit. En bekijk in de tussentijd enkele van onze andere cursussen en zelfstudies over de ontwikkeling van Android-apps.