افزایش عمر باتری هنگام استفاده از Retrofit 2

استفاده از cache در Retrofit 2
Retrofit 2 کتابخانه قدرتمند برای ارتباط با سرور و دریافت اطلاعات می باشد. هنگام تعامل با سرور موارد زیادی وجود دارند که باید مدیریت شوند. کتابخانه Retrofit 2 به خوبی از عهده این کار بر می آید. پیشتر در مقاله ای به آشنایی و کار با این کتابخانه پرداخته ایم. در این مقاله پیاده سازی قدم به قدم کتابخانه Retrofit در پروژه اندرویدی را بررسی نموده ایم.
هنگام تعامل با سرور مواردی باید دانلود شوند. از بین آنها مواردی وجود دارند که تکراری هستند و نباید هر بار که برنامه با سرور ارتباط می گیرد دوباره دانلود شوند. استفاده از اینترنت به تنهایی موجب نشت باتری شده و کم کم عمر آنرا کوتاه تر می کند. در نتیجه تا حد امکان باید ارتباط بهینه ای با سرور برقرار کنیم و حجم دیتای تبادلی را کاهش دهیم.
راه حل هایی برای افزایش عمر باتری هنگام ارتباط با سرور
- Cache و فشرده سازی داده ها : جلوگیری از دانلود داده های در مواقع غیر ضروری با ذخیره سازی آنها در حافظه پنهان و استفاده در صورت لزوم. به حداقل رساندن زمان تبادل داده ها با فشرده سازی و کاهش حجم آنها.
- مصرف کمتر باتری توسط Wifi به نسبت داده تلفن همراه: برای دانلود فایل هایی حجیم باید از wifi استفاده کرد و این محدودیت را روی دیوایس قائل شد چرا که مصرف باتری دیتای گوشی بیشتر است.
- استفاده دوباره از object های ساخته شده برای connection به جای ساخت connection جدید
- دانلود بیشتر با یک connection به جای استفاده از هر connection یک دانلود مجزا
طرح یک مثال برای استفاده از حافظه پنهان در استفاده از کتابخانه Retrofit 2
در این مثال از api مربوط به github استفاده می کنیم. تصویر کلی این مثال به این صورت است که درخواستی به سرور ارسال می گردد تا پاسخی دریافت شود. برای مدت زمان یک دقیقه پاسخ دریافتی در حافظه نهان یا cache باقی می ماند.
مرحله اول : افزودن کتابخانه ها به build.gradle
// rxandroid implementation 'io.reactivex.rxjava2:rxandroid:2.0.1' // Retrofit 2 for network tasks implementation 'com.squareup.retrofit2:retrofit:2.3.0' implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' implementation 'com.squareup.retrofit2:converter-gson:2.3.0' implementation 'com.squareup.okhttp3:logging-interceptor:3.9.0' implementation 'com.google.code.gson:gson:2.8.2'
مرحله دوم : ساخت کلاس entity
برای نگهداری اطلاعات گرفته شده از api کلاسی متناسب با فیلدهای پاسخ سرور می سازیم :
import com.google.gson.annotations.SerializedName data class GithubAccount( @SerializedName("login") var login : String = "", @SerializedName("id") var id : Int = 0, @SerializedName("avatar_url") var avatarUrl : String = "", @SerializedName("created_at") var createdAt : String = "", @SerializedName("updated_at") var updatedAt : String = "")
مرحله سوم : ساخت اینترفیس برای Retrofit
import io.reactivex.Observable import retrofit2.Response import retrofit2.http.GET import retrofit2.http.Path interface GithubApi { @GET("/users/{username}") fun getGithubAccountObservable(@Path("username") username: String): Observable<Response<GithubAccount>> }
مرحله چهارم : ساخت object برای cache
سایز cache به اندازه 10mb مشخص شده است. به این معنی که cache های قدیمی، وقتی این میزان پر شود، برای نگهداری داده های جدید، از بین می روند. ()getCacheDir متدی در کلاس اکتیویتی ست که مقدار بازگشتی آن directory پیش فرض cache در اندروید است.
val cacheSize = 10 * 1024 * 1024 // 10 MB val httpCacheDirectory = File(getCacheDir(), "http-cache") val cache = Cache(httpCacheDirectory, cacheSize.toLong())
مرحله پنجم : ایجاد و دستیابی به cache
val networkCacheInterceptor = Interceptor { chain -> val response = chain.proceed(chain.request()) var cacheControl = CacheControl.Builder() .maxAge(1, TimeUnit.MINUTES) .build() response.newBuilder() .header("Cache-Control", cacheControl.toString()) .build() }
مرحله ششم : ایجاد log
این مرحله ضروری نیست اما همان طور که می دانید می تواند برای زمان debug برنامه مفید باشد :
val loggingInterceptor = HttpLoggingInterceptor() loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
مرحله هفتم : ساخت object از HttpClient
این مرحله به منظور تنظیمات و دستیابی cache انجام می شود :
val httpClient = OkHttpClient.Builder() .cache(cache) .addNetworkInterceptor(networkCacheInterceptor) .addInterceptor(loggingInterceptor) .build()
مرحله هشتم : ساخت نمونه Retrofit با بکارگیری HttpClient
val retrofit = Retrofit.Builder() .baseUrl("https://api.github.com/") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .client(httpClient) .build()
مرحله نهم : ایجاد درخواست به سرور و دریافت پاسخ از api
val githubApi = retrofit.create(GithubApi::class.java) githubApi.getGithubAccountObservable("google") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(object : DisposableObserver<Response<GithubAccount>>() { override fun onNext(response: Response<GithubAccount>) { if (response.raw().cacheResponse() != null) { Log.d("Network", "response came from cache") } if (response.raw().networkResponse() != null) { Log.d("Network", "response came from server") } Toast.makeText(applicationContext, response.body().toString(), Toast.LENGTH_SHORT).show() } override fun onComplete() {} override fun onError(e: Throwable) { e.printStackTrace() } })
در نهایت با پیاده سازی مرحله 4 تا 9 کلاس اکتیویتی به صورت زیر خواهد بود :
import android.content.Intent import android.os.Bundle import android.support.v7.app.AppCompatActivity import android.util.Log import android.view.View import android.widget.Toast import com.example.myapplication.data.GithubAccount import com.example.myapplication.data.GithubApi import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.observers.DisposableObserver import io.reactivex.schedulers.Schedulers import okhttp3.Cache import okhttp3.CacheControl import okhttp3.Interceptor import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import retrofit2.Response import retrofit2.Retrofit import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory import retrofit2.converter.gson.GsonConverterFactory import java.io.File import java.util.concurrent.TimeUnit class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) loadGithubAccount() } fun loadGithubAccount() { // Create a cache object val cacheSize = 10 * 1024 * 1024 // 10 MB val httpCacheDirectory = File(getCacheDir(), "http-cache") val cache = Cache(httpCacheDirectory, cacheSize.toLong()) // create a network cache interceptor, setting the max age to 1 minute val networkCacheInterceptor = Interceptor { chain -> val response = chain.proceed(chain.request()) var cacheControl = CacheControl.Builder() .maxAge(1, TimeUnit.MINUTES) .build() response.newBuilder() .header("Cache-Control", cacheControl.toString()) .build() } // Create the logging interceptor val loggingInterceptor = HttpLoggingInterceptor() loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY // Create the httpClient, configure it // with cache, network cache interceptor and logging interceptor val httpClient = OkHttpClient.Builder() .cache(cache) .addNetworkInterceptor(networkCacheInterceptor) .addInterceptor(loggingInterceptor) .build() // Create the Retrofit with the httpClient val retrofit = Retrofit.Builder() .baseUrl("https://api.github.com/") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .client(httpClient) .build() // Build the gitHubApi with Retrofit and do the network request val githubApi = retrofit.create(GithubApi::class.java) githubApi.getGithubAccountObservable("google") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribeWith(object : DisposableObserver<Response<GithubAccount>>() { override fun onNext(response: Response<GithubAccount>) { if (response.raw().cacheResponse() != null) { Log.d("Network", "response came from cache") } if (response.raw().networkResponse() != null) { Log.d("Network", "response came from server") } Toast.makeText(applicationContext, response.body().toString(), Toast.LENGTH_SHORT).show() } override fun onComplete() {} override fun onError(e: Throwable) { e.printStackTrace() } }) } }