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

HTTP Requests con Retrofit en Android

HTTP Requests con Retrofit en Android

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ónDescripción
@GETPetición GET
@POSTPetición POST
@PUTPetición PUT
@DELETEPetición DELETE
@PATCHActualización parcial
@PathVariable en la URL
@QueryParámetro de consulta
@BodyCuerpo de la petición
@HeaderHeader personalizado

▶️ Cómo Ejecutar

  1. Abre Android Studio
  2. Sincroniza las dependencias
  3. Presiona Run
  4. 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