中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android中Rxjava實現三級緩存的兩種方式

發布時間:2020-09-20 18:59:59 來源:腳本之家 閱讀:124 作者:我想要有你在的未來 欄目:移動開發

本文正如標題所說的用rxjava實現數據的三級緩存分別為內存,磁盤,網絡,剛好最近在看Android源碼設計模式解析與實戰(受里面的ImageLoader的設計啟發)。

我把代碼放到了我的hot項目中,github地址

源碼下載地址:Rxjava_jb51.rar

1.使用concat()和first()的操作符。

2.使用BehaviorSubject。

先說BehaviorSubject的實現方法,廢話不多說直接上代碼,

/**
 * Created by wukewei on 16/6/20.
 */
public class BehaviorSubjectFragment extends BaseFragment {

  public static BehaviorSubjectFragment newInstance() {
    BehaviorSubjectFragment fragment = new BehaviorSubjectFragment();
    return fragment;
  }

  String diskData = null;
  String networkData = "從服務器獲取的數據";
  BehaviorSubject<String> cache;

  View mView;

  @Nullable
  @Override
  public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    mView = inflater.inflate(R.layout.fragment_content, container, false);
    init();
    return mView;
  }

  private void init() {
    mView.findViewById(R.id.get).setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        subscriptionData(new Observer<String>() {
          @Override
          public void onCompleted() {

          }

          @Override
          public void onError(Throwable e) {

          }

          @Override
          public void onNext(String s) {
            Log.d("onNext", s);
          }
        });
      }
    });

    mView.findViewById(R.id.memory).setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        BehaviorSubjectFragment.this.cache = null;
      }
    });

    mView.findViewById(R.id.memory_disk).setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        BehaviorSubjectFragment.this.cache = null;
        BehaviorSubjectFragment.this.diskData = null;
      }
    });

  }

  private void loadNewWork() {
    Observable<String> o = Observable.just(networkData)
        .doOnNext(new Action1<String>() {
          @Override
          public void call(String s) {
            BehaviorSubjectFragment.this.diskData = s;
            Log.d("寫入磁盤", "寫入磁盤");
          }
        });
    o.subscribe(new Action1<String>() {
      @Override
      public void call(String s) {
        cache.onNext(s);
      }
    }, new Action1<Throwable>() {
      @Override
      public void call(Throwable throwable) {

      }
    });
  }

  private Subscription subscriptionData(@NonNull Observer<String> observer) {
    if (cache == null) {
      cache = BehaviorSubject.create();
      Observable.create(new Observable.OnSubscribe<String>() {
        @Override
        public void call(Subscriber<? super String> subscriber) {
          String data = diskData;
          if (data == null) {
            Log.d("來自網絡", "來自網絡");
            loadNewWork();
          } else {
            Log.d("來自磁盤", "來自磁盤");
            subscriber.onNext(data);
          }
        }
      })
          .subscribeOn(Schedulers.io())
          .subscribe(cache);

    } else {
      Log.d("來自內存", "來自內存");
    }

    return cache.observeOn(AndroidSchedulers.mainThread()).subscribe(observer);
  }


}

其中最主要的是subscriptionData()這個方法,就是先判斷 cache是否存在要是存在的話就返回內存中數據,再去判斷磁盤數據是否存在,如果存在就返回,要是前面兩種都不存在的時候,再去網絡中獲取數據。還有最重要的是當你從網絡獲取數據的時候要記得保存在內存中和保存在磁盤中,在磁盤獲取數據的時候把它賦值給內存。

接下來就說說用concat()和first()的操作符來實現,這是我在看Android源碼設計模式解析與實戰,作者在第一章的時候就介紹ImageLoader的設計。

在內存中存儲的方式LruCache來實現的,磁盤存儲的方式就是序列化存儲。

1.定義一個接口:

/**
 * Created by wukewei on 16/6/19.
 */
public interface ICache {
  <T> Observable<T> get(String key, Class<T> cls);

  <T> void put(String key, T t);
}

2.內存存儲的實現

/**
 * Created by wukewei on 16/6/19.
 */
public class MemoryCache implements ICache{

  private LruCache<String, String> mCache;

  public MemoryCache() {
    final int maxMemory = (int) Runtime.getRuntime().maxMemory();
    final int cacheSize = maxMemory / 8;
    mCache = new LruCache<String, String>(cacheSize) {
      @Override
      protected int sizeOf(String key, String value) {
        try {
          return value.getBytes("UTF-8").length;
        } catch (UnsupportedEncodingException e) {
          e.printStackTrace();
          return value.getBytes().length;
        }
      }
    };
  }

  @Override
  public <T> Observable<T> get(final String key, final Class<T> cls) {
    return Observable.create(new Observable.OnSubscribe<T>() {
      @Override
      public void call(Subscriber<? super T> subscriber) {

        String result = mCache.get(key);

        if (subscriber.isUnsubscribed()) {
          return;
        }

        if (TextUtils.isEmpty(result)) {
          subscriber.onNext(null);
        } else {
          T t = new Gson().fromJson(result, cls);
          subscriber.onNext(t);
        }

        subscriber.onCompleted();
      }
    });
  }

  @Override
  public <T> void put(String key, T t) {
    if (null != t) {
      mCache.put(key, new Gson().toJson(t));
    }
  }

  public void clearMemory(String key) {
    mCache.remove(key);
  }
}

3.磁盤存儲的實現

/**
 * Created by wukewei on 16/6/19.
 */
public class DiskCache implements ICache{

  private static final String NAME = ".db";
  public static long OTHER_CACHE_TIME = 10 * 60 * 1000;
  public static long WIFI_CACHE_TIME = 30 * 60 * 1000;
  File fileDir;
  public DiskCache() {
    fileDir = CacheLoader.getApplication().getCacheDir();
  }

  @Override
  public <T> Observable<T> get(final String key, final Class<T> cls) {
    return Observable.create(new Observable.OnSubscribe<T>() {
      @Override
      public void call(Subscriber<? super T> subscriber) {

        T t = (T) getDiskData1(key + NAME);

        if (subscriber.isUnsubscribed()) {
          return;
        }

        if (t == null) {
          subscriber.onNext(null);
        } else {
          subscriber.onNext(t);
        }

        subscriber.onCompleted();
      }
    })
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread());

  }

  @Override
  public <T> void put(final String key, final T t) {
    Observable.create(new Observable.OnSubscribe<T>() {
      @Override
      public void call(Subscriber<? super T> subscriber) {

        boolean isSuccess = isSave(key + NAME, t);

        if (!subscriber.isUnsubscribed() && isSuccess) {

          subscriber.onNext(t);
          subscriber.onCompleted();
        }
      }
    })
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe();
  }

  /**
   * 保存數據
   */
  private <T> boolean isSave(String fileName, T t) {
    File file = new File(fileDir, fileName);

    ObjectOutputStream objectOut = null;
    boolean isSuccess = false;
    try {
      FileOutputStream out = new FileOutputStream(file);
          objectOut = new ObjectOutputStream(out);
      objectOut.writeObject(t);
      objectOut.flush();
      isSuccess=true;
    } catch (IOException e) {
      Log.e("寫入緩存錯誤",e.getMessage());
    } catch (Exception e) {
      Log.e("寫入緩存錯誤",e.getMessage());
    } finally {
      closeSilently(objectOut);
    }
    return isSuccess;
  }

  /**
   * 獲取保存的數據
   */
  private Object getDiskData1(String fileName) {
    File file = new File(fileDir, fileName);

    if (isCacheDataFailure(file)) {
      return null;
    }

    if (!file.exists()) {
      return null;
    }
    Object o = null;
    ObjectInputStream read = null;
    try {
      read = new ObjectInputStream(new FileInputStream(file));
      o = read.readObject();
    } catch (StreamCorruptedException e) {
      Log.e("讀取錯誤", e.getMessage());
    } catch (IOException e) {
      Log.e("讀取錯誤", e.getMessage());
    } catch (ClassNotFoundException e) {
      Log.e("錯誤", e.getMessage());
    } finally {
      closeSilently(read);
    }
    return o;
  }



  private void closeSilently(Closeable closeable) {
    if (closeable != null) {
      try {
        closeable.close();
      } catch (Exception ignored) {
      }
    }
  }



  /**
   * 判斷緩存是否已經失效
   */
  private boolean isCacheDataFailure(File dataFile) {
    if (!dataFile.exists()) {
      return false;
    }
    long existTime = System.currentTimeMillis() - dataFile.lastModified();
    boolean failure = false;
    if (NetWorkUtil.getNetworkType(CacheLoader.getApplication()) == NetWorkUtil.NETTYPE_WIFI) {
      failure = existTime > WIFI_CACHE_TIME ? true : false;
    } else {
      failure = existTime > OTHER_CACHE_TIME ? true : false;
    }

    return failure;
  }

  public void clearDisk(String key) {
    File file = new File(fileDir, key + NAME);
    if (file.exists()) file.delete();
  }
}

isCacheDataFailure()方式中就是判斷當前的數據是否失效,我是根據當前的網絡狀況來分wifi狀況和非wifi狀況,wifi狀態下數據過期時間比較短,其他狀態過期時間比較長。

4.CacheLoader的設計

/
**
 * Created by wukewei on 16/6/19.
 */
public class CacheLoader {
  private static Application application;

  public static Application getApplication() {
    return application;
  }

  private ICache mMemoryCache, mDiskCache;

  private CacheLoader() {

    mMemoryCache = new MemoryCache();
    mDiskCache = new DiskCache();
  }
  private static CacheLoader loader;

  public static CacheLoader getInstance(Context context) {
    application = (Application) context.getApplicationContext();
    if (loader == null) {
      synchronized (CacheLoader.class) {
        if (loader == null) {
          loader = new CacheLoader();
        }
      }
    }
    return loader;
  }


  public <T> Observable<T> asDataObservable(String key, Class<T> cls, NetworkCache<T> networkCache) {

    Observable observable = Observable.concat(
        memory(key, cls),
        disk(key, cls),
        network(key, cls, networkCache))
        .first(new Func1<T, Boolean>() {
          @Override
          public Boolean call(T t) {
            return t != null;
          }
        });
    return observable;
  }

  private <T> Observable<T> memory(String key, Class<T> cls) {

    return mMemoryCache.get(key, cls).doOnNext(new Action1<T>() {
      @Override
      public void call(T t) {
        if (null != t) {
          Log.d("我是來自內存","我是來自內存");
        }
      }
    });
  }

  private <T> Observable<T> disk(final String key, Class<T> cls) {

    return mDiskCache.get(key, cls)
        .doOnNext(new Action1<T>() {
          @Override
          public void call(T t) {
            if (null != t) {
              Log.d("我是來自磁盤","我是來自磁盤");
              mMemoryCache.put(key, t);
            }
          }
        });
  }

  private <T> Observable<T> network(final String key, Class<T> cls
      , NetworkCache<T> networkCache) {

    return networkCache.get(key, cls)
        .doOnNext(new Action1<T>() {
          @Override
          public void call(T t) {
            if (null != t) {
              Log.d("我是來自網絡","我是來自網絡");
              mDiskCache.put(key, t);
              mMemoryCache.put(key, t);
            }
          }
        });
  }


  public void clearMemory(String key) {
    ((MemoryCache)mMemoryCache).clearMemory(key);
  }



  public void clearMemoryDisk(String key) {
    ((MemoryCache)mMemoryCache).clearMemory(key);
    ((DiskCache)mDiskCache).clearDisk(key);
  }
}

5.網絡獲取的NetworkCache:

/**
 * Created by wukewei on 16/6/19.
 */
public abstract class NetworkCache<T> {
  public abstract Observable<T> get(String key, final Class<T> cls);
}

6.接下來看怎么使用

/**
 * Created by wukewei on 16/5/30.
 */
public class ItemPresenter extends BasePresenter<ItemContract.View> implements ItemContract.Presenter {

  private static final String key = "new_list";
  protected int pn = 1;

  protected void replacePn() {
    pn = 1;
  }

  private boolean isRefresh() {
    return pn == 1;
  }

  private NetworkCache<ListPopular> networkCache;


  public ItemPresenter(Activity activity, ItemContract.View view) {
    super(activity, view);

  }

  @Override
  public void getListData(String type) {
    if (isRefresh()) mView.showLoading();
    networkCache = new NetworkCache<ListPopular>() {
      @Override
      public Observable<ListPopular> get(String key, Class<ListPopular> cls) {
        return mHotApi.getPopular(ItemPresenter.this.pn, Constants.PAGE_SIZE, type)
            .compose(SchedulersCompat.applyIoSchedulers())
            .compose(RxResultHelper.handleResult())
            .flatMap(populars -> {
              ListPopular popular = new ListPopular(populars);
              return Observable.just(popular);
            });
      }
    };

    Subscription subscription = CacheLoader.getInstance(mActivity)
        .asDataObservable(key + type + ItemPresenter.this.pn, ListPopular.class, networkCache)
        .map(listPopular -> listPopular.data)
        .subscribe(populars -> {
          mView.showContent();
          if (isRefresh()) {
            if (populars.size() == 0) mView.showNotdata();
            mView.addRefreshData(populars);
          } else {
            mView.addLoadMoreData(populars);
          }
        }, throwable -> {
          if (isRefresh())
          mView.showError(ErrorHanding.handleError(throwable));
          handleError(throwable);
        });

    addSubscrebe(subscription);

  }
}

一定要給個key,我是根據key來獲取數據的,還要就是給個類型。

但是這個我設計的這個緩存還是不是很理想,接來下想要實現的就是在傳入的時候類的class都不用給明,要是有好的實現的方式,歡迎告訴我。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

珠海市| 武清区| 泸州市| 南雄市| 河西区| 许昌市| 三亚市| 临沂市| 潢川县| 恭城| 巴里| 永济市| 正宁县| 方城县| 临潭县| 荆门市| 建瓯市| 渝中区| 民和| 彰化市| 神池县| 万宁市| 竹溪县| 泸水县| 清丰县| 工布江达县| 应城市| 博乐市| 邵武市| 南开区| 太保市| 石阡县| 忻州市| 东平县| 潼南县| 微山县| 乌审旗| 托克逊县| 西畴县| 城固县| 晋城|