Hola amigos 👋 Bienvenidos a un nuevo tutorial de Universo Android. Hoy aprenderemos a realizar peticiones HTTP con Retrofit, la biblioteca más popular y eficiente para consumir APIs REST en Android.
Al finalizar este tutorial tendrás una aplicación que incluirá:
- Configuración de Retrofit
- Peticiones GET, POST, PUT, DELETE
- Manejo de respuestas JSON
- Interceptores para logging
- Manejo de errores
- RecyclerView con datos de API
- Carga asÃncrona con callbacks
🟩 1. ¿Qué es Retrofit?
Retrofit es una biblioteca de tipo-seguro para realizar peticiones HTTP en Android y Java. Convierte tu API REST en una interfaz Java, facilitando el consumo de servicios web.
🟩 2. Ventajas de Retrofit
- Código limpio y fácil de mantener
- Conversión automática de JSON
- Soporte para peticiones sÃncronas y asÃncronas
- Manejo de errores integrado
- Compatible con RxJava, Coroutines
🟩 3. Crear el Proyecto
Creamos un nuevo proyecto en Android Studio con Empty Activity.
🟩 4. Agregar Dependencias
📄 build.gradle (Module: app)
dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.recyclerview:recyclerview:1.3.2'
implementation 'androidx.cardview:cardview:1.0.0'
// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
// OkHttp para logging
implementation 'com.squareup.okhttp3:logging-interceptor:4.11.0'
// Gson
implementation 'com.google.code.gson:gson:2.10.1'
}🟩 5. Agregar Permisos
📄 AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:usesCleartextTraffic="true"
android:theme="@style/Theme.AppCompat.Light.DarkActionBar">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>🟩 6. Clase Modelo
📄 Post.java
public class Post {
private int userId;
private int id;
private String title;
private String body;
public Post() {}
public Post(int userId, String title, String body) {
this.userId = userId;
this.title = title;
this.body = body;
}
// Getters
public int getUserId() { return userId; }
public int getId() { return id; }
public String getTitle() { return title; }
public String getBody() { return body; }
// Setters
public void setUserId(int userId) { this.userId = userId; }
public void setId(int id) { this.id = id; }
public void setTitle(String title) { this.title = title; }
public void setBody(String body) { this.body = body; }
}🟩 7. Interface de la API
📄 JsonPlaceholderApi.java
import java.util.List;
import retrofit2.Call;
import retrofit2.http.*;
public interface JsonPlaceholderApi {
// GET - Obtener todos los posts
@GET("posts")
Call<List<Post>> getPosts();
// GET - Obtener un post por ID
@GET("posts/{id}")
Call<Post> getPost(@Path("id") int id);
// GET - Obtener posts con query parameters
@GET("posts")
Call<List<Post>> getPostsByUser(@Query("userId") int userId);
// POST - Crear un nuevo post
@POST("posts")
Call<Post> createPost(@Body Post post);
// PUT - Actualizar un post completo
@PUT("posts/{id}")
Call<Post> updatePost(@Path("id") int id, @Body Post post);
// PATCH - Actualizar parcialmente
@PATCH("posts/{id}")
Call<Post> patchPost(@Path("id") int id, @Body Post post);
// DELETE - Eliminar un post
@DELETE("posts/{id}")
Call<Void> deletePost(@Path("id") int id);
}🟩 8. Cliente Retrofit
📄 RetrofitClient.java
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import java.util.concurrent.TimeUnit;
public class RetrofitClient {
private static final String BASE_URL = "https://jsonplaceholder.typicode.com/";
private static Retrofit retrofit = null;
public static Retrofit getClient() {
if (retrofit == null) {
// Logging interceptor
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
// OkHttpClient con timeouts
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build();
// Retrofit instance
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
public static JsonPlaceholderApi getApi() {
return getClient().create(JsonPlaceholderApi.class);
}
}🟩 9. Diseño XML
📄 activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Retrofit API Demo"
android:textSize="24sp"
android:textStyle="bold"
android:textColor="#2196F3"
android:layout_marginBottom="16dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="16dp">
<Button
android:id="@+id/btnGet"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="GET"
android:layout_marginRight="4dp" />
<Button
android:id="@+id/btnPost"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="POST"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp" />
<Button
android:id="@+id/btnDelete"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="DELETE"
android:layout_marginLeft="4dp" />
</LinearLayout>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>🟩 10. Layout del Item
📄 res/layout/item_post.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
app:cardCornerRadius="8dp"
app:cardElevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/txtId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ID: 1"
android:textSize="12sp"
android:textColor="#999999" />
<TextView
android:id="@+id/txtTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TÃtulo"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#000000"
android:layout_marginTop="8dp" />
<TextView
android:id="@+id/txtBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Descripción"
android:textSize="14sp"
android:textColor="#666666"
android:layout_marginTop="8dp"
android:maxLines="3"
android:ellipsize="end" />
</LinearLayout>
</androidx.cardview.widget.CardView>🟩 11. Adaptador
📄 PostAdapter.java
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
public class PostAdapter extends RecyclerView.Adapter<PostAdapter.ViewHolder> {
private List<Post> posts = new ArrayList<>();
public void setPosts(List<Post> posts) {
this.posts = posts;
notifyDataSetChanged();
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_post, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Post post = posts.get(position);
holder.bind(post);
}
@Override
public int getItemCount() {
return posts.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView txtId, txtTitle, txtBody;
ViewHolder(View itemView) {
super(itemView);
txtId = itemView.findViewById(R.id.txtId);
txtTitle = itemView.findViewById(R.id.txtTitle);
txtBody = itemView.findViewById(R.id.txtBody);
}
void bind(Post post) {
txtId.setText("ID: " + post.getId());
txtTitle.setText(post.getTitle());
txtBody.setText(post.getBody());
}
}
}🟩 12. MainActivity
📄 MainActivity.java
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Toast;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public class MainActivity extends AppCompatActivity {
RecyclerView recyclerView;
PostAdapter adapter;
Button btnGet, btnPost, btnDelete;
ProgressBar progressBar;
JsonPlaceholderApi api;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initializeViews();
setupRecyclerView();
api = RetrofitClient.getApi();
btnGet.setOnClickListener(v -> getPosts());
btnPost.setOnClickListener(v -> createPost());
btnDelete.setOnClickListener(v -> deletePost(1));
}
private void initializeViews() {
recyclerView = findViewById(R.id.recyclerView);
btnGet = findViewById(R.id.btnGet);
btnPost = findViewById(R.id.btnPost);
btnDelete = findViewById(R.id.btnDelete);
progressBar = findViewById(R.id.progressBar);
}
private void setupRecyclerView() {
adapter = new PostAdapter();
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);
}
private void getPosts() {
showProgress(true);
Call<List<Post>> call = api.getPosts();
call.enqueue(new Callback<List<Post>>() {
@Override
public void onResponse(Call<List<Post>> call, Response<List<Post>> response) {
showProgress(false);
if (response.isSuccessful() && response.body() != null) {
List<Post> posts = response.body();
adapter.setPosts(posts);
Toast.makeText(MainActivity.this,
"Cargados: " + posts.size() + " posts",
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this,
"Error: " + response.code(),
Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<List<Post>> call, Throwable t) {
showProgress(false);
Toast.makeText(MainActivity.this,
"Error de conexión: " + t.getMessage(),
Toast.LENGTH_LONG).show();
}
});
}
private void createPost() {
showProgress(true);
Post newPost = new Post(1, "Mi nuevo post", "Este es el contenido del post");
Call<Post> call = api.createPost(newPost);
call.enqueue(new Callback<Post>() {
@Override
public void onResponse(Call<Post> call, Response<Post> response) {
showProgress(false);
if (response.isSuccessful() && response.body() != null) {
Post createdPost = response.body();
Toast.makeText(MainActivity.this,
"Post creado con ID: " + createdPost.getId(),
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this,
"Error al crear: " + response.code(),
Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<Post> call, Throwable t) {
showProgress(false);
Toast.makeText(MainActivity.this,
"Error: " + t.getMessage(),
Toast.LENGTH_SHORT).show();
}
});
}
private void deletePost(int id) {
showProgress(true);
Call<Void> call = api.deletePost(id);
call.enqueue(new Callback<Void>() {
@Override
public void onResponse(Call<Void> call, Response<Void> response) {
showProgress(false);
if (response.isSuccessful()) {
Toast.makeText(MainActivity.this,
"Post eliminado exitosamente",
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(MainActivity.this,
"Error al eliminar: " + response.code(),
Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<Void> call, Throwable t) {
showProgress(false);
Toast.makeText(MainActivity.this,
"Error: " + t.getMessage(),
Toast.LENGTH_SHORT).show();
}
});
}
private void showProgress(boolean show) {
progressBar.setVisibility(show ? View.VISIBLE : View.GONE);
}
}🟩 13. Anotaciones Principales
| Anotación | Descripción |
|---|---|
@GET | Petición GET |
@POST | Petición POST |
@PUT | Petición PUT |
@DELETE | Petición DELETE |
@PATCH | Actualización parcial |
@Path | Variable en la URL |
@Query | Parámetro de consulta |
@Body | Cuerpo de la petición |
@Header | Header personalizado |
▶️ Cómo Ejecutar
- Abre Android Studio
- Sincroniza las dependencias
- Presiona Run
- Prueba los botones GET, POST, DELETE
🧪 Resultado Final
Aplicación funcional que consume una API REST, muestra datos en RecyclerView y realiza operaciones CRUD.
📥 Descargar Proyecto
👉
🙌 Gracias por Visitar mi Blog
✔️ Compártelo
✔️ Déjame un comentario
✔️ SÃgueme para más contenido
❓ Preguntas Frecuentes
1. ¿Qué es Retrofit?
Una biblioteca tipo-seguro para realizar peticiones HTTP que convierte tu API REST en una interfaz Java.
2. ¿Cómo manejo errores en Retrofit?
Usa los callbacks onResponse y onFailure, y verifica response.isSuccessful().
3. ¿Necesito permisos especiales?
SÃ, INTERNET en el AndroidManifest y usesCleartextTraffic="true" para HTTP.
4. ¿Puedo usar Retrofit con Coroutines?
SÃ, Retrofit soporta suspend functions para uso con Kotlin Coroutines.

No hay comentarios:
Publicar un comentario