当项目中在http网络请求基类中需要进行本地存储时,可能会导致网络请求慢,需要特别的注意.
import {toastShort} from './ToastUtil';
import fetch from './fetch-polyfill';
import {NativeModules} from 'react-native';
import SceneUtils from './SceneUtils';
import LoadingStore from '../component/loading/LoadingStore';
import strings from "../common/strings";
import appsFlyer from 'react-native-appsflyer';
import * as AesUtil from "./AesUtil";
import GlobalStore from "../page/GlobalStore";
import _ from 'lodash';
const {HttpConfig, Config} = NativeModules;
/**
* 网络请求
* @param url
* @param method
* @param formData
* @param loadingType 全局添加等待状态,仅用于单个http网络请求等待标识
* loadingType=0 使用Loading等待框
* loadingType=1 使用LoadingView等待框
*/
/** 如果yield call的是一个Promise对象,那只有在Promise返回的是resolve方法的情况下,
* 下面跟着的yield put及后面的代码才会执行,若返回了reject则后面的代码则全部停止执行
* https://www.jianshu.com/p/045d6fc47de4
*/
function httpFetch(url, method, formData, loadingType?: number = -1) {
return new Promise((resolve, reject) => {
const headParams = {
'Connection': 'keep-alive',
'Content-Type': 'application/x-www-form-urlencoded', //默认格式
'credentials': 'include', //包含cookie
'mode': 'cors', //允许跨域
};
Config.getData(strings.SESSIONID, 0, (sessionId) => {
Config.getData(strings.UID, 0, (uid) => {
console.log(url, sessionId + ":" + uid);
if (sessionId && uid) {
headParams.Cookie = `SESSIONID=${sessionId};UID=${uid}`;
}
if (loadingType === 0) {
LoadingStore.getInstance().setLoading(true);
} else if (loadingType === 1) {
LoadingStore.getInstance().setLoadingView(true);
}
Config.getData(strings.usePrepareUrl, 1, usePrepareUrl => {
let prepardUrl = null;
if (usePrepareUrl) {
prepardUrl = strings.product_base_url_prepared;
}
requestData(prepardUrl, url, method, formData, headParams, loadingType).then(resultObject => {
resolve(resultObject);
}).catch(err => {
if (loadingType === 0) {
LoadingStore.getInstance().setLoading(false);
} else if (loadingType === 1) {
LoadingStore.getInstance().setLoadingView(false);
}
reject(err);
});
});
});
});
});
}
function requestData(prepareUrl? = null, url, method, formData, headParams, loadingType?: number = -1) {
let debugMode = HttpConfig.isDebug && GlobalStore.getInstance().jsDebug;
let baseUrl = null;
if (!prepareUrl) {
baseUrl = debugMode ? strings.test_base_url : strings.product_base_url;
} else {
baseUrl = prepareUrl;
}
console.log("baseUrl", baseUrl);
console.log("url", url);
let fullUrl = baseUrl + url;
let paramsArray = [];
//formData添加通用参数
formData.clientType = 'android';
formData.appVersion = HttpConfig.appVersion;
formData.pkg_name = HttpConfig.pkg_name;
formData.deviceName = HttpConfig.deviceName;
formData.osVersion = HttpConfig.osVersion;
formData.utmsource = HttpConfig.utmsource;
formData.apk_source = HttpConfig.apk_source;
return new Promise((resolve, reject) => {
// appsFlyer.getAppsFlyerUID((error, appsFlyerUID) => {
// if (error) {
// console.error(error);
// formData.appsflyerId = "";
// } else {
// formData.appsflyerId = appsFlyerUID;
// }
Config.getData(strings.deviceId, 0, async deviceId => {
if (deviceId)
formData.deviceId = deviceId;
let source = await HttpConfig.getSource();
formData.source = source;
formData.advertisingId = await HttpConfig.getAdvertisingAd();
console.log(formData);
let encryptData = await AesUtil.encrypt(JSON.stringify(formData));
paramsArray.push(`en_data=${encodeURIComponent(encryptData)}`);
fetch(fullUrl, {
method,
headers: headParams,
timeout: 30 * 1000,
body: method === 'GET' ? null : paramsArray.join('&'),
}).then(response => {
if (response.ok) {
return response.json();
} else {
if (loadingType === 0) {
LoadingStore.getInstance().setLoading(false);
} else if (loadingType === 1) {
LoadingStore.getInstance().setLoadingView(false);
}
reject({
code: -1,
message: response.statusText,
});
}
})
.then(async encryptJson => {
if (loadingType === 0) {
LoadingStore.getInstance().setLoading(false);
} else if (loadingType === 1) {
LoadingStore.getInstance().setLoadingView(false);
}
let code = -1;
let message = "Kesalahan parsing data";
console.log(url, encryptJson);
if (encryptJson) {
code = parseInt(encryptJson.code);
if (encryptJson.message)
message = encryptJson.message;
if (code === 0 || code === 1001) {
let {data} = encryptJson;
if (data && _.isString(data)) {
let decryptData = await AesUtil.decrypt(data);
console.log("decryptData", decryptData);
if (decryptData) {
resolve(JSON.parse(decryptData));
} else {
resolve(decryptData)
}
} else {
resolve(data)
}
} else {
if (encryptJson.code === -2) {//重新登录
Config.setBoolData(strings.isLogin, false);
Config.remove(strings.userInfo);
Config.remove(strings.UID);
Config.remove(strings.SESSIONID);
toastShort(encryptJson.message, false);
SceneUtils.gotoLogin();
return;
}
if (reject) {
reject(encryptJson);
console.log(encryptJson)
} else {
toastShort(encryptJson.message, false);
}
}
} else {
if (reject) {
let encryptJson = `{\"code\":${code},\"message\":\"${message}\",\"data\":{}}`;
reject(JSON.parse(encryptJson));
console.log(encryptJson)
} else {
toastShort(message, false);
}
}
})
.catch(error => {
if (loadingType === 0) {
LoadingStore.getInstance().setLoading(false);
} else if (loadingType === 1) {
LoadingStore.getInstance().setLoadingView(false);
}
let errMsg = error && error.toString();
let code = -1;
if (errMsg.indexOf('Network request failed') > 0) {
if (debugMode) {
code = 20002;
errMsg = 'Kesalahan koneksi';
} else {
Config.setBoolData(strings.usePrepareUrl, true);
}
}
if (errMsg.indexOf('Unexpected end of JSON input') > 0) {
errMsg = 'Kesalahan parsing data';
}
console.log("httpError", url);
if (reject) {
reject({
code,
message: errMsg
});
} else {
toastShort(errMsg, false);
}
});
});
// });
});
}
export {httpFetch};
其中NativeModules中Config是我用原生写的本地存储代码,通过原生与RN通信的方式透传到RN端来使用,以前使用的是@react-native-community/async-storage来进行本地存储,因为这个类中需要进行加解密的操作,以及大量的本地存储数据,原来导致了每个网络请求都特别的慢
查看是否是这个问题:
这个是另外一个项目的网络请求代码
async request(method, url, params) {
// const userAgent = DeviceInfo.getUserAgent();
let startNetDate = new Date().getTime()
let net = await NetInfo.getConnectionInfo();
console.log("NetInfo",new Date().getTime()-startNetDate);
// console.log(net)
let plat = 'Beibeipay/5.0';
let type = `(${DeviceInfo.getSystemName()}; ${DeviceInfo.getDeviceName()}; ${DeviceInfo.getSystemName()} ${DeviceInfo.getSystemVersion()})`;
// console.log(type)
let version = `Mobile/${DeviceInfo.getSystemVersion()}`
let appName = `Beibeipay/${DeviceInfo.getVersion()}`;
let ptn = `Partner/${config.partner}`;
let netType = `NetType/${net.type}`;
let lan = `Language/zh_CN`;
let sysInfo = `${plat} ${type} ${version} RN${appName} ${ptn} ${netType} ${lan}`;
let startTokenDate = new Date().getTime()
const token = await AsyncStorage.getItem('userToken');
console.log("token-End",new Date().getTime()-startTokenDate);
// console.log(sysInfo)
return axios.request({
url: url,
baseURL: config.apiBaseUrl,
headers: {
// 'Content-Type': 'application/json',
'X-Api-Key': token,
'X-Api-Partner': config.partner,
'User-Agent': sysInfo
},
timeout: 30000,
method: method,
data: method.toLowerCase() !== 'get' ? params : null,
params: method.toLowerCase() === 'get' ? params : null,
responseType: 'json',
transformRequest: [function(data) {
if (data instanceof FormData) {
return data
}
return Qs.stringify(data, {
arrayFormat: 'brackets'
})
}],
transformResponse: [function(response) {
if (response.status === 401 || response.status === 403 || response.code === '40401' || response.code === '40403') {
// Toast(response.msg)
// 跳转到登录页面 传参是为了 走到login登录时强制刷新tab里面
NavigationModule.navigate('Login', {
origin: 'baseRequest'
})
} else if (response.code) {
Toast(response.msg)
}
return response
}]
})
}
可以明显的看到,使用async await后异步队列明显需要等待200ms左右.1-2个本地存储的数据看不出太大的问题,但是当基类中出现加解密,以及需要获取到多个本地数据来配置参数时,就会出现大的问题
解决方案:
1.使用原生android SharedPreference类来操作本地存储的数据,已亲自验证,速度比AsyncStorage 和LocalStorage快很多,代码见最上面分享,若需要本地android 原生通信相关代码,可留言我.
2.进行数据缓存,具体是直接从内存中读取,在首次进来时,将所需代码一次性读取到内存中,后续同步修改,删除.代码如下(具体看token获取部分):
async request(method, url, params, configType? = 'v1/') {
// const userAgent = DeviceInfo.getUserAgent();
let net = await NetInfo.getConnectionInfo();
// console.log(net)
let plat = 'Beibeipay/5.0';
let type = `(${DeviceInfo.getSystemName()}; ${DeviceInfo.getDeviceName()}; ${DeviceInfo.getSystemName()} ${DeviceInfo.getSystemVersion()})`;
// console.log(type)
let version = `Mobile/${DeviceInfo.getSystemVersion()}`
let appName = `Beibeipay/${DeviceInfo.getVersion()}`;
let ptn = `Partner/${config.partner}`;
let netType = `NetType/${net.type}`;
let lan = `Language/zh_CN`;
let sysInfo = `${plat} ${type} ${version} RN${appName} ${ptn} ${netType} ${lan}`;
// const token = await AsyncStorage.getItem('userToken');
const token = ConfigData.getInstance().getUserToken();
// console.log(sysInfo)
return axios.request({
url: url,
baseURL: config.apiBaseUrl + configType,
headers: {
// 'Content-Type': 'application/json',
'X-Api-Key': token,
'X-Api-Partner': config.partner,
'User-Agent': sysInfo
},
timeout: 30000,
method: method,
data: method.toLowerCase() !== 'get' ? params : null,
params: method.toLowerCase() === 'get' ? params : null,
responseType: 'json',
transformRequest: [function(data) {
if (data instanceof FormData) {
return data
}
return Qs.stringify(data, {
arrayFormat: 'brackets'
})
}],
transformResponse: [function(response) {
if (response.status === 401 || response.status === 403 || response.code === '40401' || response.code === '40403') {
// Toast(response.msg)
// 跳转到登录页面 传参是为了 走到login登录时强制刷新tab里面
NavigationModule.navigate('Login', {
origin: 'baseRequest'
})
} else if (response.code) {
Toast(response.msg)
}
return response
}]
})
}
ConfigData类:
import React, {Component} from 'react';
import AsyncStorage from '@react-native-community/async-storage';
export default class ConfigData {
static instance;
userToken = "";
constructor(){
}
static getInstance = () => {
if(!ConfigData.instance) {
ConfigData.instance = new ConfigData();
}
return ConfigData.instance;
};
setUserToken(userToken,saveToDisk? = true){
this.userToken = userToken;
if(saveToDisk) {
AsyncStorage.setItem('userToken', userToken);
}
}
getUserToken() {
return this.userToken;
}
}