آموزش استفاده از ckeditor همراه با آپلود عکس در لاراول 6
آموزش استفاده از ckeditor همراه با آپلود عکس در لاراول 6
2020-02-17
Pagination یا صفحه بندی در لاراول 6 با Collection یا آرایه
Pagination یا صفحه بندی در لاراول 6 با Collection یا آرایه
2020-02-26

آموزش Caching یا کشینگ یا کش کردن داده ها با رتروفیت در اندروید ، کش کردن کردن داده ها یک روش معمول در تمامی پلتفرم هاست برای ذخیره موقت داده ها در دستگاه اجرا کننده برنامه برای استفاده مجدد در زمان هایی که دستگاه به اینترنت متصل نیست و یا میخواهد یک سری داده های تکراری را از سرور دریافت کند.

آموزش Caching یا کش کردن داده ها با رتروفیت در اندروید

در این مقاله ما بطور کامل آموزش خواهیم داد که چطور داده های دریافتی را با استفاده از رتروفیت بطور موقت کش کنید و در مواقع نیاز از آن استفاده کنید و نکته جالب درباره این نوع کش کردن این است که هیچ نیازی به دیتابیس ندارید !

چرا باید داده ها را کش کنیم ؟

  • مصرف پهنای باند را کاهش می دهد.
  • مدت زمانی که باید منتظر ماند تا پاسخ را از سرور دریافت کرد را بطور محسوسی کاهش خواهد داد.
  • بار سرور که در اثر ترافیک های اضافی بوجود می آید را کاهش می دهد.
  • اگر شما نیاز به داده هایی دارید که اخیرا از سرور دریافت کردید، دیگر نیازی به درخواست دوبار از سرور نخواهد بود چرا که می توانید همان داده های کش شده را بازیابی و استفاده کنید.

بنابراین با این مزایا گفته شده کش کردن داده ها یا اطلاعات جزو واجبات هر اپلیکیشن خواهد بود پس بی درنگ به سراغ آموزش پیاده سازی کش داده کردن داده ها با رتروفیت برویم.

نکته :
اگر اپلیکیشن شما جزو اپلکیشن هایی است که اطلاعات آن بصورت آنی به روز می شوند ترجیح داده می شود که از کش کردن داده ها استفاده نکنید و api های خودرا تا حد امکان بهینه کنید چرا که درخواست ها زیاد می شود و ممکن است بار سرور هم بالا رفته و سیستم شما به مشکل جدی بخورد.

اگر قبلا از رتروفیت استفاده کرده اید قطعا میدانید پیاده سازی بیس درخواست ها با رتروفیت چگونه خواهد بود اگر هم تا به حال با رتروفیت کار نکردید اکیدا پیشنهاد می شود قبل از شروع آموزش کش کردن داده ها با رتروفیت ، پست‌ آموزش رتروفیت از تجاری اپ را مطالعه کنید.

خب ما یک نمونه ( instance ) از رتروفیت و همچنین Gson ( برای تبدیل پاسخ یا ریسپانس دریافتی به کلاس های جاوا یا کاتلین )‌ بدین صورت ایجاد میکنیم

کاتلین

    val retrofit = Retrofit.Builder()
        .baseUrl("")
        .addConverterFactory(GsonConverterFactory.create())
        .build()

جاوا

    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("")
            .addConverterFactory(GsonConverterFactory.create())
            .build();

مشکل اصلی بلاک کد فوق که نمایان گر ساخت یک نمونه از Retrofit است این است که از OkHttpClient پیش فرض استفاده شده که این موضوع رابطه خوبی با کش کردن داده ها ندارد ! یا به عبارتی اصلا cache-friendly نیست.

درادامه ما یک نمونه از OkHttpClient ایجاد می کنیم که دارای قابلیت کش کردن داده ها یا به عبارتی cache-enable است و می تواند داده های دریافتی را بطور کارآمدی دریافت و مدیریت کند زمانی که :

  • دستگاه شما آفلاین است و هیچ دسترسی به اینترنت ندارد.
  • دستگاه نیاز به دسترسی به داده ای کاملا مشابه با داده ای دارد که در مدت زمان بسیار کمتری قبل آنرا دریافت کرده.

نحوه ساخت OkHttpClient بصورت cache-friendly

مرحله 1: تعریف یک متد جهت چک کردن دسترسی دستگاه به اینترنت

در مرحله اول باید دسترسی دستگاه به اینترنت را چک کنیم پس یک متد جهت چک کردن دسترسی دستگاه به اینترنت مشابه زیر ایجاد میکنیم

کاتلین

 fun hasNetwork(context: Context): Boolean? {
         var isConnected: Boolean? = false // Initial Value
         val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
         val activeNetwork: NetworkInfo? = connectivityManager.activeNetworkInfo
         if (activeNetwork != null && activeNetwork.isConnected)
             isConnected = true
         return isConnected
     }

جاوا

public boolean hasNetwork(Context context) {
         boolean isConnected = false;
         ConnectivityManager connectivityManager = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE));
         NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
         if (activeNetwork != null && activeNetwork.isConnected())
             isConnected = true;
         return isConnected;
     }

مرحله 2: تعیین سایز cache کش برای آموزش کش کردن داده ها با رتروفیت

در مرحله دوم باید سایز cache کش را مشخص کنیم در خط زیر ما مقدار 5MG برای کش مشخص کردیم و توجه داشته باشید که مقدار باید از نوع long باشد.

کاتلین

    val cacheSize = (5 * 1024 * 1024).toLong()

جاوا

    long cacheSize = (5 * 1024 * 1024);

مرحله 3: ایجاد متغیر کش cache

در مرحله سوم باید یک متغیر کش بسازیم که برای اینکار باید دایرکتوری کش به همراه سایز کش را به متغیر Cache پاس می دهیم و یک نمونه کش برای OkHttpClient بدین صورت میسازیم.

کاتلین

    val myCache = Cache(this.cacheDir, cacheSize)

جاوا

         Cache myCache = new Cache(this.getCacheDir(), cacheSize);

مرحله 4: ایجاد یک نمونه OkHttpClient با Interceptor

در مرحله چهارم کافی ست یک نمونه از OkHttpClient به همراه Interceptor بسازیم .ما باید یک Interceptor به نمونه OkHttpClient اضافه ( add ) کنیم تا درخواست ها را متناسب با تنظیماتی که ما برای آن مشخص می کنیم تغییر دهد و براساس آنها پاسخ های دریافتی را مدیریت کند.

در مثال زیر بطور کامل نحوه ساخت نمونه OkHttpClient به همراه اضافه کردن cache به آن آورده شده است:

کاتلین

    val okHttpClient = OkHttpClient.Builder()
        // مشخص کردن نمونه کشی که قبلا ایجاد کردیم
        .cache(myCache)
        // اضافه کردن یک Interceptor به OkHttpClient
        .addInterceptor { chain ->

            // گرفتن درخواست از chain
            var request = chain.request()

            // چک کردن متصل بودن دستگاه به اینترنت

            request = if (hasNetwork(this)!!)
            //اگر اینترتن متصل بود به اطلاعات کش که 5 ثانیه قبل ذخیره شده دسترسی پیدا کن
            // و اگر کش ذخیره شده از 5 ثانیه بیشتر بود سپس یک خطا را هدایت کن و ریسپانس را دریافت کن
            // پارامتر max-age به عبارتی نشان دهنده سن کش است
                request.newBuilder().header("Cache-Control", "public, max-age=" + 5).build()
            else
                // اگر دستگاه به اینترنت متصل نبود کش 7 روز پیش را بازیابی میکند
                // اگر کش 7 روز پیش در دسترس نبود سپس یک خطا نمایش داده میشود و درخواست دریافت مجدد اطلاعات را میکند
                // پارامتر only-if-cached مشخص کننده این مسئله برای درخواست است که تنها اطلاعات را از کش دریافت کن
                request.newBuilder().header(
                    "Cache-Control",
                    "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 7
                ).build()
            // در نهایت درخواست یا request تغییر یافته را به chain اضافه میکنیم
            chain.proceed(request)
        }
        .build()

جاوا

  OkHttpClient okHttpClient = new OkHttpClient.Builder()
            // مشخص کردن نمونه کشی که قبلا ایجاد کردیم
            .cache(myCache)
            // اضافه کردن یک Interceptor به OkHttpClient
            .addInterceptor(new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    // گرفتن درخواست از chain
                    Request request = chain.request();

                    // چک کردن متصل بودن دستگاه به اینترنت
                    if (hasNetwork(JavaMainActivity.this)){
                        //اگر اینترتن متصل بود به اطلاعات کش که 5 ثانیه قبل ذخیره شده دسترسی پیدا کن
                        // و اگر کش ذخیره شده از 5 ثانیه بیشتر بود سپس یک خطا را هدایت کن و ریسپانس را دریافت کن
                        // پارامتر max-age به عبارتی نشان دهنده سن کش است
                        request = request.newBuilder().addHeader("Cache-Control", "public, max-age=" + 5).build();
                    }else{

                        // اگر دستگاه به اینترنت متصل نبود کش 7 روز پیش را بازیابی میکند
                        // اگر کش 7 روز پیش در دسترس نبود سپس یک خطا نمایش داده میشود و درخواست دریافت مجدد اطلاعات را میکند
                        // پارامتر only-if-cached مشخص کننده این مسئله برای درخواست است که تنها اطلاعات را از کش دریافت کن
                        request = request.newBuilder().addHeader(
                                "Cache-Control",
                                "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 7
                        ).build();
                    }
                    // در نهایت درخواست یا request تغییر یافته را به chain اضافه میکنیم

                    return chain.proceed(request);
                }
            }).build();

برای اینکه بیشتر درباره پارامتر های کنترل کش و هدر بدانید میتوانید از اینجا مطالعه کنید.

قرار دادن موارد گفته شده در کنار هم

در نهایت و در ادامه آموزش کش کردن داده ها با رتروفیت کافیست همه مواردی که تا به حال ساختیم را به هم بدین صورت متصل کنیم.برای اتصال OkHttpClient به رتروفیت تنها کافیست آنرا به client رتروفیت بدین صورت متصل کنیم:

کاتلین

    val retrofit = Retrofit.Builder()
        .baseUrl("")
        .addConverterFactory(GsonConverterFactory.create())
        // اضافه کردن نمونه OkHttpClient ساخته شده
        .client(okHttpClient)
        .build()

جاوا

Retrofit retrofit = new Retrofit.Builder()
                 .baseUrl("")
                 .addConverterFactory(GsonConverterFactory.create())
                 // اضافه کردن نمونه OkHttpClient ساخته شده
                 .client(okHttpClient)
                 .build();

در آخر کلاس های جاوا و کاتلین ما (در آموزش کش کردن داده ها با رتروفیت) که وظیفه پیاده سازی نمونه ای از رتروفیت با قابلیت کش داشتند بدین صورت خواهد شد.

کاتلین

 package com.tejariapp.retrofitcache
import android.content.Context
 import android.net.ConnectivityManager
 import android.net.NetworkInfo
 import android.os.Bundle
 import androidx.appcompat.app.AppCompatActivity
 import okhttp3.Cache
 import okhttp3.OkHttpClient
 import retrofit2.Retrofit
 import retrofit2.converter.gson.GsonConverterFactory

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)


    val cacheSize = (5 * 1024 * 1024).toLong()

    val myCache = Cache(this.cacheDir, cacheSize)


    val okHttpClient = OkHttpClient.Builder()
        // مشخص کردن نمونه کشی که قبلا ایجاد کردیم
        .cache(myCache)
        // اضافه کردن یک Interceptor به OkHttpClient
        .addInterceptor { chain ->

            // گرفتن درخواست از chain
            var request = chain.request()

            // چک کردن متصل بودن دستگاه به اینترنت

            request = if (hasNetwork(this)!!)
            //اگر اینترتن متصل بود به اطلاعات کش که 5 ثانیه قبل ذخیره شده دسترسی پیدا کن
            // و اگر کش ذخیره شده از 5 ثانیه بیشتر بود سپس یک خطا را هدایت کن و ریسپانس را دریافت کن
            // پارامتر max-age به عبارتی نشان دهنده سن کش است
                request.newBuilder().header("Cache-Control", "public, max-age=" + 5).build()
            else
                // اگر دستگاه به اینترنت متصل نبود کش 7 روز پیش را بازیابی میکند
                // اگر کش 7 روز پیش در دسترس نبود سپس یک خطا نمایش داده میشود و درخواست دریافت مجدد اطلاعات را میکند
                // پارامتر only-if-cached مشخص کننده این مسئله برای درخواست است که تنها اطلاعات را از کش دریافت کن
                request.newBuilder().header(
                    "Cache-Control",
                    "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 7
                ).build()
            // در نهایت درخواست یا request تغییر یافته را به chain اضافه میکنیم
            chain.proceed(request)
        }
        .build()


    val retrofit = Retrofit.Builder()
        .baseUrl("")
        .addConverterFactory(GsonConverterFactory.create())
        // اضافه کردن نمونه OkHttpClient ساخته شده
        .client(okHttpClient)
        .build()

}


fun hasNetwork(context: Context): Boolean? {
    var isConnected: Boolean? = false // Initial Value
    val connectivityManager =
        context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    val activeNetwork: NetworkInfo? = connectivityManager.activeNetworkInfo
    if (activeNetwork != null && activeNetwork.isConnected)
        isConnected = true
    return isConnected
}
}

جاوا

 package com.tejariapp.retrofitcache;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.os.Bundle;
import java.io.IOException;
import okhttp3.Cache;
 import okhttp3.Interceptor;
 import okhttp3.OkHttpClient;
 import okhttp3.Request;
 import okhttp3.Response;
 import retrofit2.Retrofit;
 import retrofit2.converter.gson.GsonConverterFactory;

public class JavaMainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_java_main);



    long cacheSize = (5 * 1024 * 1024);
    Cache myCache = new Cache(this.getCacheDir(), cacheSize);


    OkHttpClient okHttpClient = new OkHttpClient.Builder()
            // مشخص کردن نمونه کشی که قبلا ایجاد کردیم
            .cache(myCache)
            // اضافه کردن یک Interceptor به OkHttpClient
            .addInterceptor(new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    // گرفتن درخواست از chain
                    Request request = chain.request();

                    // چک کردن متصل بودن دستگاه به اینترنت
                    if (hasNetwork(JavaMainActivity.this)){
                        //اگر اینترتن متصل بود به اطلاعات کش که 5 ثانیه قبل ذخیره شده دسترسی پیدا کن
                        // و اگر کش ذخیره شده از 5 ثانیه بیشتر بود سپس یک خطا را هدایت کن و ریسپانس را دریافت کن
                        // پارامتر max-age به عبارتی نشان دهنده سن کش است
                        request = request.newBuilder().addHeader("Cache-Control", "public, max-age=" + 5).build();
                    }else{

                        // اگر دستگاه به اینترنت متصل نبود کش 7 روز پیش را بازیابی میکند
                        // اگر کش 7 روز پیش در دسترس نبود سپس یک خطا نمایش داده میشود و درخواست دریافت مجدد اطلاعات را میکند
                        // پارامتر only-if-cached مشخص کننده این مسئله برای درخواست است که تنها اطلاعات را از کش دریافت کن
                        request = request.newBuilder().addHeader(
                                "Cache-Control",
                                "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 7
                        ).build();
                    }
                    // در نهایت درخواست یا request تغییر یافته را به chain اضافه میکنیم

                    return chain.proceed(request);
                }
            }).build();



    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl("")
            .addConverterFactory(GsonConverterFactory.create())
            // اضافه کردن نمونه OkHttpClient ساخته شده
            .client(okHttpClient)
            .build();
}


public boolean hasNetwork(Context context) {
    boolean isConnected = false;
    ConnectivityManager connectivityManager = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE));
    NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
    if (activeNetwork != null && activeNetwork.isConnected())
        isConnected = true;
    return isConnected;
}
}

نتیجه

در نهایت می توان اینطور نتیجه گرفت که اگر اپلیکیشنی دارید که API های آن محدودیت در تعداد درخواست دارد ، نه تنها کش کردن داده با استفاده از رتروفیت مفید است (کش کردن داده ها با رتروفیت) بلکه ضروری است. چرا که می توانید در ارسال درخواست به سرور بطور قابل توجه ای صرفه جویی کنید و بار سرور را هم بطور قابل ملاحظه ای کم کنید آن هم بدون اینکه نیاز باشد داده کش شده را در دیتابیس داخلی اپلیکیشن ذخیره کنید !

نکته دیگر اینکه گزینه کش کردن داده ها با رتروفیت برای اپلیکیشن هایی که می خواهند داده را بصورت آفلاین هم دراختیار کاربران قرار دهد بسیار مفید و ضروری ست ! در کل کش کردن داده ها با رتروفیت میتواند به سرعت اپلیکیشن شما و خوشحالی سرور خیلی کمک کند !

امیداورم این آموزش برای شما مفید بوده باشد…

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *