آموزش Caching یا کشینگ یا کش کردن داده ها با رتروفیت در اندروید ، کش کردن کردن داده ها یک روش معمول در تمامی پلتفرم هاست برای ذخیره موقت داده ها در دستگاه اجرا کننده برنامه برای استفاده مجدد در زمان هایی که دستگاه به اینترنت متصل نیست و یا میخواهد یک سری داده های تکراری را از سرور دریافت کند.
در این مقاله ما بطور کامل آموزش خواهیم داد که چطور داده های دریافتی را با استفاده از رتروفیت بطور موقت کش کنید و در مواقع نیاز از آن استفاده کنید و نکته جالب درباره این نوع کش کردن این است که هیچ نیازی به دیتابیس ندارید !
بنابراین با این مزایا گفته شده کش کردن داده ها یا اطلاعات جزو واجبات هر اپلیکیشن خواهد بود پس بی درنگ به سراغ آموزش پیاده سازی کش داده کردن داده ها با رتروفیت برویم.
نکته :
اگر اپلیکیشن شما جزو اپلکیشن هایی است که اطلاعات آن بصورت آنی به روز می شوند ترجیح داده می شود که از کش کردن داده ها استفاده نکنید و api های خودرا تا حد امکان بهینه کنید چرا که درخواست ها زیاد می شود و ممکن است بار سرور هم بالا رفته و سیستم شما به مشکل جدی بخورد.
اگر قبلا از رتروفیت استفاده کرده اید قطعا میدانید پیاده سازی بیس درخواست ها با رتروفیت چگونه خواهد بود اگر هم تا به حال با رتروفیت کار نکردید اکیدا پیشنهاد می شود قبل از شروع آموزش کش کردن داده ها با رتروفیت ، پست آموزش رتروفیت از تجاری اپ را مطالعه کنید.
خب ما یک نمونه ( instance ) از رتروفیت و همچنین Gson ( برای تبدیل پاسخ یا ریسپانس دریافتی به کلاس های جاوا یا کاتلین ) بدین صورت ایجاد میکنیم
همچنین بخوانید: آموزش کتابخانه 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 است و می تواند داده های دریافتی را بطور کارآمدی دریافت و مدیریت کند زمانی که :
در مرحله اول باید دسترسی دستگاه به اینترنت را چک کنیم پس یک متد جهت چک کردن دسترسی دستگاه به اینترنت مشابه زیر ایجاد میکنیم
کاتلین
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;
}
در مرحله دوم باید سایز cache کش را مشخص کنیم در خط زیر ما مقدار 5MG برای کش مشخص کردیم و توجه داشته باشید که مقدار باید از نوع long باشد.
کاتلین
val cacheSize = (5 * 1024 * 1024).toLong()
جاوا
long cacheSize = (5 * 1024 * 1024);
در مرحله سوم باید یک متغیر کش بسازیم که برای اینکار باید دایرکتوری کش به همراه سایز کش را به متغیر Cache پاس می دهیم و یک نمونه کش برای OkHttpClient بدین صورت میسازیم.
کاتلین
val myCache = Cache(this.cacheDir, cacheSize)
جاوا
Cache myCache = new Cache(this.getCacheDir(), cacheSize);
در مرحله چهارم کافی ست یک نمونه از 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 های آن محدودیت در تعداد درخواست دارد ، نه تنها کش کردن داده با استفاده از رتروفیت مفید است (کش کردن داده ها با رتروفیت) بلکه ضروری است. چرا که می توانید در ارسال درخواست به سرور بطور قابل توجه ای صرفه جویی کنید و بار سرور را هم بطور قابل ملاحظه ای کم کنید آن هم بدون اینکه نیاز باشد داده کش شده را در دیتابیس داخلی اپلیکیشن ذخیره کنید !
نکته دیگر اینکه گزینه کش کردن داده ها با رتروفیت برای اپلیکیشن هایی که می خواهند داده را بصورت آفلاین هم دراختیار کاربران قرار دهد بسیار مفید و ضروری ست ! در کل کش کردن داده ها با رتروفیت میتواند به سرعت اپلیکیشن شما و خوشحالی سرور خیلی کمک کند !
امیداورم این آموزش برای شما مفید بوده باشد…