一、移动端网络特点
移动网络面临诸多挑战:
网络特点:
- 高延迟(2G: 400ms, 3G: 100ms, 4G: 20ms)
- 不稳定(频繁切换、信号强弱变化)
- 带宽有限
- 流量成本高
二、弱网检测
1. 网络状态监听
// React Native网络检测importNetInfofrom"@react-native-community/netinfo";useEffect(()=>{constunsubscribe=NetInfo.addEventListener(state=>{console.log("Connection type:",state.type);console.log("Is connected?:",state.isConnected);// 检测网络类型if(state.type==='cellular'){console.log("当前使用移动网络");if(state.details.isConnectionExpensive){console.log("节省流量模式");}}});return()=>unsubscribe();},[]);// 获取当前网络信息constcheckNetwork=async()=>{conststate=awaitNetInfo.fetch();returnstate;};2. 智能重试机制
// Android OkHttp拦截器publicclassRetryInterceptorimplementsInterceptor{privatestaticfinalintMAX_RETRIES=3;privatestaticfinallongRETRY_DELAY_MS=1000;@OverridepublicResponseintercept(Chainchain)throwsIOException{Requestrequest=chain.request();Responseresponse=null;IOExceptionlastException=null;for(intattempt=0;attempt<MAX_RETRIES;attempt++){try{response=chain.proceed(request);if(response.isSuccessful()){returnresponse;}// 如果是可重试的错误码if(isRetryable(response.code())){response.close();continue;}returnresponse;}catch(IOExceptione){lastException=e;// 检测网络是否可用if(!isNetworkAvailable()){thrownewNoNetworkException("网络不可用");}// 指数退避if(attempt<MAX_RETRIES-1){try{Thread.sleep(RETRY_DELAY_MS*(attempt+1));}catch(InterruptedExceptionie){Thread.currentThread().interrupt();break;}}}}throwlastException;}privatebooleanisRetryable(intcode){returncode>=500||code==408||code==429;}}三、请求优化
1. 请求合并
// Flutter请求合并classRequestBatcher{privatependingRequests=newMap<string,Promise<any>>();privatebatchTimer:NodeJS.Timeout|null=null;privatereadonlyBATCH_DELAY=50;// 50ms内合并请求asyncbatchRequest<T>(key:string,request:()=>Promise<T>):Promise<T>{// 如果已有相同请求在处理中,返回同一个Promiseif(this.pendingRequests.has(key)){returnthis.pendingRequests.get(key)asPromise<T>;}constpromise=request();this.pendingRequests.set(key,promise);// 延迟清理setTimeout(()=>{this.pendingRequests.delete(key);},this.BATCH_DELAY);returnpromise;}}2. 请求优先级
// 请求优先级队列enumRequestPriority{HIGH=0,// 用户操作相关,如点击、滑动NORMAL=1,// 普通数据请求LOW=2// 预加载、统计等}classPriorityRequestQueue{privatequeues:Map<RequestPriority,Queue[]>=newMap([[RequestPriority.HIGH,[]],[RequestPriority.NORMAL,[]],[RequestPriority.LOW,[]]]);addRequest(request:()=>Promise<any>,priority:RequestPriority){constqueue=this.queues.get(priority)!;returnnewPromise((resolve,reject)=>{queue.push({request,resolve,reject});this.processQueue();});}privateasyncprocessQueue(){for(const[priority,queue]ofthis.queues){while(queue.length>0){const{request,resolve,reject}=queue.shift()!;try{constresult=awaitrequest();resolve(result);}catch(e){reject(e);}// 高优先级请求可以中断低优先级if(priority===RequestPriority.HIGH){break;}}}}}四、数据压缩
1. 请求体压缩
// Flutter请求压缩import{gzip}from'pako';classCompressedHttpClient{asyncpostCompressed<T>(url:string,data:any):Promise<T>{constjsonString=JSON.stringify(data);constcompressed=gzip(jsonString);constresponse=awaitfetch(url,{method:'POST',headers:{'Content-Encoding':'gzip','Content-Type':'application/json'},body:compressed});returnresponse.json();}}2. 响应压缩处理
// 处理压缩响应asyncfunctionfetchDecompressed(url:string):Promise<any>{constresponse=awaitfetch(url);constcontentEncoding=response.headers.get('Content-Encoding');letdata;if(contentEncoding==='gzip'){constbuffer=awaitresponse.arrayBuffer();constdecompressed=ungzip(buffer);consttext=newTextDecoder().decode(decompressed);data=JSON.parse(text);}elseif(contentEncoding==='br'){constbuffer=awaitresponse.arrayBuffer();constdecompressed=brotliDecompress(buffer);consttext=newTextDecoder().decode(decompressed);data=JSON.parse(text);}else{data=awaitresponse.json();}returndata;}五、本地缓存
1. SQLite缓存
// Android Room缓存@Entity(tableName="api_cache")publicclassApiCache{@PrimaryKeypublicStringkey;publicStringresponse;publiclongtimestamp;publiclongexpireTime;}@DaopublicinterfaceApiCacheDao{@Query("SELECT * FROM api_cache WHERE key = :key AND timestamp + expireTime > :currentTime")ApiCachegetValidCache(Stringkey,longcurrentTime);@Insert(onConflict=OnConflictStrategy.REPLACE)voidinsert(ApiCachecache);@Query("DELETE FROM api_cache WHERE timestamp + expireTime < :currentTime")voiddeleteExpired(longcurrentTime);}publicclassCacheRepository{public<T>TgetOrFetch(Stringkey,longexpireMs,Supplier<T>fetcher){// 查缓存longcurrentTime=System.currentTimeMillis();ApiCachecache=cacheDao.getValidCache(key,currentTime);if(cache!=null){returnparseJson(cache.response);}// 缓存未命中,请求网络Tresult=fetcher.get();// 存入缓存ApiCachenewCache=newApiCache();newCache.key=key;newCache.response=toJson(result);newCache.timestamp=currentTime;newCache.expireTime=expireMs;cacheDao.insert(newCache);returnresult;}}2. MMKV高速缓存
// React Native MMKVimport{MMKV}from'react-native-mmkv';conststorage=newMMKV({id:'api-cache'});classMMKVCache{set<T>(key:string,value:T,expireMs?:number):void{constdata={value,expireTime:expireMs?Date.now()+expireMs:null};storage.set(key,JSON.stringify(data));}get<T>(key:string):T|null{constjson=storage.getString(key);if(!json)returnnull;constdata=JSON.parse(json);if(data.expireTime&&Date.now()>data.expireTime){storage.delete(key);returnnull;}returndata.value;}}六、离线优先
1. Service Worker缓存策略
// Flutter Service WorkerconstCACHE_NAME='app-cache-v1';constOFFLINE_URL='/offline.html';// 缓存策略:Cache Firstself.addEventListener('fetch',event=>{if(event.request.mode==='navigate'){event.respondWith(fetch(event.request).catch(()=>{returncaches.match(OFFLINE_URL);}));return;}event.respondWith(caches.match(event.request).then(response=>{returnresponse||fetch(event.request).then(fetchResponse=>{returncaches.open(CACHE_NAME).then(cache=>{cache.put(event.request,fetchResponse.clone());returnfetchResponse;});});}));});2. 离线数据同步
// 离线数据队列classOfflineQueue{privatequeue:OfflineRequest[]=[];// 网络离线时,存入队列asyncenqueue(request:OfflineRequest){this.queue.push(request);awaitthis.persist();}// 网络恢复后,同步队列asyncsync(){constpending=[...this.queue];for(constrequestofpending){try{awaitthis.execute(request);// 成功后移除this.queue=this.queue.filter(r=>r.id!==request.id);}catch(e){// 失败后重试request.retryCount++;if(request.retryCount>=3){// 重试超过3次,标记失败awaitthis.markFailed(request);this.queue=this.queue.filter(r=>r.id!==request.id);}}}awaitthis.persist();}}七、弱网体验优化
1.骨架屏
// React骨架屏 const ProductCardSkeleton = () => ( <View style={styles.card}> <View style={styles.imageSkeleton} /> <View style={styles.titleSkeleton} /> <View style={styles.priceSkeleton} /> </View> ); const ProductCard = ({ product, isLoading }) => { if (isLoading) { return <ProductCardSkeleton />; } return ( <View style={styles.card}> <Image source={{ uri: product.image }} /> <Text>{product.title}</Text> <Text>¥{product.price}</Text> </View> ); };2. 渐进式加载
// 图片渐进式加载 const ProgressiveImage = ({ uri, style }) => { const [loaded, setLoaded] = useState(false); const [error, setError] = useState(false); return ( <View style={style}> {/* 先显示缩略图 */} <Image source={{ uri: uri + '?w=50&h=50&fit=cover' }} style={[style, { opacity: loaded ? 1 : 0 }]} onLoad={() => setLoaded(true)} /> {/* 占位图 */} {!loaded && !error && ( <View style={[styles.placeholder, style]} /> )} </View> ); };八、总结
移动端网络优化提升用户体验:
- 网络检测:实时感知网络状态
- 智能重试:指数退避提高成功率
- 数据压缩:减少流量消耗
- 本地缓存:减少网络请求
- 离线优先:弱网也能使用
最佳实践:
- 做好网络状态检测
- 实现智能重试机制
- 使用本地缓存减少请求
- 优化弱网下的用户体验
个人观点,仅供参考