הדוגמה וההדרכה של דוגמת Retrofit ב-Android

ברוך הבא להדרכת מבוא Android Retrofit. היום נשתמש בספרייה Retrofit שפותחה על ידי Square כדי לטפל בקריאות ל-REST API באפליקציית Android שלנו.

Retrofit Android

Retrofit היא REST client מסוג טייפ-סייף לאנדרואיד ולג'אווה שמטרתה להקל על צריבת שירותי web RESTful. לא נכנס לפרטים של גרסאות Retrofit 1.x ונדבוק ישירות ל-Retrofit 2 שיש לו הרבה תכונות חדשות וממשק API פנימי ששונה בהשוואה לגרסאות הקודמות. Retrofit 2 משתמש ב-OkHttp כשכבת הרשת ונבנית על גביה. Retrofit מסירה אוטומטית את תגובת ה-JSON על ידי שימוש ב-POJO (Plain Old Java Object) שחייב להיות מוגדר מראש עבור מבנה ה-JSON. כדי לסרוג ולהמיר את ה-JSON אנו זקוקים לממיר כדי להמיר אותו ל-Gson תחילה. עלינו להוסיף את התלות הבאות בקובץ build.grade שלנו.

compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.google.code.gson:gson:2.6.2'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'

תלות ב-OkHttp כבר נשלחו עם תלות ב-Retrofit 2. אם ברצונך להשתמש בתלות ב-OkHttp נפרדות, יש לאפשר את תלות ב-OkHttp מתלות ב-Retrofit 2 כך:

compile ('com.squareup.retrofit2:retrofit:2.1.0') {  
  // הוצא את תלות ב-OkHttp של Retrofit והגדר מודול משלך
  exclude module: 'okhttp'
}
compile 'com.google.code.gson:gson:2.6.2'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
compile 'com.squareup.okhttp3:okhttps:3.4.1'
  • ה-interceptor של הלוגינג יוצר מחרוזת לוג של כל התגובה שמוחזרת.
  • ישנם ממירים נוספים לפרסום את ה-JSON לסוג הנדרש. כמה מהם מופיעים למטה.
  1. Jackson : com.squareup.retrofit2:converter-jackson:2.1.0
  2. Moshi : com.squareup.retrofit2:converter-moshi:2.1.0
  3. Protobuf : com.squareup.retrofit2:converter-protobuf:2.1.0
  4. Wire : com.squareup.retrofit2:converter-wire:2.1.0
  5. פשוט XML : com.squareup.retrofit2:converter-simplexml:2.1.0

הוסף הרשאה לגישה לאינטרנט בקובץ AndroidManifest.xml.

אינטרספטורים של OkHttp

האינטרספטורים הם מנגנונים חזקים הקיימים ב-OkHttp שיכולים לעקוב, לשנות ולנסות שוב שיחות. האינטרספטורים יכולים להתחלק באופן ראשי לשני סוגים:

  • אינטרספטורים של אפליקציה : כדי לרשום אינטרספטור של אפליקציה, עלינו לקרוא addInterceptor() על OkHttpClient.Builder
  • אינטרספטורים של רשת : כדי לרשום אינטרספטור של רשת, עלינו לקרוא addNetworkInterceptor() במקום addInterceptor()

הגדרת ממשק Retrofit

package com.journaldev.retrofitintro;

import com.journaldev.retrofitintro.pojo.MultipleResource;

import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

class APIClient {

    private static Retrofit retrofit = null;

    static Retrofit getClient() {

        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();


        retrofit = new Retrofit.Builder()
                .baseUrl("https://reqres.in")
                .addConverterFactory(GsonConverterFactory.create())
                .client(client)
                .build();



        return retrofit;
    }

}

השיטה getClient() בקוד למעלה תיקרא בכל פעם בעת הגדרת ממשק Retrofit. Retrofit מספקת רשימת אנוטציות עבור כל אחת משיטות ה-HTTP: @GET, @POST, @PUT, @DELETE, @PATCH או @HEAD. בואו נראה איך כיתת ה-APIInterface.java שלנו נראית.

package com.journaldev.retrofitintro;

import com.journaldev.retrofitintro.pojo.MultipleResource;
import com.journaldev.retrofitintro.pojo.User;
import com.journaldev.retrofitintro.pojo.UserList;

import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.Query;

interface APIInterface {

    @GET("/api/unknown")
    Call<MultipleResource> doGetListResources();

    @POST("/api/users")
    Call<User> createUser(@Body User user);

    @GET("/api/users?")
    Call<UserList> doGetUserList(@Query("page") String page);

    @FormUrlEncoded
    @POST("/api/users?")
    Call<UserList> doCreateUserWithField(@Field("name") String name, @Field("job") String job);
}

בכיתה למעלה, הגדרנו כמה שיטות שבוצעות בקשות HTTP עם אנוטציות. @GET("/api/unknown") קוראת ל-doGetListResources();. doGetListResources() הוא שם השיטה. MultipleResource.java הוא מודל POJO לאובייקט התגובה שלנו שמשמש למפה את פרמטרי התגובה למשתנים המתאימים להם. מחלקות POJO אלו פועלות כסוג ההחזרה של השיטה. מחלקת POJO פשוטה עבור MultipleResources.java ניתנת למטה.

package com.journaldev.retrofitintro.pojo;

import com.google.gson.annotations.SerializedName;

import java.util.ArrayList;
import java.util.List;

public class MultipleResource {

    @SerializedName("page")
    public Integer page;
    @SerializedName("per_page")
    public Integer perPage;
    @SerializedName("total")
    public Integer total;
    @SerializedName("total_pages")
    public Integer totalPages;
    @SerializedName("data")
    public List<Datum> data = null;

    public class Datum {

        @SerializedName("id")
        public Integer id;
        @SerializedName("name")
        public String name;
        @SerializedName("year")
        public Integer year;
        @SerializedName("pantone_value")
        public String pantoneValue;

    }
}

האנוטציה @SerializedName משמשת לציין את שם השדה שנמצא במענה JSON. הצג את מחלקת ה-POJO והעתק אותה לתוך מבנה הפרויקט שלך ב-Android Studio. מחלקות ה-POJO מעוטרות בתוך מחלקת Retrofit מסוג Call. הערה: מערך JSONArray מוסרס כרשימת אובייקטים במחלקות ה-POJO פרמטרי השיטה: ישנן מגוון רחב של אפשרויות אפשריות של פרמטרים להעביר בתוך שיטה:

  • @Body – שולח אובייקטים של Java כגוף בקשה.
  • @Url – משתמש ב-URLים דינמיים.
  • @Query – נוכל פשוט להוסיף פרמטר של שיטה עם @Query ושם של פרמטר שאליה יוצג הסוג. כדי להצפין שאילתה ב-URL, נשתמש בצורה הבאה: @Query(value = "auth_token", encoded = true) String auth_token
  • @Field – שליחת נתונים כ-form-urlencoded. זה דורש את ההוראה @FormUrlEncoded מחוברת לשיטה. פרמטר ה-@Field עובד רק עם POST

הערה: @Field דורש פרמטר חובה. במקרים בהם @Field הוא אופציונלי, נוכל להשתמש ב-@Query במקום ולהעביר ערך של null.

מבנה פרויקט לדוגמא של Retrofit ב-Android

חבילת pojo מגדירה ארבע דגמים לכל אחת מתגובות נקודת הקצה שמוגדרות בממשק APIInterface.java. User.java

package com.journaldev.retrofitintro.pojo;

import com.google.gson.annotations.SerializedName;

public class User {

    @SerializedName("name")
    public String name;
    @SerializedName("job")
    public String job;
    @SerializedName("id")
    public String id;
    @SerializedName("createdAt")
    public String createdAt;

    public User(String name, String job) {
        this.name = name;
        this.job = job;
    }


}

המחלקה למעלה משמשת ליצירת גוף תגובה עבור שיטת createUser() UserList.java

package com.journaldev.retrofitintro.pojo;

import com.google.gson.annotations.SerializedName;

import java.util.ArrayList;
import java.util.List;

public class UserList {

    @SerializedName("page")
    public Integer page;
    @SerializedName("per_page")
    public Integer perPage;
    @SerializedName("total")
    public Integer total;
    @SerializedName("total_pages")
    public Integer totalPages;
    @SerializedName("data")
    public List<Datum> data = new ArrayList();

    public class Datum {

        @SerializedName("id")
        public Integer id;
        @SerializedName("first_name")
        public String first_name;
        @SerializedName("last_name")
        public String last_name;
        @SerializedName("avatar")
        public String avatar;

    }
}

CreateUserResponse.java

package com.journaldev.retrofitintro.pojo;

import com.google.gson.annotations.SerializedName;

public class CreateUserResponse {

    @SerializedName("name")
    public String name;
    @SerializedName("job")
    public String job;
    @SerializedName("id")
    public String id;
    @SerializedName("createdAt")
    public String createdAt;
}

הקובץ MainActivity.java הוא המקום בו אנו קוראים לכל נקודות הקצה שמוגדרות בממשק ומציגים כל שדה ב-Toast/TextView.

package com.journaldev.retrofitintro;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;

import com.journaldev.retrofitintro.pojo.CreateUserResponse;
import com.journaldev.retrofitintro.pojo.MultipleResource;
import com.journaldev.retrofitintro.pojo.User;
import com.journaldev.retrofitintro.pojo.UserList;

import java.util.List;

import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;

public class MainActivity extends AppCompatActivity {

    TextView responseText;
    APIInterface apiInterface;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        responseText = (TextView) findViewById(R.id.responseText);
        apiInterface = APIClient.getClient().create(APIInterface.class);


        /**
         GET List Resources
         **/
        Call<MultipleResource> call = apiInterface.doGetListResources();
        call.enqueue(new Callback<MultipleResource>() {
            @Override
            public void onResponse(Call<MultipleResource> call, Response<MultipleResource> response) {


                Log.d("TAG",response.code()+"");

                String displayResponse = "";

                MultipleResource resource = response.body();
                Integer text = resource.page;
                Integer total = resource.total;
                Integer totalPages = resource.totalPages;
                List<MultipleResource.Datum> datumList = resource.data;

                displayResponse += text + " Page\n" + total + " Total\n" + totalPages + " Total Pages\n";

                for (MultipleResource.Datum datum : datumList) {
                    displayResponse += datum.id + " " + datum.name + " " + datum.pantoneValue + " " + datum.year + "\n";
                }

                responseText.setText(displayResponse);

            }

            @Override
            public void onFailure(Call<MultipleResource> call, Throwable t) {
                call.cancel();
            }
        });

        /**
         Create new user
         **/
        User user = new User("morpheus", "leader");
        Call<User> call1 = apiInterface.createUser(user);
        call1.enqueue(new Callback<User>() {
            @Override
            public void onResponse(Call<User> call, Response<User> response) {
                User user1 = response.body();

                Toast.makeText(getApplicationContext(), user1.name + " " + user1.job + " " + user1.id + " " + user1.createdAt, Toast.LENGTH_SHORT).show();

            }

            @Override
            public void onFailure(Call<User> call, Throwable t) {
                call.cancel();
            }
        });

        /**
         GET List Users
         **/
        Call<UserList> call2 = apiInterface.doGetUserList("2");
        call2.enqueue(new Callback<UserList>() {
            @Override
            public void onResponse(Call<UserList> call, Response<UserList> response) {

                UserList userList = response.body();
                Integer text = userList.page;
                Integer total = userList.total;
                Integer totalPages = userList.totalPages;
                List<UserList.Datum> datumList = userList.data;
                Toast.makeText(getApplicationContext(), text + " page\n" + total + " total\n" + totalPages + " totalPages\n", Toast.LENGTH_SHORT).show();

                for (UserList.Datum datum : datumList) {
                    Toast.makeText(getApplicationContext(), "id : " + datum.id + " name: " + datum.first_name + " " + datum.last_name + " avatar: " + datum.avatar, Toast.LENGTH_SHORT).show();
                }


            }

            @Override
            public void onFailure(Call<UserList> call, Throwable t) {
                call.cancel();
            }
        });


        /**
         POST name and job Url encoded.
         **/
        Call<UserList> call3 = apiInterface.doCreateUserWithField("morpheus","leader");
        call3.enqueue(new Callback<UserList>() {
            @Override
            public void onResponse(Call<UserList> call, Response<UserList> response) {
                UserList userList = response.body();
                Integer text = userList.page;
                Integer total = userList.total;
                Integer totalPages = userList.totalPages;
                List<UserList.Datum> datumList = userList.data;
                Toast.makeText(getApplicationContext(), text + " page\n" + total + " total\n" + totalPages + " totalPages\n", Toast.LENGTH_SHORT).show();

                for (UserList.Datum datum : datumList) {
                    Toast.makeText(getApplicationContext(), "id : " + datum.id + " name: " + datum.first_name + " " + datum.last_name + " avatar: " + datum.avatar, Toast.LENGTH_SHORT).show();
                }

            }

            @Override
            public void onFailure(Call<UserList> call, Throwable t) {
                call.cancel();
            }
        });

    }
}

apiInterface = APIClient.getClient().create(APIInterface.class); משמש ליצירת אינסטנס של ה-APIClient. כדי למפות את מחלקת המודל לתגובה, אנו משתמשים ב: MultipleResource resource = response.body(); הרצת היישום תקרא לכל אחת מנקודות הקצה ותציג הודעת Toast עבורן בהתאם. זה מסיים את הדוגמה לשימוש ב-Retrofit באנדרואיד. ניתן להוריד את פרויקט הדוגמה של Retrofit ל-Android מהקישור למטה.

הורדת פרויקט דוגמה של Retrofit ל-Android

Source:
https://www.digitalocean.com/community/tutorials/retrofit-android-example-tutorial