Hola amigos 👋 Bienvenidos a un nuevo tutorial de Universo Android. Hoy aprenderemos a usar Retrofit con Gson, la combinación perfecta para consumir APIs REST convirtiendo automáticamente JSON en objetos Java y viceversa.
Al finalizar este tutorial tendrás:
- Configuración completa Retrofit + Gson
- Peticiones GET, POST, PUT, DELETE
- Conversión automática JSON
- Modelos con anotaciones Gson
- Manejo de errores
- RecyclerView con datos de API
- Ejemplos prácticos
🟩 1. ¿Qué es Retrofit + Gson?
Retrofit es una biblioteca HTTP type-safe que convierte tu API en una interfaz Java. Gson es el convertidor que transforma JSON en objetos Java automáticamente.
🟩 2. Ventajas de la Combinación
- Conversión automática JSON ↔ Java
- Type-safe (detección de errores en compile-time)
- Código limpio y mantenible
- Anotaciones @SerializedName para mapeo
- Manejo de tipos complejos
🟩 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.11.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 Logging
implementation 'com.squareup.okhttp3:logging-interceptor:4.12.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:usesCleartextTraffic="true"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
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. Modelos con Gson
📄 Post.java
import com.google.gson.annotations.SerializedName;
public class Post {
@SerializedName("userId")
private int userId;
@SerializedName("id")
private int id;
@SerializedName("title")
private String title;
@SerializedName("body")
private String body;
// Constructor vacío
public Post() {}
// Constructor para crear 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; }
}📄 User.java
import com.google.gson.annotations.SerializedName;
public class User {
@SerializedName("id")
private int id;
@SerializedName("name")
private String name;
@SerializedName("username")
private String username;
@SerializedName("email")
private String email;
@SerializedName("phone")
private String phone;
public int getId() { return id; }
public String getName() { return name; }
public String getUsername() { return username; }
public String getEmail() { return email; }
public String getPhone() { return phone; }
}🟩 7. Interface API
📄 JsonPlaceholderApi.java
import java.util.List;
import retrofit2.Call;
import retrofit2.http.*;
public interface JsonPlaceholderApi {
// GET - Lista de posts
@GET("posts")
Call<List<Post>> getPosts();
// GET - Post específico
@GET("posts/{id}")
Call<Post> getPost(@Path("id") int id);
// GET - Posts por usuario
@GET("posts")
Call<List<Post>> getPostsByUser(@Query("userId") int userId);
// GET - Lista de usuarios
@GET("users")
Call<List<User>> getUsers();
// POST - Crear post
@POST("posts")
Call<Post> createPost(@Body Post post);
// PUT - Actualizar completo
@PUT("posts/{id}")
Call<Post> updatePost(@Path("id") int id, @Body Post post);
// PATCH - Actualización parcial
@PATCH("posts/{id}")
Call<Post> patchPost(@Path("id") int id, @Body Post post);
// DELETE - Eliminar post
@DELETE("posts/{id}")
Call<Void> deletePost(@Path("id") int id);
// POST con campos individuales
@FormUrlEncoded
@POST("posts")
Call<Post> createPostForm(
@Field("userId") int userId,
@Field("title") String title,
@Field("body") String body
);
}🟩 8. Cliente Retrofit con Gson
📄 RetrofitClient.java
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
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) {
// Configurar Gson
Gson gson = new GsonBuilder()
.setLenient()
.create();
// Logging interceptor
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
// OkHttpClient
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(gson))
.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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Retrofit + Gson Demo"
android:textSize="24sp"
android:textStyle="bold"
android:textColor="#2196F3"
android:padding="16dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp">
<Button
android:id="@+id/btnGetPosts"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="GET Posts"
android:layout_margin="4dp" />
<Button
android:id="@+id/btnGetUsers"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="GET Users"
android:layout_margin="4dp" />
<Button
android:id="@+id/btnCreate"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="POST"
android:layout_margin="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"
android:padding="8dp" />
</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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<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/txtUserId"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="User: 1"
android:textSize="12sp"
android:textColor="#999999"
android:layout_marginStart="16dp" />
</LinearLayout>
<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="Cuerpo del post"
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<>();
private OnItemClickListener listener;
public interface OnItemClickListener {
void onItemClick(Post post);
}
public PostAdapter(OnItemClickListener listener) {
this.listener = listener;
}
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, listener);
}
@Override
public int getItemCount() {
return posts.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView txtId, txtUserId, txtTitle, txtBody;
ViewHolder(View itemView) {
super(itemView);
txtId = itemView.findViewById(R.id.txtId);
txtUserId = itemView.findViewById(R.id.txtUserId);
txtTitle = itemView.findViewById(R.id.txtTitle);
txtBody = itemView.findViewById(R.id.txtBody);
}
void bind(Post post, OnItemClickListener listener) {
txtId.setText("ID: " + post.getId());
txtUserId.setText("User: " + post.getUserId());
txtTitle.setText(post.getTitle());
txtBody.setText(post.getBody());
itemView.setOnClickListener(v -> listener.onItemClick(post));
}
}
}🟩 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 btnGetPosts, btnGetUsers, btnCreate;
ProgressBar progressBar;
JsonPlaceholderApi api;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initializeViews();
setupRecyclerView();
api = RetrofitClient.getApi();
btnGetPosts.setOnClickListener(v -> getPosts());
btnGetUsers.setOnClickListener(v -> getUsers());
btnCreate.setOnClickListener(v -> createPost());
}
private void initializeViews() {
recyclerView = findViewById(R.id.recyclerView);
btnGetPosts = findViewById(R.id.btnGetPosts);
btnGetUsers = findViewById(R.id.btnGetUsers);
btnCreate = findViewById(R.id.btnCreate);
progressBar = findViewById(R.id.progressBar);
}
private void setupRecyclerView() {
adapter = new PostAdapter(post ->
Toast.makeText(this, "Post: " + post.getTitle(), Toast.LENGTH_SHORT).show()
);
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: " + t.getMessage(),
Toast.LENGTH_LONG).show();
}
});
}
private void getUsers() {
showProgress(true);
Call<List<User>> call = api.getUsers();
call.enqueue(new Callback<List<User>>() {
@Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
showProgress(false);
if (response.isSuccessful() && response.body() != null) {
List<User> users = response.body();
StringBuilder info = new StringBuilder();
for (User user : users) {
info.append(user.getName()).append("\n");
}
Toast.makeText(MainActivity.this,
"Usuarios: " + users.size(),
Toast.LENGTH_SHORT).show();
}
}
@Override
public void onFailure(Call<List<User>> call, Throwable t) {
showProgress(false);
Toast.makeText(MainActivity.this,
"Error: " + t.getMessage(),
Toast.LENGTH_SHORT).show();
}
});
}
private void createPost() {
showProgress(true);
Post newPost = new Post(1, "Mi Post con Gson", "Creado usando Retrofit y Gson");
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: " + 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 showProgress(boolean show) {
progressBar.setVisibility(show ? View.VISIBLE : View.GONE);
}
}🟩 13. Anotaciones Gson Útiles
// Mapear nombre diferente
@SerializedName("user_id")
private int userId;
// Nombre alternativo
@SerializedName(value = "email", alternate = {"e-mail", "emailAddress"})
private String email;
// Excluir del JSON
@Expose(serialize = false, deserialize = false)
private String password;
// Formato de fecha
@SerializedName("created_at")
private Date createdAt;🟩 14. Configurar Gson Custom
Gson gson = new GsonBuilder()
.setLenient() // JSON flexible
.setPrettyPrinting() // JSON formateado
.setDateFormat("yyyy-MM-dd HH:mm:ss") // Formato fecha
.excludeFieldsWithoutExposeAnnotation() // Solo @Expose
.create();🟩 15. Comparación Convertidores
| Convertidor | Velocidad | Tamaño | Popularidad |
|---|---|---|---|
| Gson | Media | Medio | Alta |
| Moshi | Rápida | Pequeño | Media |
| Jackson | Rápida | Grande | Alta |
▶️ Cómo Ejecutar
- Abre Android Studio
- Copia el código
- Sync Gradle
- Presiona Run
- Prueba los botones GET, POST
🧪 Resultado Final
App que consume API REST con conversión automática JSON usando Retrofit + Gson.
📥 Descargar Proyecto
👉
🙌 Gracias por Visitar mi Blog
✔️ Compártelo
✔️ Déjame un comentario
✔️ Sígueme para más contenido
❓ Preguntas Frecuentes
1. ¿Qué hace @SerializedName?
Mapea el nombre del campo JSON al campo Java, útil cuando los nombres difieren.
2. ¿Retrofit funciona sin Gson?
Sí, pero Gson automatiza la conversión JSON. Sin él debes parsear manualmente.
3. ¿Puedo usar otro convertidor?
Sí, puedes usar Moshi, Jackson u otros con Retrofit.
4. ¿Cómo manejo errores HTTP?
Verifica response.isSuccessful() y response.code() en el callback.

No hay comentarios:
Publicar un comentario