前言
在现代Android开发中,网络请求是应用不可或缺的一部分,而良好的缓存策略可以显著提升用户体验。RxJava作为一种响应式编程框架,能够优雅地处理异步操作和事件流。本文将介绍如何结合RxJava实现网络请求与缓存策略。
一、RxJava基础回顾
RxJava的核心概念包括:
Observable:被观察者,数据源
Observer:观察者,接收数据
Operator:操作符,用于转换、过滤、组合数据流
Scheduler:调度器,控制线程切换
java
Observable.just("Hello RxJava") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(text -> Log.d("RxJava", text));
二、网络请求实现
1. 使用Retrofit+RxJava进行网络请求
首先添加依赖:
gradle
implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
定义API接口:
java
public interface ApiService { @GET("user/{id}") Observable<User> getUser(@Path("id") String userId); }
创建Retrofit实例:
java
Retrofit retrofit = new Retrofit.Builder() .baseUrl("https://api.example.com/") .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava3CallAdapterFactory.create()) .build(); ApiService apiService = retrofit.create(ApiService.class);
2. 发起网络请求
java
apiService.getUser("123") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<User>() { @Override public void onSubscribe(Disposable d) { // 可以在这里显示加载进度条 } @Override public void onNext(User user) { // 处理返回的用户数据 } @Override public void onError(Throwable e) { // 处理错误 } @Override public void onComplete() { // 请求完成 } });
三、缓存策略实现
1. 内存缓存 + 磁盘缓存 + 网络请求
我们可以实现一个三级缓存策略:
首先检查内存缓存
如果没有,检查磁盘缓存
如果还是没有,发起网络请求
将网络请求结果缓存到内存和磁盘
2. 实现缓存管理器
java
public class CacheManager { private static final String TAG = "CacheManager"; private static CacheManager instance; private LruCache<String, Object> memoryCache; private File cacheDir; private CacheManager(Context context) { // 初始化内存缓存 (这里设置为应用最大内存的1/8) final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); final int cacheSize = maxMemory / 8; memoryCache = new LruCache<String, Object>(cacheSize) { @Override protected int sizeOf(String key, Object value) { // 计算每个缓存对象的大小 return value.toString().length() / 1024; } }; // 初始化磁盘缓存目录 cacheDir = new File(context.getCacheDir(), "network_cache"); if (!cacheDir.exists()) { cacheDir.mkdirs(); } } public static synchronized CacheManager getInstance(Context context) { if (instance == null) { instance = new CacheManager(context); } return instance; } // 内存缓存操作 public synchronized void putToMemory(String key, Object value) { if (memoryCache.get(key) == null && value != null) { memoryCache.put(key, value); } } public synchronized Object getFromMemory(String key) { return memoryCache.get(key); } // 磁盘缓存操作 public void putToDisk(String key, Object value) { File file = new File(cacheDir, key); try { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file)); oos.writeObject(value); oos.close(); } catch (IOException e) { e.printStackTrace(); } } public Object getFromDisk(String key) { File file = new File(cacheDir, key); if (!file.exists()) return null; try { ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file)); Object value = ois.readObject(); ois.close(); return value; } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); return null; } } // 清除缓存 public void clearMemoryCache() { memoryCache.evictAll(); } public void clearDiskCache() { File[] files = cacheDir.listFiles(); if (files != null) { for (File file : files) { file.delete(); } } } }
3. 结合RxJava实现缓存策略
java
public Observable<User> getUserWithCache(String userId) { return Observable.concat( getFromMemory(userId), getFromDisk(userId), getFromNetwork(userId)) .filter(user -> user != null) .firstElement() .toObservable(); } private Observable<User> getFromMemory(String userId) { return Observable.create(emitter -> { User user = (User) CacheManager.getInstance(context).getFromMemory(userId); if (user != null) { emitter.onNext(user); } emitter.onComplete(); }); } private Observable<User> getFromDisk(String userId) { return Observable.create(emitter -> { User user = (User) CacheManager.getInstance(context).getFromDisk(userId); if (user != null) { // 放入内存缓存 CacheManager.getInstance(context).putToMemory(userId, user); emitter.onNext(user); } emitter.onComplete(); }).subscribeOn(Schedulers.io()); } private Observable<User> getFromNetwork(String userId) { return apiService.getUser(userId) .doOnNext(user -> { // 保存到内存和磁盘 CacheManager.getInstance(context).putToMemory(userId, user); CacheManager.getInstance(context).putToDisk(userId, user); }); }
4. 使用示例
java
getUserWithCache("123") .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(user -> { // 更新UI }, throwable -> { // 处理错误 });
四、高级缓存策略
1. 缓存过期策略
java
public class CacheItem<T> implements Serializable { private T data; private long timestamp; public CacheItem(T data) { this.data = data; this.timestamp = System.currentTimeMillis(); } public boolean isExpired(long expiryTime) { return (System.currentTimeMillis() - timestamp) > expiryTime; } public T getData() { return data; } } // 修改getFromDisk方法 public Observable<User> getFromDisk(String userId) { return Observable.create(emitter -> { CacheItem<User> cacheItem = (CacheItem<User>) CacheManager.getInstance(context) .getFromDisk(userId); if (cacheItem != null) { if (!cacheItem.isExpired(TimeUnit.MINUTES.toMillis(10))) { // 10分钟过期 User user = cacheItem.getData(); CacheManager.getInstance(context).putToMemory(userId, user); emitter.onNext(user); } else { // 缓存过期,删除磁盘缓存 CacheManager.getInstance(context).removeFromDisk(userId); } } emitter.onComplete(); }).subscribeOn(Schedulers.io()); }
2. 先显示缓存再更新网络数据
java
public Observable<User> getUserWithCacheFirst(String userId) { Observable<User> memory = getFromMemory(userId); Observable<User> disk = getFromDisk(userId); Observable<User> network = getFromNetwork(userId) .doOnNext(user -> { // 保存到内存和磁盘 CacheManager.getInstance(context).putToMemory(userId, user); CacheManager.getInstance(context).putToDisk(userId, user); }); return Observable.concat(memory, disk) .filter(user -> user != null) .take(1) .concatWith(network) .distinctUntilChanged(); }
五、错误处理与重试机制
java
getUserWithCache("123") .retryWhen(throwableObservable -> throwableObservable .zipWith(Observable.range(1, 3), (throwable, retryCount) -> { if (retryCount < 3 && throwable instanceof IOException) { return retryCount; } throw Exceptions.propagate(throwable); }) .flatMap(retryCount -> Observable.timer((long) Math.pow(2, retryCount), TimeUnit.SECONDS)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(user -> { // 更新UI }, throwable -> { // 处理错误 });
六、总结
通过RxJava实现网络请求与缓存策略,我们可以获得以下优势:
响应式编程:代码更加简洁、可读性更强
线程管理:轻松处理线程切换
灵活组合:可以方便地组合多个数据源
错误处理:提供强大的错误处理机制
可扩展性:便于添加新的功能,如缓存过期、重试机制等
在实际项目中,可以根据具体需求调整缓存策略,例如:
对实时性要求高的数据可以缩短缓存时间
对不常变化的数据可以延长缓存时间
对重要数据可以实现多级缓存
希望本文能帮助你在Android开发中更好地使用RxJava处理网络请求与缓存策略。