前言
为什么要进行离线缓存?
对于某些应用来说,特别是视频App,视频的数据是存储在服务器的数据库上的,如果用户每次都去请求服务器,这对服务器的压力比较大,资源浪费也比较严重。而且用户可能希望在没有网络的情况下,也可以观看视频,这时候离线缓存是一个比较好的解决方案。
离线缓存方案
- A :优先从本地获取数据,如果数据过时或者不存在从服务器获取数据,数据返回后同时将数据同步到本地数据库。
- B:优先从服务器获取数据,数据返回后同时将数据存储到本地数据库,如果网络故障则从本地数据库读取数据。
- C:同时从本地和服务器获取数据,如果本地数据库返回数据则先展示本地数据,等网络数据返回后再展示网络数据同时将数据同步到本地数据库。
数据存储
/**
* 保存数据
* @param url
* @param data
* @param callback
*/
saveData(url, data, callback) {
if (!data || !url) return;
AsyncStorage.setItem(url, JSON.stringify(this._wrapData(data)), callback);
}
//本地数据存储应该有实效性 所以给每个数据都加上一个时间戳 如果条件允许的话最好使用服务器上的时间戳
_wrapData(data) {
return {data: data, timestamp: new Date().getTime()};
}
获取本地数据
/**
* 获取本地数据
* @param url
* @returns {Promise}
*/
fetchLocalData(url) {
return new Promise((resolve, reject) => {
AsyncStorage.getItem(url, (error, result) => {
if (!error) {
try {
resolve(JSON.parse(result));
} catch (e) {
reject(e);
console.error(e);
}
} else {
reject(error);
console.error(error);
}
})
})
}
获取网络数据
/**
* 获取网络数据
* @param url
* @param flag
* @returns {Promise}
*/
fetchNetData(url, flag) {
return new Promise((resolve, reject) => {
if (flag !== FLAG_STORAGE.flag_trending) {
fetch(url)
.then((response) => {
if (response.ok) {
return response.json();
}
throw new Error('Network response was not ok.');
})
.then((responseData) => {
this.saveData(url, responseData)
resolve(responseData);
})
.catch((error) => {
reject(error);
})
} else {
new Trending().fetchTrending(url)
.then(items => {
if (!items) {
throw new Error('responseData is null');
}
this.saveData(url, items);
resolve(items);
})
.catch(error => {
reject(error);
})
}
})
}
A策略获取数据
/**
* 获取数据,优先获取本地数据,如果无本地数据或本地数据过期则获取网络数据
* @param url
* @param flag
* @returns {Promise}
*/
fetchData(url, flag) {
return new Promise((resolve, reject) => {
this.fetchLocalData(url).then((wrapData) => {
if (wrapData && DataStore.checkTimestampValid(wrapData.timestamp)) {
resolve(wrapData);
} else {
this.fetchNetData(url, flag).then((data) => {
resolve(this._wrapData(data));
}).catch((error) => {
reject(error);
})
}
}).catch((error) => {
this.fetchNetData(url, flag).then((data) => {
resolve(this._wrapData(data));
}).catch((error => {
reject(error);
}))
})
})
}
/**
* 检查timestamp是否在有效期内
* @param timestamp 项目更新时间
* @return {boolean} true 不需要更新,false需要更新
*/
static checkTimestampValid(timestamp) {
const currentDate = new Date();
const targetDate = new Date();
targetDate.setTime(timestamp);
if (currentDate.getMonth() !== targetDate.getMonth()) return false;
if (currentDate.getDate() !== targetDate.getDate()) return false;
if (currentDate.getHours() - targetDate.getHours() > 4) return false;//有效期4个小时
// if (currentDate.getMinutes() - targetDate.getMinutes() > 1)return false;
return true;
}