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