Android Network Calls with Retrofit

edited June 21 in Android

Retrofit ဆိုတာဘာလဲ

Retrofit ဆိုတာကတော့ Android မှာ REST API တွေနဲ့ အလုပ်လုပ်တဲ့အခါမှာ network call တွေကို လွယ်လွယ်ကူကူ ဆောင်ရွက်နိုင်အောင်လုပ်ပေးတဲ့ library တစ်ခုပါ။
REST အကြောင်းကို မသိသေးရင်တော့ ဒီမှာ သွားကြည့်လို့ရပါတယ်။

Retrofit ကို ဘယ်လိုသုံးလဲ

ဒီ tutorial ရဲ့ source code အပြည့်အစုံကို https://gitlab.com/anntphyothwin/android-archi-sample/ ကို git clone လုပ်ပြီး စမ်းသပ်နိုင်ပါတယ်။

Dependencies

Retrofit ကိုသုံးဖို့အတွက် အရင်ဆုံး Retrofit library ကို app ရဲ့ gradle dependencies ထဲမှာ အရင်ထည့်ရပါမယ်။

dependencies {
    implementation 'com.squareup.retrofit2:retrofit:2.3.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.0.2'
}

Client တည်ဆောက်ခြင်း

Retrofit နဲ့ network call တွေလုပ်မယ်ဆိုရင် အရင်ဆုံး Retrofit Client တစ်ခု အရင်ဆောက်ပေးရပါမယ်။
RetrofitClient ကို ဆောက်တဲ့အခါမှာ Singleton design pattern နဲ့ဆောက်သင့်ပါတယ်။ App တစ်ခုက network call တစ်ခုထက်မက ရှိနိုင်တဲ့အတွက် network call တစ်ခု ခေါ်တိုင်းမှာ RetrofitClient အသစ်တစ်ခုဆောက်နေရင် memory waste ဖြစ်တဲ့အတွက်ကြောင့်ပါ။

RetrofitClient (source code)

package com.zwenex.androidarchisample.network;

import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

/**
 * RetrofitClient is used to make Retrofit instances.
 * Uses Singleton design pattern. (Explained in AppDatabase)
 */
public class RetrofitClient {
    private static Retrofit instance;

    /** API base URL */
    private static String BASE_URL = "http://bookapi.tiide.org";

    /** Checks if an instance already exists, and if not, creates a new instance and returns it.*/
    public static Retrofit getInstance(){
        if(instance == null){
            instance = buildRetrofitClient();
        }
        return instance;
    }

    /** Make private to prevent having multiple instances. */
    private static Retrofit buildRetrofitClient(){
        return new Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build();
    }
}

Network call တွေပြုလုပ်မယ့် base url ကို ထည့်ပေးရပါတယ်။ အခု tutorial အတွက် API တွေ ကို http://bookapi.tiide.org မှာတင်ပေးထားပါတယ်။Network call ခေါ်လိုက်ရင် ကျလာမယ့် data တွေက JSON type ဖြစ်တဲ့အတွက် JSON တွေကို Java Object အဖြစ် ပြောင်းလဲဖို့ GsonConverterFactory ကို သုံးပေးရပါတယ်။

JSON ဆိုတာက JavaScript Object Notation ကိုပြောတာပါ။ REST API ကို အသုံးပြုရင် များသောအားဖြင့် JSON data တွေ response ပြန်ပေးပါတယ်။ JSON အကြောင်း အသေးစိတ် သိချင်ရင် ဒီမှာ ဖတ်ကြည့်လို့ရပါတယ်။
GSON ဆိုတာကတော့ JSON ကို Java Object အဖြစ်ပြောင်းပေးတဲ့ library တစ်ခုပါ။ GSON အကြောင်း အသေးစိတ် သိချင်ရင် ဒီမှာ ဖတ်ကြည့်လို့ရပါတယ်။

ApiService Interface တည်ဆောက်ခြင်း

ဒုတိယအဆင့်အနေနဲ့ APIService interface ဆောက်ပေးရပါမယ်။ အခု tutorial မှာ book list ကို API ကတစ်ဆင့် ခေါ်ပြီး Android UI မှာ ပြပေးမှာ ဖြစ်ပါတယ်။
စာအုပ် data တွေကို API ကယူမှာဖြစ်တဲ့အတွက် BookApiService ဆိုတဲ့ interface လေးကို ဆောက်ပါမယ်။

BookApiService (source code)

import com.zwenex.androidarchisample.db.entity.BookEntity;
import java.util.List;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.http.GET;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
import retrofit2.http.Query;

/**
 * Network API Service for Book.
 * All APIs for Book are defined here.
 */
public interface BookApiService {

    /**
     * Use @GET annotation to make a GET Request.
     * If the api requires a parameter, use @Query(param_name).
     */
    @GET("api/v1/getBook")
    Call<BookEntity> getBook(@Query("id") int id);

    @GET("api/v1/getBooks")
    Call<List<BookEntity>> getBooks();

    /**
     * Use @POST annotation to make a POST Request.
     * The @Multipart and @Part annotations are required only when making file uploads.
     * If your post request does not have file upload you use like:
     *
     *  @FormUrlEncoded
     *  @POST("api/v1/createBook")
     *  Call<BookEntity> createBook(
     *      @Field("title") String title,
     *      @Field("description") String description
     *  )
     */
    @Multipart
    @POST("api/v1/createBook")
    Call<BookEntity> createBook(
            @Part("title") RequestBody title,
            @Part("description") RequestBody description,
            @Part("author_id") RequestBody authorId,
            @Part MultipartBody.Part cover
    );
}

GET Method

ပထမဆုံး getBook ဆိုတဲ့ method ကတော့ API ကနေ စာအုပ်တစ်အုပ်ရဲ့ data ကို request လုပ်တဲ့ method ပါ။ Retrofit မှာ GET method ကို အသုံးပြုပြီး request လုပ်မယ်ဆိုရင် @GET annotation ကိုသုံးပေးရပါတယ်။ Parameter အနေနဲ့ကတော့ API ရဲ့ path ကိုထည့်ပေးရပါတယ်။ GET method မှာ query ထည့်ပေးလိုက်ချင်တယ်ဆိုရင် method ရဲ့ parameter ထဲကနေ @Query ဆိုတဲ့ annotation နဲ့ထည့်ပေးလို့ရပါတယ်။ @Query မှာလဲ query ရဲ့ field name ကို parameter အဖြစ် ထည့်ပေးရပါတယ်။
ဒီ method ကိုခေါ်လိုက်ရင် network request က http://bookapi.tiide.org/api/v1/getBook?id=[[id]] အနေနဲ့ထွက်သွားမှာပါ။ [[id]] ဆိုတဲ့နေရာမှာ query ကနေထည့်ပေးလိုက်တဲ့ value ဝင်သွားမှာပါ။ ဥပမာ getBook(11) ဆိုပြီးတော့ ခေါ်လိုက်ရင် http://bookapi.tiide.org/api/v1/getBook?id=11 ဆိုပြီး ထွက်သွားမှာပါ။
အဲ့လို API ကို Request ပို့လိုက်ရင် API က JSON data response ပြန်ပါတယ်။ အဲ့ဒီ response က ချက်ချင်းပြန်ရောက်လာတာမဟုတ်တဲ့အတွက် Call အနေနဲ့ လက်ခံရပါတယ်။ ဒီမှာကတော့ Retrofit ရဲ့ Call အနေနဲ့လက်ခံပါတယ်။ Call လက်ခံတဲ့အခါမှာ response ပြန်လာတဲ့ JSON ကို GSON Converter က Java Object အဖြစ် အလိုအလျောက်ပြောင်းပေးတဲ့အတွက် Call အနေနဲ့ လက်ခံရပါတယ်။
ဒီ sample မှာတော့ BookEntity object [Source Code] ပုံစံအတိုင်း ပြန်ပေးတဲ့အတွက် Call<BookEntity> အနေနဲ့ လက်ခံပေးရပါတယ်။

ဒုတိယ getBooks() method ကလည်း ပထမပုံစံအတိုင်း GET request ပဲဖြစ်တဲ့အတွက် @GET annotation ထည့်ပေးရပါတယ်။ သူကတော့ query parameter မပါတဲ့အတွက် တခြား parameter တွေထည့်ပေးစရာ မလိုပါဘူး။ ဒီ method ကို ခေါ်လိုက်ရင် http://bookapi.tiide.org/api/v1/getBooks ဆိုပြီး request ထွက်သွားမှာဖြစ်ပါတယ်။ ဒီ request က စာအုပ်တွေကို Array နေနဲ့ JSON response ပြန်ပေးတဲ့အတွက် Call<List<BookEntity>> ဆိုပြီး လက်ခံပေးရပါတယ်။

POST Method

နောက်တစ်ခုကတော့ POST method ပါ။ POST method ကို FormUrlEncoded နဲ့ FormData ပုံစံ ၂ မျိုးနဲ့ ခေါ်လို့ရပါတယ်။ Retrofit မှာ File Upload မပါတဲ့ POST method တွေကို FormUrlEncoded နဲ့ သုံးလေ့ရှိပြီး FileUpload ပါရင်တော့ Multipart FormData နဲ့ သုံးရပါတယ်။
FormUrlEncoded နဲ့ POST လုပ်မယ်ဆိုရင်တော့ @FormUrlEncoded annotation ထည့်ပေးရပြီးတော့ form ထဲမှာ ထည့်ပေးချင်တဲ့ data တွေကို @Field annotation သုံးပြီးတော့ Field name ကို annotation parameter အဖြစ်ထည့်ပေးရပါတယ်။ Field data type တွေက String တွေ int တွေသုံးလို့ရပါတယ်။

File upload ပါလို့ Multipart သုံးမယ်ဆိုရင်တော့ @Multipart annotation ထည့်ပေးရပြီးတော့ form ထဲထည့်ပေးချင်တဲ့ data တွေကို @Part annotation သုံးပြီး ထည့်ပေးရပါတယ်။ ဒီနေရာမှာတော့ String တို့ int တို့လို data type တွေကို တိုက်ရိုက်သုံးလို့မရတော့ပါဘူး။ အကုန်လုံးကို RequestBody အဖြစ်ပြောင်းပေးပြီးတော့မှ သုံးလို့ရပါတယ်။ Upload လုပ်မယ့် file ကိုတော့ MultipartBody.Part အနေနဲ့ ထည့်ပေးရပါတယ်။

Comments

  • edited June 21

    Network Request စတင်ပြုလုပ်ခြင်း

    RetrofitClient နဲ့ ApiService တွေဆောက်ပြီးပြီဆိုရင်တော့ data လိုချင်တဲ့နေရာကနေ network request တွေစတင်ပြုလုပ်နိုင်ပါပြီ။ Network request တွေကို Activity မှာဖြစ်စေ Fragment မှာဖြစ်စေ Service တွေမှာဖြစ်စေ data လိုချင်တဲ့နေရာကနေ RetrofitClient instance ကတစ်ဆင့် ပြုလုပ်နိုင်ပါတယ်။
    Network request တွေက network ရဲ့ speed ပေါ်မူတည်ပြီး response ပြန်ချိန် ကြာနိုင်တဲ့အတွက် data မရောက်ခင်မှာ UI ရပ်မနေအောင် Callback ကိုသုံးရပါတယ်။

    BookRepository (source code)

    RetrofitClient.getInstance().create(BookApiService.class).getBooks().enqueue(new Callback<List<BookEntity>>() {
    
                /** Performs tasks inside the onResponse block if the network call is successful. */
                @Override
                public void onResponse(Call<List<BookEntity>> call, Response<List<BookEntity>> 
                    if(response.body()!=null) {
                        List<BookEntity> bookList = response.body();
                    }
                }
    
                /** Performs tasks inside the onFailure block if the network call failed. */
                @Override
                public void onFailure(Call<List<BookEntity>> call, Throwable t) {
                    Log.e("NETWORK_RESPONSE", "Something went wrong.");
                    t.printStackTrace();
                }
            });
    

    BookApiService ကို RetrofitClient ကတစ်ဆင့်ခေါ်ပြီး လိုအပ်တဲ့ method ကို ခေါ်ရပါတယ်။ ခေါ်တဲ့ method ရဲ့ response ကိုလိုချင်ရင် enqueue လုပ်ပြီး Callback ဆောက်ပေးရပါတယ်။ Network call success ဖြစ်ခဲ့ရင် Callback ရဲ့ onResponse block ထဲဝင်ပြီးတော့ တစ်စုံတစ်ခုကြောင့် network call fail ခဲ့ရင် onFailure block ထဲဝင်ပါတယ်။ response ရတဲ့ data တွေကို လိုချင်ရင် response.body() ထဲကနေယူလို့ရပါတယ်။

    Multipart နဲ့ POST method ကိုသုံးတဲ့အခါမှာတော့ parameter တွေကို RequestBody ပြောင်းပေးရပါတယ်။

    NewBookActivity (source code)

            RequestBody titleBody = RequestBody.create(MediaType.parse("text/plain"), "Book Title");
            RequestBody descriptionBody = RequestBody.create(MediaType.parse("text/plain"), "Some Description");
            RequestBody authorBody = RequestBody.create(MediaType.parse("text/plain"), 2);
    
            //Convert bookCover file into Multipart
            MultipartBody.Part multiPartCover = null;
            if(bookCover!=null) {
                RequestBody cover = RequestBody.create(
                        MediaType.parse(FileUtils.getMimeType(bookCover.getAbsolutePath())), bookCover
                );
                multiPartCover = MultipartBody.Part.createFormData("cover", bookCover.getName(), cover);
            }
    
            /** Makes POST request in BookApiService through RetrofitClient instance */
            RetrofitClient.getInstance().create(BookApiService.class)
                    .createBook(titleBody,descriptionBody, authorBody, multiPartCover)
                    .enqueue(new Callback<BookEntity>() {
    
                        /** Performs tasks inside the onResponse block if the network call is successful. */
                        @Override
                        public void onResponse(Call<BookEntity> call, Response<BookEntity> response) {
                            if(response.body()!=null){
                                  //user response.body() for result
                            }
                        }
    
                        /** Performs tasks inside the onFailure block if the network call failed. */
                        @Override
                        public void onFailure(Call<BookEntity> call, Throwable t) {
                            t.printStackTrace();
                            Toast.makeText(NewBookActivity.this, "Failed to create book.", Toast.LENGTH_SHORT).show();
                        }
              });
    
Sign In or Register to comment.