Kom i gang med Retrofit 2 HTTP-klient

Hva du skal skape

Hva er Retrofit?

Retrofit er en type sikker HTTP-klient for Android og Java. Retrofit gjør det enkelt å koble til en REST webtjeneste ved å oversette API-en til Java-grensesnitt. I denne veiledningen viser jeg deg hvordan du bruker en av de mest populære og ofte anbefalte HTTP-bibliotekene som er tilgjengelige for Android. 

Dette kraftige biblioteket gjør det enkelt å konsumere JSON- eller XML-data som deretter blir analysert i Vanlige gamle Java-objekter (POJOs). POSTSETTELAPP, og SLETT forespørsler kan alle bli utført. 

Som de fleste open source-programvare ble Retrofit bygget på toppen av noen andre kraftige biblioteker og verktøy. Bak kulissene bruker Retrofit OkHttp (fra samme utvikler) til å håndtere nettverksforespørsler. Også, Retrofit har ikke en innebygd hvilken som helst JSON-omformer for å analysere fra JSON til Java-objekter. I stedet sender den støtte til følgende JSON-konverteringsbiblioteker for å håndtere det: 

  • Gson: com.squareup.retrofit: converter-gson
  • Jackson: com.squareup.retrofit: converter-jackson
  • Moshi: com.squareup.retrofit: converter-moshi

For protokollbuffere støtter Retrofit:

  • Protobuf: com.squareup.retrofit2: converter-Protobuf
  • Metalltråd: com.squareup.retrofit2: konverter-ledning

Og for XML støtter Retrofit:

  • Enkel ramme: com.squareup.retrofit2: converter-simpleframework

Så hvorfor bruk Retrofit?

Å utvikle ditt eget type-sikre HTTP-bibliotek til grensesnitt med en REST-API kan være en reell smerte: du må håndtere mange funksjoner, for eksempel å lage tilkoblinger, cache, forsøke mislykkede forespørsler, tråder, responsanalyse, feilsøking og mer. Retrofit, derimot, er meget godt planlagt, dokumentert og testet - et kamptestet bibliotek som vil spare deg for mye dyrebar tid og hodepine.

I denne veiledningen vil jeg forklare hvordan du bruker Retrofit 2 til å håndtere nettverksforespørsler ved å bygge en enkel app for å søke etter de siste svarene fra Stack Exchange API. Vi skal utføre  forespørsler ved å spesifisere et sluttpunkt-/ svar, vedlagt til basen URL https://api.stackexchange.com/2.2/-then få resultatene og vise dem i en resirkuleringsvisning. Jeg vil også vise deg hvordan du gjør dette med RxJava for enkel styring av strømmen av tilstand og data.

1. Opprett et Android Studio-prosjekt

Brann opp Android Studio og opprett et nytt prosjekt med en tom aktivitet som heter Hoved aktivitet.

2. Deklarere avhengigheter

Når du har opprettet et nytt prosjekt, erklærer du følgende avhengigheter i din build.gradleAvhengighetene inkluderer en resirkuleringsvisning, Retrofit-biblioteket, og også Googles Gson-bibliotek for å konvertere JSON til POJO (Plain Old Java Objects) samt Retrofits Gson-integrasjon. 

// Retrofit compile 'com.squareup.retrofit2: retrofit: 2.1.0' // JSON Parsing compile '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 ' 

Ikke glem å synkronisere prosjektet for å laste ned disse bibliotekene. 

3. Legge til internettillatelse

For å utføre nettverksoperasjoner må vi inkludere INTERNETTTillatelse i søknadsmanifestet: AndroidManifest.xml.

           

4. Generere modeller automatisk

Vi skal lage våre modeller automatisk fra våre JSON-responsdata ved å utnytte et veldig nyttig verktøy: jsonschema2pojo. 

Få Sample JSON Data

Kopier og lim inn https://api.stackexchange.com/2.2/answers?order=desc&sort=activity&site=stackoverflow i nettleserens adresselinje (eller du kan bruke Postman hvis du er kjent med verktøyet). Trykk deretter på Tast inn-Dette vil utføre en GET-forespørsel på det angitte sluttpunktet. Det du vil se som svar er en rekke JSON-objekter. Skjermbildet nedenfor er JSON-responsen ved hjelp av Postman.

"elementer": ["eier": "omdømme": 1, "user_id": 6540831, "user_type": "registrert", "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 " : 0, "last_aktivitet_date": 1480862271, "creation_date": 1480862271, "answer_id": 40959732, "question_id": 35931342, "eier": "omdømme": 629, "user_id": 3054722, "user_type": "registrert", "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_aktivitet_date ": 1480862260," creation_date ": 1480862260," answer_id ": 40959731," question_id " : 40959661, ...], "has_more": true, "backoff": 10, "quota_max": 300, "quota_remaining": 241

Kopier denne JSON-responsen enten fra nettleseren din eller fra Postman. 

Kart JSON Data til Java

Nå besøker jsonschema2pojo og lim inn JSON-responsen i innskuffen.

Velg en kilde type av JSON, annoteringsstil av Gson, og fjern markeringen Tillat flere egenskaper

Klikk deretter på Forhåndsvisning knappen for å generere Java-objektene. 

Du lurer kanskje på hva @SerializedName og @Avdekke merknader gjør i denne genererte koden. Ikke bekymre deg, jeg forklarer alt!

De @SerializedName Merknad er nødvendig for Gson å kartlegge JSON-nøklene med feltene våre. I tråd med Java's kamelCase-navngivningskonvensjon for klassemedlemsegenskaper, anbefales det ikke å bruke understreker til å skille ord i en variabel. @SerializedName hjelper å oversette mellom de to.

@SerializedName ("quota_remaining") @Visstilt privat integer kvoteRemaining;

I eksemplet ovenfor forteller vi Gson at vår JSON-nøkkel quota_remaining bør kartlegges til Java-feltet quotaRemaining.  Hvis begge disse verdiene var de samme, dvs. hvis vår JSON-nøkkel var quotaRemaining akkurat som Java-feltet, så ville det ikke være behov for @SerializedName annotasjon på feltet fordi Gson ville kartlegge dem automatisk.

De @Avdekke annotasjon indikerer at dette medlemmet skal bli utsatt for JSON serialisering eller deserialisering. 

Importer datamodeller til Android Studio

La oss nå gå tilbake til Android Studio. Opprett en ny delpakke i hovedpakken og merk den data. I den nyopprettede datapakken skal du opprette en annen pakke og gi den navnet modell. Inne i modellpakken, opprett en ny Java-klasse og gi den navnet Eieren. Kopier nå Eieren klassen som ble generert av jsonschema2pojo og lim den inn i Eieren klasse du opprettet. 

importer com.google.gson.annotations.Expose; importer com.google.gson.annotations.SerializedName; offentlig klasse Eier @SerializedName ("omdømme") @Visstilt privat integer omdømme; @SerializedName ("user_id") @Viser privat integer brukerId; @SerializedName ("user_type") @Uppdater privat String userType; @SerializedName ("profile_image") @Visstille private strengprofilImage; @SerializedName ("display_name") @Visstille privat string displayName; @SerializedName ("link") @Uppfyll privat strengekobling; @SerializedName ("accept_rate") @Viser privat integer acceptereRate; offentlig integer getReputation () return reputation;  offentlig ugyldig setReputation (integer rykte) this.reputation = reputation;  offentlig integer getUserId () return userId;  Offentlig tomgang setUserId (Integer userId) this.userId = userId;  offentlig String getUserType () return userType;  offentlig tomgang setUserType (String userType) this.userType = userType;  Offentlig String getProfileImage () return profileImage;  Offentlig tomgangssettProfileImage (StringprofilImage) this.profileImage = profileImage;  Offentlig String getDisplayName () return displayName;  offentlig ugyldig setDisplayName (String displayName) this.displayName = displayName;  offentlig String getLink () returlink;  Offentlig tomt settLink (Stringlink) this.link = link;  offentlig integer getAcceptRate () return acceptRate;  offentlig tomgang setAcceptRate (Integer acceptRate) this.acceptRate = acceptRate; 

Gjør det samme for en ny Punkt klassen, kopiert fra jsonschema2pojo. 

importer com.google.gson.annotations.Expose; importer com.google.gson.annotations.SerializedName; offentlig klasse Element @SerializedName ("eier") @Verlegg privat eier eier; @SerializedName ("is_accepted") @Expose Private Boolean isAccepted; @SerializedName ("score") @Uppfyll privat integerpoengsum; @SerializedName ("last_activity_date") @Verlegg privat integer lastAktivitetDate; @SerializedName ("creation_date") @Visstilt privat integer creationDate; @SerializedName ("answer_id") @Visstilt privat integer svar; @SerializedName ("question_id") @Visstilt privat integer spørsmålId; @SerializedName ("last_edit_date") @Viser privat integer lastEditDate; offentlig eier getOwner () retur eier;  Offentlig tomt settOwner (Eier eier) this.owner = owner;  offentlige boolske getIsAccepted () return isAccepted;  Offentlig tomgang setIsAccepted (Boolean isAccepted) this.isAccepted = isAccepted;  offentlig Integer getScore () return score;  Offentlig tomt settScore (Integer score) this.score = score;  offentlig integer getLastActivityDate () return lastActivityDate;  offentlig tomt settLastActivityDate (Integer lastActivityDate) this.lastActivityDate = lastActivityDate;  offentlig integer getCreationDate () return creationDate;  offentlig tomt settCreationDate (Integer creationDate) this.creationDate = creationDate;  offentlig integer getAnswerId () return answerId;  Offentlig tomt settAnswerId (Integer svar) this.answerId = answerId;  offentlig integer getQuestionId () return questionId;  offentlig tomt settQuestionId (Integer questionId) this.questionId = questionId;  offentlig integer getLastEditDate () return lastEditDate;  offentlig tomgang setLastEditDate (Integer lastEditDate) this.lastEditDate = lastEditDate; 

Endelig opprett en klasse som heterSOAnswersResponse for de returnerte StackOverflow svarene. Du finner koden for denne klassen i jsonschema2pojo as Eksempel. Pass på at du oppdaterer klassenavnet til SOAnswersResponse uansett hvor det skjer. 

importer com.google.gson.annotations.Expose; importer com.google.gson.annotations.SerializedName; importer java.util.List; offentlig klasse SOAnswersResponse @SerializedName ("items") @Verlegg privat liste elementer = null; @SerializedName ("has_more") @Bygg privat boolsk harMore; @SerializedName ("backoff") @Uppgå privat integerbackoff; @SerializedName ("quota_max") @Viser privat integer kvoteMax; @SerializedName ("quota_remaining") @Visstilt privat integer kvoteRemaining; offentlig liste getItems () returelementer;  offentlig tomt setItems (Liste elementer) this.items = items;  offentlig boolsk getHasMore () return harMore;  offentlig tomgang setHasMore (Boolean harMore) this.hasMore = hasMore;  offentlig integer getBackoff () return backoff;  Offentlig tomt settBackoff (Integer backoff) this.backoff = backoff;  offentlig Integer getQuotaMax () return quotaMax;  offentlig tomgang setQuotaMax (Integer quotaMax) this.quotaMax = quotaMax;  offentlig integer getQuotaRemaining () return kvoteRemaining;  Offentlig ugyldig setQuotaRemaining (Integer quotaRemaining) this.quotaRemaining = quotaRemaining; 

5. Opprette Retrofit Instance

Hvis du vil utstede nettverksforespørsler til en REST-API med Retrofit, må vi opprette en forekomst ved hjelp av Retrofit.Builder klasse og konfigurere den med en base URL. 

Lag en ny delpakkepakke inne i data pakke og navn den fjern. Nå inne fjern, opprett en Java-klasse og gi den navnet RetrofitClient. Denne klassen vil skape en singleton av Retrofit. Retrofit trenger en basiswebadresse for å bygge sin forekomst, så vi sender en nettadresse når du ringer RetrofitClient.getClient (String baseUrl). Denne URL-en vil da bli brukt til å bygge forekomsten i linje 13. Vi spesifiserer også den JSON-omformer vi trenger (Gson) på linje 14. 

importere retrofit2.Retrofit; importere retrofit2.converter.gson.GsonConverterFactory; offentlig klasse RetrofitClient privat statisk Retrofit retrofit = null; offentlig statisk Retrofit getClient (String baseUrl) if (retrofit == null) retrofit = ny Retrofit.Builder () .baseUrl (baseUrl) .addConverterFactory (GsonConverterFactory.create ()) .build ();  returnere ettermontering  

6. Opprette API-grensesnittet

Inne i den eksterne pakken, opprett et grensesnitt og ring det SOService. Dette grensesnittet inneholder metoder vi skal bruke til å utføre HTTP-forespørsler som POSTSETTE, LAPP, og SLETT. For denne opplæringen skal vi utføre en  be om. 

importer com.chikeandroid.retrofittutorial.data.model.SOAnswersResponse; importer java.util.List; importere retrofit2.Call; importere retrofit2.http.GET; offentlig grensesnitt SOService @GET ("/ answers? order = desc & sort = aktivitet & site = stackoverflow") Ring fåsvar (); @GET ("/ answers? Order = desc & sort = aktivitet & site = stackoverflow") Ring getAnswers (@Query ("tagged") String tags);  

De @FÅ annotasjon definerer eksplisitt det  forespørsel som vil bli utført når metoden blir kalt. Hver metode i dette grensesnittet må ha en HTTP-annotasjon som gir forespørselsmetoden og relativ nettadresse. Det finnes fem innebygde merknader: @FÅ@POST, @SETTE@DELETE, og @HODE.

I den andre metoden definisjonen la vi til en spørringsparameter for å filtrere dataene fra serveren. Retrofit har @Query ( "nøkkel") annotasjon til bruk i stedet for hardkoding i sluttpunktet. Nøkkelverdien representerer parameternavnet i nettadressen. Det vil bli lagt til URL-adressen av Retrofit. For eksempel, hvis vi overfører verdien "Android" som et argument til getAnswers (String tags) Metoden, den fulle nettadressen vil være:

https://api.stackexchange.com/2.2/answers?order=desc&sort=activity&site=stackoverflow&tagged=android

Parametre for grensesnittmetodene kan ha følgende merknader:

@Sti variabel substitusjon for API-endepunktet
@Spørsmål Angir forespørselsnøkkelnavnet med verdien av den annoterte parameteren
@Kropp nyttelast for POST-anropet 
@Overskrift Angir toppteksten med verdien av den merkede parameteren

7. Opprette API Utils

Nå skal du lage en verktøysklasse. Vi nevner det ApiUtils. Denne klassen vil ha basen URL som en statisk variabel og også gi SOService grensesnitt til vår søknad gjennom getSOService () statisk metode.

offentlig klasse ApiUtils offentlig statisk endelig streng BASE_URL = "https://api.stackexchange.com/2.2/"; offentlig statisk SOService getSOService () returner RetrofitClient.getClient (BASE_URL) .create (SOService.class);  

8. Vis til en RecyclerView

Siden resultatene vil bli vist i en resirkuleringsvisning, trenger vi en adapter. Følgende kodestykke viser AnswersAdapter klasse.

offentlig klasse AnswersAdapter utvider RecyclerView.Adapter privat liste mItems; privat kontekst mContext; privat PostItemListener mItemListener; offentlig klasse ViewHolder utvider RecyclerView.ViewHolder implementerer View.OnClickListener public TextView titleTv; PostItemListener mItemListener; offentlig ViewHolder (Se itemView, PostItemListener postItemListener) super (itemView); titleTv = (TextView) itemView.findViewById (android.R.id.text1); this.mItemListener = postItemListener; itemView.setOnClickListener (this);  @Override public void onClick (View view) Item item = getItem (getAdapterPosition ()); this.mItemListener.onPostClick (item.getAnswerId ()); notifyDataSetChanged ();  offentlig svaradapter (kontekst kontekst, liste innlegg, PostItemListener itemListener) mItems = innlegg; mContext = kontekst; mItemListener = itemListener;  @Override public AnswersAdapter.ViewHolder onCreateViewHolder (ViewGroup foreldre, int viewType) Context context = parent.getContext (); LayoutInflater inflater = LayoutInflater.from (kontekst); Se postView = inflater.inflate (android.R.layout.simple_list_item_1, foreldre, falsk); ViewHolder viewHolder = ny ViewHolder (postView, this.mItemListener); returnere viewHolder;  @Override public void onBindViewHolder (AnswersAdapter.ViewHolder holder, int posisjon) Item item = mItems.get (posisjon); TextView textView = holder.titleTv; textView.setText (item.getOwner () getDisplayName ().);  @ Overstyr offentlig int getItemCount () return mItems.size ();  offentlig ugyldig oppdateringAnswers (Liste elementer) mItems = items; notifyDataSetChanged ();  private Item getItem (int adapterPosition) return mItems.get (adapterposisjon);  offentlig grensesnitt PostItemListener void onPostClick (long id); 

9. Utfør forespørselen

Inne i onCreate () metode av Hoved aktivitet, vi initialiserer en forekomst av SOService grensesnitt (linje 9), resirkuleringsvisning, og også adapteren. Til slutt kaller vi loadAnswers () metode. 

 private svarAdapter mAdapter; privat RecyclerView mRecyclerView; privat 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 = ny svarAdapter (denne nye ArrayList(0), nye svarAdapter.PostItemListener () @Overtrid offentlig tomgang onPostClick (lang id) Toast.makeText (MainActivity.this, "Post id er" + id, Toast.LENGTH_SHORT) .show (); ); RecyclerView.LayoutManager layoutManager = ny LinearLayoutManager (denne); mRecyclerView.setLayoutManager (layoutManager); mRecyclerView.setAdapter (mAdapter); mRecyclerView.setHasFixedSize (true); RecyclerView.ItemDecoration itemDecoration = ny DividerItemDecoration (dette DividerItemDecoration.VERTICAL_LIST); mRecyclerView.addItemDecoration (itemDecoration); loadAnswers (); 

De loadAnswers () Metode gjør en nettverksforespørsel ved å ringe Enqueue (). Når svaret kommer tilbake, hjelper Retrofit oss til å analysere JSON-responsen på en liste over Java-objekter. (Dette er gjort mulig ved å bruke GsonConverter.)  

offentlig ugyldig belastningAnswers () mService.getAnswers (). enqueue (ny tilbakeringing() @Overgå offentlig ugyldig påmelding (Ring ring, svar svar) if (response.isSuccessful ()) mAdapter.updateAnswers (response.body (). getItems ()); Log.d ("MainActivity", "Innlegg lastet fra API");  ellers int statusCode = response.code (); // Behandle forespørselsfeil avhengig av statuskode @Overvrid offentlig tomgang på feil (Ring ring, kastbar t) showErrorMessage (); Log.d ("MainActivity", "error loading from API"); ); 

10. Forståelse Enqueue ()

Enqueue () Asynkront sender forespørselen og gir beskjed om appen din med tilbakeringing når et svar kommer tilbake. Siden denne forespørselen er asynkron, håndterer Retrofit den på en bakgrunnstråd, slik at hovedbruddstråden ikke er blokkert eller forstyrret med.

Å bruke Enqueue (), du må implementere to tilbakeringingsmetoder:

  • onResponse ()
  • onFailure ()

Bare en av disse metodene vil bli kalt som svar på en gitt forespørsel. 

  • onResponse (): Påkrevd for mottatt HTTP-respons. Denne metoden kalles for et svar som kan håndteres på riktig måte, selv om serveren returnerer en feilmelding. Så hvis du får en statuskode på 404 eller 500, vil denne metoden fortsatt bli kalt. For å få statuskoden slik at du kan håndtere situasjoner basert på dem, kan du bruke metoden response.code (). Du kan også bruke isSuccessful () metode for å finne ut om statuskoden er i området 200-300, som indikerer suksess.
  • onFailure (): Påkrevd når et nettverks unntak skjedde kommunikasjon til serveren eller når et uventet unntak oppstod, håndterer forespørselen eller behandler svaret. 

For å utføre en synkron forespørsel, kan du bruke henrette() metode. Vær oppmerksom på at synkroniserte metoder på hoved / UI-tråden vil blokkere alle brukerhandlinger. Så ikke utfør synkroniserte metoder på Android hoved- / brukergrensesnitt! Kjør dem i stedet på en bakgrunnstråd.

11. Testing av appen

Du kan nå kjøre appen. 

12. RxJava Integrasjon

Hvis du er fan av RxJava, kan du enkelt implementere Retrofit med RxJava. I Retrofit 1 ble det integrert som standard, men i Retrofit 2 må du ta med noen ekstra avhengigheter. Retrofit skip med en standardadapter for utførelse Anrop forekomster. Så du kan endre Retrofits utførelsesmekanisme for å inkludere RxJava ved å inkludere RxJava CallAdapter

Trinn 1

Legg til avhengighetene.

kompilere 'io.reactivex: rxjava: 1.1.6' compile 'io.reactivex: rxandroid: 1.2.1' compile 'com.squareup.retrofit2: adapter-rxjava: 2.1.0'

Steg 2

Legg til den nye CallAdapter RxJavaCallAdapterFactory.create () når du bygger en Retrofit-forekomst.  

offentlig statisk Retrofit getClient (String baseUrl) if (retrofit == null) retrofit = ny Retrofit.Builder () .baseUrl (baseUrl) .addCallAdapterFactory (RxJavaCallAdapterFactory.create ()) .addConverterFactory (GsonConverterFactory.create ()) .build ();  returnere ettermontering 

Trinn 3

Oppdater nå fåsvar ()  metoder for å returnere observers:

@GET ("/ answers? Order = desc & sort = activity & site = stackoverflow") Observerbar fåsvar (); @GET ("/ answers? Order = desc & sort = activity & site = stackoverflow") Observerbar getAnswers (@Query ("tagged") String tags); 

Trinn 4

Når du gjør forespørsler, svarer vår anonyme abonnent på den observerbare strømmen som avgir hendelser, i vårt tilfelle SOAnswersResponse. De onNext Metoden kalles da når abonnenten mottar noe som sendes ut, som sendes til vår adapter. 

@Override public void loadAnswers () mService.getAnswers (). SubscribeOn (Schedulers.io ()). ObserveOn (AndroidSchedulers.mainThread ()) .subscribe (new Subscriber() @Override public void onCompleted ()  @Override public void onError (Throwable e)  @Override public void onNext (SOAnswersResponse soAnswersResponse) mAdapter.updateAnswers (soAnswersResponse.getItems ()); ); 

Sjekk ut Komme i gang med ReactiveX på Android av Ashraff Hathibelagal for å lære mer om RxJava og RxAndroid. 

Konklusjon

I denne opplæringen lærte du om Retrofit: hvorfor bør du bruke den og hvordan. Jeg forklarte også hvordan å legge til RxJava integrasjon med Retrofit. I mitt neste innlegg vil jeg vise deg hvordan du skal utføre POSTSETTE, og SLETT, hvordan sende Form urlencoded data, og hvordan du kansellerer forespørsler. 

Hvis du vil vite mer om Retrofit, kan du se den offisielle dokumentasjonen. Og i mellomtiden, sjekk ut noen av våre andre kurs og opplæringsprogrammer på Android app utvikling.

  • Kommunikasjon innenfor en Android-app med EventBus

    Greenrobot EventBus er et populært åpen kildebibliotek som bruker publiserings- / abonnementsmodellen for kommunikasjon mellom komponenter i Android-systemet. I…
    Chike Mgbemena
    Android
  • Praktisk samtidighet på Android med HaMeR

    I denne opplæringen vil vi utforske HaMeR (Handler, Message and Runnable) rammeverket, en av de kraftigste samtidige modellene som er tilgjengelige på Android. Med en…
    Tin Megali
    Android SDK
  • Android fra scratch: Bruke REST APIer

    I denne opplæringen skal jeg vise deg hvordan du bruker klassene og metodene som er tilgjengelige i Android SDK, for å koble til eksterne webservere og samhandle med ...
    Ashraff Hathibelagal
    Android SDK
  • Kom i gang med en Android App-mal på 60 sekunder

    CodeCanyon har hundrevis av Android app-maler som du kan bruke til å hoppe starte utviklingen din. Denne videoen viser deg hvordan du installerer og tilpasser en ...
    Ashraff Hathibelagal
    Android SDK