Blog para desarrollo de aplicaciones en Android, aprende paso a paso como crear aplicaciones.

Usamos cookies propias y de terceros que entre otras cosas recogen datos sobre sus hábitos de navegación para mostrarle publicidad personalizada y realizar análisis de uso de nuestro sitio.
Si continúa navegando consideramos que acepta su uso. OK Más información | Y más

Retrofit + Gson en Android

Retrofit + Gson en Android

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)

gradle
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
<?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

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

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

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

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
<?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
<?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

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

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

java
// 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

java
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

ConvertidorVelocidadTamañoPopularidad
GsonMediaMedioAlta
MoshiRápidaPequeñoMedia
JacksonRápidaGrandeAlta

▶️ Cómo Ejecutar

  1. Abre Android Studio
  2. Copia el código
  3. Sync Gradle
  4. Presiona Run
  5. 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