miércoles, 13 de octubre de 2021

 Retrofit 2 con KOTLIN


Hace tiempo quería crear este mini tutorial, ya que en internet he visto demasiada información combinada, pero que  puede marear a cualquier developer que solo busca información concisa. Y para los no entendidos del tema, Retrofit es la librería más usada para hacer peticiones red(Consumir una Api) desde una aplicación Android. Estoy basándome en la información proporcionada del Android Developer Aris Guimerá en su curso de Open Webinars, dejo el link para que compren el curso que me parece excelente. https://openwebinars.net/academia/portada/api-retrofit2-kotlin/

 Ahora sí vamos al lio!


1.- Instalamos las librerías necesarias(Retrofit2, Gson,OkHttp3, loggin-interceptor, corrutinas)

  - Retrofit2: Librería para hacer las peticiones 

  -  Gson: Para transformar la información que nos llega de la api a una forma muy sencilla de trabajar. para "Parsear"

 - OkHttp3 : Librería que nos provee de funciones que nos ayudan al momento de trabajar con Retrofit

- logging-interceptor : Para llevar un tracking de la respuesta que nos llega, mediante logcat

- Corrutinas: Trabajar en segundo hilo.



2.- Habilitar permisos de internet en el Manifest


3.- Usamos un front básico para darle usabilidad.




4.- Nos olvidamos de activar el viewBinding true en el gradle, ya que usaremos esto para relacionar las vistas y no tener problemas nulidad al llamar ids. Pues lo ponemos!



5.- Nos vamos al Main Activity y creamos la variable binding y empezamos a relacionar vistas con variables 

6.- Creamos un fichero llamado API, lo hacemos con Singleton para crear una única instancia del objeto Retrofit




 7.- Crear una Interface que defina el contrato entre nuestra API y nosotros, para ver bien el json que nos trae podemos usar https://jsoneditoronline.org/.




8.- Creando una data class para hacer el modelo de datos que necesitamos. La llamaremos Language y crearemos el data class con los tipo de valores que nos devuelve.Además como empiza con [ es un array, y si es un array tenemos que crear un listado.







 Ya tenemos el modelo de datos listo.


9.- Conectamos el Api Service con el Api


10.- Implementando la corrutinas para no hacerlo en el hilo principal.



Y en el Api Service usar la palabra reservada suspend 


Petición correcta

11.- Ahora que ya sabemos que hace la petición correcta, hacemos la petición real. 
Haremos la llamada Post . Se tiene que modelar el objeto correctamente si queremos que funcione.
Añadimos el Header, el post y FormUrlEncoded

@Headers("Authorization: Bearer 657271e907c55609174816fb78716aa8")
@FormUrlEncoded
@POST ("0.2/detect")
suspend fun getTxtLanguage(@Field("q")text:String )
Quedaría así: 



Luego vemos el modelo en nuestro JsonEditorOnline y observamos como esta el json para hacer una modelación correcta .
Observamos que hay un objeto que dentro hay otro objeto llamado data, que dentro tiene un array o listado y dentro hay 3 elementos . Pues hay que modelarlo así .
Entonces, primero creamos una data class y la llamamos DetectionResponse.



Este data class va a contener un objeto llamado data , entonces creamos otro data class y la llamamos Data, y esta clase data va contener un listado de detection y


 Y ahora creamos otra data class y la llamamos Detection que va contener el contenido del listado

                                       




, y ahora no dirigimos a la  data class Data y la modificamos el nombre del la clase Detection


Ahora vamos al ApiService y agregamos que el tipo de respuesta sera:

: Response<DetectionResponse>




Ya tenemos creada la petición post!

@POST    


12.- Ahora tenemos que conectar todo al activity para tener un flujo completo .
Primero iniciamos el listener necesario.




Creamos la función getTextLanguage
private fun getTextLanguage(text: String) {
CoroutineScope(Dispatchers.IO).launch {
var result = retrofitService.getTxtLanguage(text)

if (result.isSuccessful) {
checkResult(result.body())
} else {
showError()

}
}
}
creamos la función checkResult
Lo analizamos asi : Si detectionResponse es distinto de null y si este listado no es nulo ni vacío, pues haz algo
private fun checkResult(detectionResponse: DetectionResponse?) {
if (detectionResponse != null && !detectionResponse.data.detections.isNullOrEmpty()) {
val correctLanguages= detectionResponse.data.detections.filter { it.isReliable }
if (correctLanguages.isNotEmpty()) {


runOnUiThread {
Toast.makeText(
this,
"El idioma es ${correctLanguages.first().language}", Toast.LENGTH_SHORT
).show()
}
}

}
}

Y el resultado por ahora queda asi :




Modificación de la función checkResult




Probamos



Ahora añadiremos un progress Bar



13.-  Añadiendo extras :

Ahora  pasamos al Main Activity y añadimos este progressBar

private lateinit var progressBar: ProgressBar

private fun initView() {
btnDetectLanguage = binding.btnDetectLanguage
etDescription = binding.etDescription
progressBar = binding.progressBar
}

private fun initListener() {
btnDetectLanguage.setOnClickListener {
val text = etDescription.text.toString()
if (text.isNotEmpty()) {
showLoading()
getTextLanguage(text)

}
}
}

Ahora le decimos que la función showLoading sea Visible cuando den click en el botón

private fun showLoading() {
progressBar.visibility= View.VISIBLE
}


Y ahora creamos la funcion hideLoading cuando funcione o no funcione se esconda el progressBar

private fun showLoading() {
progressBar.visibility= View.VISIBLE
}

private fun hideLoading() {
progressBar.visibility= View.GONE
}


Añadimos el cleanText, para que cuando finalice se limpie el campo.



private fun cleanText() {
etDescription.setText("")
}


14.- Usaremos logging interceptor , creamos la variable logging en la clase API



object API {
private const val BASE_URL = "https://ws.detectlanguage.com/"

val retrofitService: ApiService by lazy {
getRetrofit().create(ApiService::class.java)
}

private fun getRetrofit(): Retrofit {
val logging = HttpLoggingInterceptor()
logging.setLevel(HttpLoggingInterceptor.Level.BODY)
val httpClient=OkHttpClient.Builder()

httpClient.addInterceptor(logging)

return Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient.build())
.build()
}

}



private fun showLoading() {
progressBar.visibility = View.VISIBLE
}

private fun hideLoading() {
runOnUiThread {
progressBar.visibility = View.GONE
}

}


private fun getTextLanguage(text: String) {
CoroutineScope(Dispatchers.IO).launch {
var result = retrofitService.getTxtLanguage(text)
if (result.isSuccessful) {
checkResult(result.body())
} else {
showError()
}
cleanText()
hideLoading()
}
}