Skip to content

Commit c33d6fa

Browse files
committed
firebase authentication post published and retrofit post added
1 parent ccc77cc commit c33d6fa

File tree

5 files changed

+216
-0
lines changed

5 files changed

+216
-0
lines changed

_drafts/2019-06-10-retrofit.md

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
---
2+
layout: post
3+
title: "Retrofit"
4+
date: 2019-06-10
5+
categories: ["Biblioteki"]
6+
image: libraries/retrofit
7+
github: libraries/tree/master/retrofit
8+
description: "Biblioteki"
9+
version: Retrofit 2.5
10+
keywords: "retrofit, network, http, request, response, sync, async, call, callback, rest, api, get, post, put, delete, path, query, header, multipart, formurlencoded, json, rxjava, android, programowanie, programming"
11+
---
12+
13+
## Charakterystyka
14+
`Retrofit` jest klientem `HTTP` zorientowanym na typowanie obiektów zapytań, używającym do żądań bibliotekę `OkHttp`. Umożliwia w łatwy sposób pobieranie i przesyłanie danych w formie obiektów lub schemacie `JSON` za pośrednictwem usługi internetowej opartej na `REST`. Ponadto pozwala na wykonywanie zapytań w sposób synchroniczny i asynchroniczny z uwzględnieniem uwierzytelniania i rejestrowania stanu operacji. Aby rozpocząć pracę z `Retrofit` należy dokonać definicji klasy modelu danych, interfejsu deklarującego możliwe operacje HTTP oraz instancji klasy `Retrofit.Builder`, której zadaniem jest zbudowanie usługi w oparciu o wskazane zależności.
15+
16+
## Model
17+
Konwerter podejmuje próbę konwersji otrzymanego wyniku do zadaklerowanego typu klasy modelu o strukturze danych reprezentującej oczekiwany rezultat. Wartości zostają przypisane tylko do właściwości zachowujących zgodność z formatem odpowiedzi, tzn. brakujące lub nadmiarowe pola są ignorowane. Poniższy listing przedstawia przykładową strukturę modelu `Product` i `Producer` dla zadanej odpowiedzi w formacie `JSON`.
18+
19+
{% highlight json %}
20+
{
21+
"id": 100,
22+
"name": "Coca-Cola Lime",
23+
"producer": {
24+
"id": "200"
25+
"name": "Coca-Cola Company",
26+
"country": "USA"
27+
},
28+
"price": 2.5,
29+
"ingredients": ["water", "sugar", "e150d", "lime syrup"]
30+
}
31+
{% endhighlight %}
32+
33+
{% highlight kotlin %}
34+
//some classes with properties and getters, setters
35+
data class Product(val id: Int, val name: String, val producer: Producer, val price: Double)
36+
data class Producer(val id: Int, val name: String, val country String)
37+
//converter will just ignore missing ingredients field in model
38+
{% endhighlight %}
39+
40+
## Interfejs
41+
Rolą interfejsu jest zadeklarowanie metod odwołujących się do zasobów sieciowego `API`. Za pomocą adnotacji `@GET`, `@POST`, `@PUT`, `@DELETE` możliwe jest określenie rodzaju zapytania dla architektury `REST`. Zapytania mogą być parametryzowane przy użyciu argumentów metody oznaczonych jako `@Path` i `@Query`. Poza wysyłaniem danych w formie `text/plain` wspierana jest także obsługa zapytań typu `application/x-www-form-urlencoded` (adnotacja `@FormUrlEncoded`) oraz `multipart/form-data` (adnotacja `@Multipart`). Dodatkowo możliwe jest ustawienie metadanych nagłówka w adnotacji `@Headers`.
42+
43+
{% highlight kotlin %}
44+
interface ProductService {
45+
46+
@GET("products")
47+
fun getProducts() : Call<List<Product>>
48+
49+
@GET("products") //add some optional query to request
50+
fun getProducts(@Query("sort") sort: String) : Call<List<Product>>
51+
52+
@GET("product/{id}") //parametrize request
53+
fun getProduct(@Path("id") id: Int) : Call<Product>
54+
55+
@Headers("Cache-Control: max-age=1000000") //set additional static header
56+
@GET("producer/{id}")
57+
fun getProducer(@Path("id") id: Int) : Call<Producer>
58+
59+
@POST("products/new")
60+
fun createProduct(@Body product: Product) : Call<ResponseBody>
61+
62+
@FormUrlEncoded //send as form url encoded
63+
@POST("producers/new")
64+
fun createProducer(@Field("name") name: String, @Field("country") country: String) : Call<ResponseBody>
65+
66+
@Multipart //send as multipart - mainly for files
67+
@PUT("producer/{id}")
68+
fun updateProducer(@Path("id") id: Int, @Part("image") image: RequestBody) : Call<ResponseBody>
69+
70+
@DELETE("product/{id}")
71+
fun deleteProduct(@Path("id") id: Int) : Call<ResponseBody>
72+
73+
//some more REST API methods
74+
}
75+
{% endhighlight %}
76+
77+
## Budowniczy
78+
Aby wykorzystać stworzone API w interfejsie należy zbudować instancje typu `Retrofit` przy pomocy budowniczego `Retrofit.Builder` podając przynajmniej `bazowe URL` oraz opcjonalnie m.in. konwerter (np. `Gson`, `Protobuf`, `Simple XML`), adapter i klienta HTTP. Następnie wykorzystując obiekt `Retrofit` stworzyć instancję wybranego interfejsu API.
79+
80+
{% highlight kotlin %}
81+
private fun buildService() {
82+
val retrofit = Retrofit.Builder()
83+
.baseUrl("http://api.androidcode.pl/") //address not exists, only for example purpose
84+
.addConverterFactory(GsonConverterFactory.create()) //Gson is default
85+
.client(OkHttpClient.Builder().build()) //OkHttpClient is default
86+
.build()
87+
88+
service = retrofit.create(ProductService::class.java) //instance of ProductService
89+
//do calls on service object
90+
}
91+
{% endhighlight %}
92+
93+
## Zapytanie
94+
Żądanie sieciowe dla metody zapytania zwracającego instancje typu `Call` może zostać wykonane synchronicznie przy użyciu metody `execute` lub asynchronicznie metodą `enqueue` wraz z przekazaniem obiektu zwrotnego typu `Callback`.
95+
96+
{% highlight kotlin %}
97+
private fun getProductsSync() {
98+
val call : Call<List<Product>> = service.getProducts()
99+
val response : Response<List<Product>> = call.execute()
100+
//wait for response in this place
101+
if(response.isSuccessful) {
102+
//do something with data
103+
val data : List<Product>? = response.body()
104+
}
105+
else {
106+
//do some fail action
107+
val error = response.errorBody()
108+
val message = response.message()
109+
}
110+
}
111+
112+
private fun getProductsAsync() {
113+
val call : Call<List<Product>> = service.getProducts()
114+
//do request at this point and do something else during waiting for response
115+
call.enqueue(object: Callback<List<Product>> {
116+
override fun onResponse(call: Call<List<Product>>, response: Response<List<Product>>) {
117+
//response returned at some moment
118+
val products = response.body()
119+
//do something with data
120+
}
121+
override fun onFailure(call: Call<List<Product>>, t: Throwable) {
122+
//fail returned at some moment
123+
}
124+
})
125+
}
126+
{% endhighlight %}
127+
128+
## Autoryzacja
129+
W sytuacji, gdy zapytania wymagają autoryzacji możliwe jest dodanie tokenu autoryzacyjnego do zapytania przy użyciu adnotacji `@Header("Authorization")`, jednakże w takim przypadku autoryzacja dotyczy tylko tego żądania. Aby dodać autoryzację do wszystkich zapytań należy dodać obiekt typu `Interceptor` do konfiguracji klienta.
130+
131+
{% highlight kotlin %}
132+
@GET("products")
133+
fun getProducts(@Header("Authorization") String credentials) : Call<List<Product>>
134+
//instead of adding authorization to single request like above
135+
//just add authorization to every request by Interceptor config as below
136+
137+
private fun buildServiceWithAuthorization() {
138+
//create interceptor which add credentials to request
139+
val interceptor = object: Interceptor {
140+
override fun intercept(chain: Interceptor.Chain): okhttp3.Response {
141+
val originalRequest = chain.request()
142+
val builder = originalRequest.newBuilder()
143+
.header("Authorization", Credentials.basic("username", "password"))
144+
return chain.proceed(builder.build())
145+
}
146+
}
147+
148+
//pass interceptor to http client
149+
val okHttpClient = OkHttpClient().newBuilder()
150+
.addInterceptor(interceptor)
151+
.build()
152+
153+
val retrofit = Retrofit.Builder()
154+
.baseUrl("http://api.androidcode.pl/")
155+
.addConverterFactory(GsonConverterFactory.create())
156+
.client(okHttpClient)
157+
.build()
158+
159+
service = retrofit.create(ProductService::class.java) //instance of ProductService
160+
}
161+
{% endhighlight %}
162+
163+
## RxJava
164+
`Retrofit` umożliwia współpracę z `RxJava` (metody mogą zwracać `Observable`) poprzez dodanie adaptera `RxJava2CallAdapterFactory` do konfiguracji budowniczego co sprawia, że tworzenie aplikacji z wykorzystaniem obu bibliotek staje się prostsze. Dzięki temu Retrofit jest nierzadko wybierany jako podstawowy klient sieciowy w aplikacji używających RxJava.
165+
166+
{% highlight kotlin %}
167+
interface ProductServiceRxJava {
168+
169+
@GET("products")
170+
fun getProducts() : Observable<List<Product>> //or other type of RxJava observables
171+
172+
companion object {
173+
fun create(): ProductServiceRxJava {
174+
val retrofit = Retrofit.Builder()
175+
.baseUrl("http://api.androidcode.pl/")
176+
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
177+
.addConverterFactory(GsonConverterFactory.create())
178+
.build()
179+
return retrofit.create<ProductServiceRxJava>(ProductServiceRxJava::class.java)
180+
}
181+
}
182+
}
183+
184+
class RxJavaActivity : AppCompatActivity() {
185+
186+
private val service by lazy { ProductServiceRxJava.create() }
187+
private var disposable: Disposable? = null
188+
189+
override fun onCreate(savedInstanceState: Bundle?) {
190+
super.onCreate(savedInstanceState)
191+
setContentView(R.layout.activity_rxjava)
192+
getProductsRxJava()
193+
}
194+
195+
override fun onDestroy() {
196+
super.onDestroy()
197+
disposable?.dispose()
198+
}
199+
200+
private fun getProductsRxJava() {
201+
disposable = service.getProducts()
202+
.subscribeOn(Schedulers.io())
203+
.observeOn(AndroidSchedulers.mainThread())
204+
.subscribe(
205+
{ result ->
206+
val products = result
207+
//do something with data
208+
},
209+
{ error ->
210+
val message = error.message
211+
//some error action
212+
}
213+
)
214+
}
215+
}
216+
{% endhighlight %}
File renamed without changes.
672 KB
Loading
16.9 KB
Loading
56.7 KB
Loading

0 commit comments

Comments
 (0)