استارت‌آپ و کارآفرینی

درآمدی بر Retrofit2 و ویژگی‌های آن – قسمت اول

Retrofit یکی از مشهورترین کتابخانه‌های کلاینت HTTP برای اندروید است که از لحاظ عملکرد از بسیاری کتابخانه‌های دیگه بهتر و سریع‌تر عمل می‌کنه. کار کردن باهاش هم ساده‌ است. این کتابخانه اخیرا و بخصوص در نسخه‌ دو با تغییرات زیادی همراه بوده. نسخه دو که چند ماهی از ارائه اون بیشتر نمی‌گذره به سرعت در حال تغییر هست. موقع استفاده از چنین کتابخانه‌هایی حتما ChangeLogهای اونا رو بررسی کنید. برای مثال تغییرات نسخه بتا این کتابخانه بسیار زیاد هست.

در این قسمت به چندتا از ویژگی‌هایی که در این نسخه اضافه‌شده و یا تغییر پیدا کرده اشاره می‌کنم و در قسمت بعدی مثالی عملی ارائه می‌شه که سورس اون رو در github قرار خواهم داد.

از تغییرات قابل توجه اینکه در Retrofit نسخه یک امکانی برای کنسل‌کردن درخواست ارسال‌شده به سرور وجود نداشت یا حداقل به‌راحتی قابل پیاده‌سازی نبود، این امکان از نسخه دو به بعد ارائه شده و به راحتی می‌توان این کار رو انجام داد.

آخرین نسخه این کتابخانه که نزدیک به بیست روز پیش ارائه شده رو توسط خط زیر می‌تونید به پروژه‌تون اضافه کنید:

compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'

با اتمام sync شدن gradle می‌تونید از این کتابخانه در برنامه‌تون استفاده‌ کنید.

تعریف توابع سرویس

در بخش تعریف سرویس Retrofit نسخه یک باید سرویس‌های همگام و ناهمگام رو به صورت جداگانه تعریف می‌کردیم ولی در نسخه دو تنها به یک صورت تعریف می‌شوند:

/* Synchronous in Retrofit 1.9 */
 
public interface APIService {
 
    @POST("/list")
    Repo loadRepo();
 
}
/* Asynchronous in Retrofit 1.9 */
 
public interface APIService {
 
    @POST("/list")
    void loadRepo(Callback<Repo> cb);
 
}

در Retrofit2

import retrofit.Call;
 
/* Retrofit 2.0 */
 
public interface APIService {
 
    @POST("/list")
    Call<Repo> loadRepo();
 
}

برای اجرا سرویس های ایجاد شده در نسخه دو به صورت همگام از تابع execute و برای اجرا به صورت ناهمگام از تابع enqueue استفاده می‌شود.

درخواست به صورت همگام

// Synchronous Call in Retrofit 2.0
 
Call<Repo> call = service.loadRepo();
try {
 Repo repo = call.execute();
} catch (IOException e) {
 e.printStackTrace();
}

درخواست به صورت ناهمگام

// Asynchronous Call in Retrofit 2.0

Call<Repo> call = service.loadRepo();
call.enqueue(new Callback<Repo>() {
    @Override
    public void onResponse(Call<Repo> call,Response<Repo> response) {
        // Get result Repo from response.body()
    }
 
    @Override
    public void onFailure(Call<Repo> call,Throwable t) {
 
    }
});

کد بالا در background اجرا می‌شه و وقتی که نتیجه بدست اومد اون رو می‌شه با تابع ()response.body در دسترس داشت. دقت داشته باشید که باید هر دو تابع onResponse و onFailure در داخل Main Thread اجرا شوند. روش پیشنهاد شده برای اجرا توابع سرویس Retrofit به صورت ناهمگام هست این نوع درخواست‌ها با روند کاری برنامه‌های اندروید سازگارتر هستند.

لغو درخواست از سرور

ایده اصلی عوض‌کردن الگو کتابخانه به استفاده از Call این است که روشی برای کنسل‌کردن درخواست از سرور فراهم شود. این کار توسط تابع cancel به صورت زیر انجام می‌شود:

call.cancel();

روش تبدیل جدید – خارج شدن کتابخانه GsonConverter‌ از Retrofit

یکی دیگه از تغییرات مهم در نسخه دو خارج‌شدن کتابخانه GsonConverter از داخل کتابخانه Retrofit است. در نسخه یک به صورت پیش‌فرض این کتابخانه برای تبدیل دیتاهای دریافت شده از درخواست به کلاس جاوا یا اصطلاحا POJO استفاده می‌شد ولی اکنون در نسخه دو باید در داخل فایل gradle یکی از تبدیل کننده‌های رسمی Retrofit رو تعریف کرد. برای مثال چنانچه بخواهیم از GsonConverter استفاده کنیم باید خط زیر رو به gradle اضافه کنیم:

compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'

با استفاده از تابع addConverterFactory می‌توان نوع تبدیل‌کننده را به Retrofit معرفی کرد. دقت کنید که کلاس RestAdapter هم به Retrofit تغییر نام داده‌است:

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://api.nuuneoi.com/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();
APIService apiService = retrofit.create(APIService.class);

تعدادی از کتابخانه‌های تبدیل کننده برای Retrofit به صورت زیر است، می‌توانید بنابر نیاز از هرکدام استفاده کنید:

GSON: com.squareup.retrofit2:converter-gson:2.0.0-beta4
Moshi: com.squareup.retrofit2:converter-moshi:2.0.0-beta4
Jackson: com.squareup.retrofit2:converter-jackson:2.0.0-beta4
SimpleXML: com.squareup.retrofit2:converter-simplexml:2.0.0-beta4
ProtoBuf: com.squareup.retrofit2:converter-protobuf:2.0.0-beta4
Wire: com.squareup.retrofit2:converter-wire:2.0.0-beta4

روش جدید آدرس دهی به سرور

یکی دیگه از تغییرات در نسخه دو تغییر روش تعریف baseUrl برای معرفی آدرس سرور است. در این بخش تابع setEndpoint به تابع baseUrl تغییر نام داده‌است. تعدادی از مثال‌های اون در زیر آورده می‌شه:

apiservice1 apiservice2 apiservice3

روش پیشنهادی برای معرفی آدرس سرور به صورت زیر است:

Base URL: آدرس همیشه به / ختم بشه.

Url@: همیشه بدون / شروع بشه.

برای نمونه:

public interface APIService {
    @POST("user/list")
    Call<Users> loadUsers();
}
 
public void doSomething() {
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("http://api.nuuneoi.com/base/")
            .addConverterFactory(GsonConverterFactory.create())
            .build();
 
    APIService service = retrofit.create(APIService.class);
}

تابع loadUsers دیتای خودش رو از آدرس http://api.nuuneoi.com/base/user/list دریافت می‌کنه. یادتون باشه حتما بعد از آپگرید به نسخه دو Retrofit روش آدرس دهی رو هم عوض کنید.

اضافه شدن OkHttp‌ به صورت پیش‌فرض

در نسخه یک برای استفاده از کتابخانه OkHttp باید اون رو به صورت دستی در بخش gradle اضافه می‌کردید ولی در نسخه دو به بعد این نیازمندی به صورت پیش فرض در بعش gradle کتابخانه Retrofit اضافه‌شده و نیاز به کار خاصی نداره. الگوی Call که بالا در موردش حرف زدیم، در همین کتابخانه OkHttp پیاده‌سازی‌شده است.

تغییر بعدی که بعد از دیدن نسخه beta4 بهش مردد شدم در ادامه آورده شده، یکی از مهم‌ترین تغییرات هم هست. اگه احساس می‌کنید که اشتباه دارم ذکر می‌کنم در کامنت‌ها بهم یادآوری کنید.

اجرا شدن تابع onResponse به همراه تابع onFailure در صورت بروز خطا

در نسخه دو و حداقل تا نسخه beta2 چنان‌چه در زمان دریافت پاسخ به مشکل بر می‌خوردید یعنی دریافت نتیجه با خطا مواجه می‌شد، هم تابع onReponse و هم تابع onFailure اجرا می‌شدند، اما در نسخه یک با بروز خطا تابع onResponse اجرا نمی‌شد. در لینک منبع این مقاله هم چنانچه دقت کنید همین توضیح رو داده که البته مقاله قدیمی هست و مربوط به beta2 هست.

در مثالی که برای Retrofit2 آماده کردم در نسخه beta4 گویا اینگونه نیست و تابع onReponse دیگه در زمان onFailure اجرا نخواهد شد و برعکس.
از مشکلاتی که ایجاد می‌کرد این بود که چنانچه مقدار بازگشتی یعنی response رو چک نمی‌کردید در زمان خطا، تابع ()response.body مقدار null برمی‌گردوند و بعضی وقت‌ها باعث فورس‌کلوز می‌شه.
response
فارغ از چگونگی رفتار تابع enqueue این رو مد نظر داشته باشید که در نسخه دو، این بخش تغییر زیادی پیدا کرده از تغییر نام توابع گرفته تا روش برخورد با مقادیر بازگشتی و بروز خطاها. تا حد امکان با استفاده از چک‌کردن مقادیر و استفاده از try catch از بروز خطاهای منجر به بسته‌شدن برنامه دوری کنید. منابع رو هم مطالعه کنید، اطلاعات بیشتری وجود داره. همچنین در مثال ارائه شده این بررسی‌ها لحاظ شده است.

منابع: + +

7 نظرات
  1. ستار می گوید

    سلام جناب خضری من تو یه پروژه از volley استفاده کردم و یه سری کدها مربوط به کلاس application دارم میخوام retrofit2 تبدیل کنم : آیا میتونید کمکم کنید: کدهای volley:
    MyApplication.java
    package info.androidhive.smsverification.app;

    import android.app.Application;
    import android.text.TextUtils;

    import com.android.volley.Request;
    import com.android.volley.RequestQueue;
    import com.android.volley.toolbox.Volley;

    /**
    * Created by Ravi on 13/05/15.
    */

    public class MyApplication extends Application {

    public static final String TAG = MyApplication.class
    .getSimpleName();

    private RequestQueue mRequestQueue;

    private static MyApplication mInstance;

    @Override
    public void onCreate() {
    super.onCreate();
    mInstance = this;
    }

    public static synchronized MyApplication getInstance() {
    return mInstance;
    }

    public RequestQueue getRequestQueue() {
    if (mRequestQueue == null) {
    mRequestQueue = Volley.newRequestQueue(getApplicationContext());
    }

    return mRequestQueue;
    }

    public void addToRequestQueue(Request req, String tag) {
    req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);
    getRequestQueue().add(req);
    }

    public void addToRequestQueue(Request req) {
    req.setTag(TAG);
    getRequestQueue().add(req);
    }

    public void cancelPendingRequests(Object tag) {
    if (mRequestQueue != null) {
    mRequestQueue.cancelAll(tag);
    }
    }
    }

    1. بهروز خضری می گوید

      سلام،
      من آشنایی با volley ندارم متاسفانه. اینکه کدتون رو اینطوری اینجا کپی پیست کردید، هیچ کمکی نمی‌کنه چون به شدت ناخوانا هست. به صورت یک Gist توو فروم‌ها سوال‌تون رو بپرسید امیدوارم کسی باشه بتونه کمک تون کنه.

  2. امید می گوید

    سلام آیا توی retrofit راهی برای sort کردن یک فیلد هست؟

  3. وحید می گوید

    سپاس

  4. محمد می گوید

    سلام من تازه دارم retrofit رو یاد میگیرم نمیدونم چرا خطای Use JsonReader.setLenient(true) to accept malformed JSON at line 1 column 1 path $ میده و من مطمعنم این خطا از طرف سروره نه کد های retrotit چون وقتی از ادرس http://thearash.net/list_of_blogs.php استفاده می کنم درست کار میکنه ولی وقتی از ادرس http://yousef98.gigfa.com/this استفاده میکنم این خطا رو میده ممنون میشم کمکم کنید. مرسی

    1. بهروز خضری می گوید

      سلام،
      مشکل از گیگفا هست.
      لطفا از یک هاست غیر رایگان استفاده کنید.

      1. محمد می گوید

        ممنون بابت جوابتون

ارسال یک پاسخ

آدرس ایمیل شما منتشر نخواهد شد.