Last active
January 11, 2016 10:04
-
-
Save TechIsFun/0013bdfd4e65fe6e6f7d to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package com.example; | |
| import android.content.SharedPreferences; | |
| import android.support.annotation.NonNull; | |
| import com.example.networking.ApiService; | |
| import com.example.networking.response.ExampleResponse; | |
| import com.google.gson.Gson; | |
| import java.util.HashMap; | |
| import rx.Observable; | |
| import timber.log.Timber; | |
| public class ExampleDataManager { | |
| private static final long MAX_AGE = 10 * 60 * 1000; // 10 minutes | |
| private SharedPreferences mSharedPreferences; | |
| private ApiService mApiService; | |
| private HashMap<String, ExampleResponse> mMemoryCache = new HashMap<>(); | |
| private Gson mGson = new Gson(); | |
| public ExampleDataManager(SharedPreferences sharedPreferences, | |
| ApiService apiService) { | |
| mSharedPreferences = sharedPreferences; | |
| mApiService = apiService; | |
| } | |
| public Observable<ExampleResponse> observeExampleResponse() { | |
| return Observable.concat(fromMemory(), fromDisk(), fromNetwork()) | |
| .first(response -> isValid(response)) | |
| .onErrorResumeNext(throwable -> fallbackToDiskCache()); | |
| } | |
| protected Observable<ExampleResponse> fromNetwork() { | |
| return mApiService.getExampleObject() | |
| .doOnNext(exampleResponse -> exampleResponse.setUpdatedAt(now())) | |
| .doOnNext(exampleResponse -> cacheInMemory(exampleResponse)) | |
| .doOnNext(exampleResponse -> cacheOnDisk(exampleResponse)) | |
| .compose(logSource("NETWORK")); | |
| } | |
| private Observable<ExampleResponse> fallbackToDiskCache() { | |
| Timber.v("fallback to disk cache"); | |
| return fromDisk(); | |
| } | |
| protected void cacheOnDisk(ExampleResponse exampleResponse) { | |
| String json = mGson.toJson(exampleResponse); | |
| mSharedPreferences.edit().putString(getPrefKey(), json).apply(); | |
| } | |
| protected Observable<ExampleResponse> fromDisk() { | |
| Observable<ExampleResponse> observable = Observable.create(subscriber -> { | |
| ExampleResponse exampleResponse = readFromPreferences(); | |
| if (exampleResponse != null) { | |
| subscriber.onNext(exampleResponse); | |
| } | |
| subscriber.onCompleted(); | |
| }); | |
| return observable.doOnNext(data -> cacheInMemory(data)) | |
| .compose(logSource("DISK")); | |
| } | |
| private ExampleResponse readFromPreferences() { | |
| String json = mSharedPreferences.getString(getPrefKey(), null); | |
| try { | |
| return mGson.fromJson(json, ExampleResponse.class); | |
| } catch (Exception e) { | |
| Timber.v(e, "ignored"); | |
| } | |
| return null; | |
| } | |
| protected void cacheInMemory(@NonNull ExampleResponse exampleResponse) { | |
| mMemoryCache.put(exampleResponse); | |
| } | |
| protected Observable<ExampleResponse> fromMemory() { | |
| Observable<ExampleResponse> observable = Observable.create(subscriber -> { | |
| subscriber.onNext(mMemoryCache.get()); | |
| subscriber.onCompleted(); | |
| }); | |
| return observable.compose(logSource("MEMORY")); | |
| } | |
| private boolean isValid(ExampleResponse exampleResponse) { | |
| return exampleResponse != null && !isExpired(exampleResponse.getUpdatedAt()); | |
| } | |
| protected void clearCache() { | |
| mMemoryCache.remove(); | |
| mSharedPreferences.edit().remove(getPrefKey()).apply(); | |
| } | |
| private String getPrefKey() { | |
| // TODO: return a constant value or generete a key | |
| } | |
| private static long now() { | |
| return System.currentTimeMillis(); | |
| } | |
| private static boolean isExpired(long timestamp) { | |
| return System.currentTimeMillis() - timestamp > MAX_AGE; | |
| } | |
| private Observable.Transformer<ExampleResponse, ExampleResponse> logSource(final String source) { | |
| return dataObservable -> dataObservable.doOnNext(data -> { | |
| if (data == null) { | |
| Timber.v(source + " does not have any data."); | |
| } else if (!isValid(data)) { | |
| Timber.v(source + " has stale data."); | |
| } else { | |
| Timber.v(source + " has the data you are looking for!"); | |
| } | |
| }); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment