【架构实战】移动端网络优化:弱网加速方案

一、移动端网络特点

移动网络面临诸多挑战:

网络特点:

  • 高延迟(2G: 400ms, 3G: 100ms, 4G: 20ms)
  • 不稳定(频繁切换、信号强弱变化)
  • 带宽有限
  • 流量成本高

二、弱网检测

1. 网络状态监听

javascript 复制代码
// React Native网络检测
import NetInfo from "@react-native-community/netinfo";

useEffect(() => {
  const unsubscribe = 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();
}, []);

// 获取当前网络信息
const checkNetwork = async () => {
  const state = await NetInfo.fetch();
  return state;
};

2. 智能重试机制

java 复制代码
// Android OkHttp拦截器
public class RetryInterceptor implements Interceptor {
    
    private static final int MAX_RETRIES = 3;
    private static final long RETRY_DELAY_MS = 1000;
    
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Response response = null;
        IOException lastException = null;
        
        for (int attempt = 0; attempt < MAX_RETRIES; attempt++) {
            try {
                response = chain.proceed(request);
                
                if (response.isSuccessful()) {
                    return response;
                }
                
                // 如果是可重试的错误码
                if (isRetryable(response.code())) {
                    response.close();
                    continue;
                }
                
                return response;
                
            } catch (IOException e) {
                lastException = e;
                
                // 检测网络是否可用
                if (!isNetworkAvailable()) {
                    throw new NoNetworkException("网络不可用");
                }
                
                // 指数退避
                if (attempt < MAX_RETRIES - 1) {
                    try {
                        Thread.sleep(RETRY_DELAY_MS * (attempt + 1));
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            }
        }
        
        throw lastException;
    }
    
    private boolean isRetryable(int code) {
        return code >= 500 || code == 408 || code == 429;
    }
}

三、请求优化

1. 请求合并

typescript 复制代码
// Flutter请求合并
class RequestBatcher {
  private pendingRequests = new Map<string, Promise<any>>();
  private batchTimer: NodeJS.Timeout | null = null;
  private readonly BATCH_DELAY = 50; // 50ms内合并请求
  
  async batchRequest<T>(key: string, request: () => Promise<T>): Promise<T> {
    // 如果已有相同请求在处理中,返回同一个Promise
    if (this.pendingRequests.has(key)) {
      return this.pendingRequests.get(key) as Promise<T>;
    }
    
    const promise = request();
    this.pendingRequests.set(key, promise);
    
    // 延迟清理
    setTimeout(() => {
      this.pendingRequests.delete(key);
    }, this.BATCH_DELAY);
    
    return promise;
  }
}

2. 请求优先级

typescript 复制代码
// 请求优先级队列
enum RequestPriority {
  HIGH = 0,   // 用户操作相关,如点击、滑动
  NORMAL = 1, // 普通数据请求
  LOW = 2     // 预加载、统计等
}

class PriorityRequestQueue {
  private queues: Map<RequestPriority, Queue[]> = new Map([
    [RequestPriority.HIGH, []],
    [RequestPriority.NORMAL, []],
    [RequestPriority.LOW, []]
  ]);
  
  addRequest(request: () => Promise<any>, priority: RequestPriority) {
    const queue = this.queues.get(priority)!;
    return new Promise((resolve, reject) => {
      queue.push({ request, resolve, reject });
      this.processQueue();
    });
  }
  
  private async processQueue() {
    for (const [priority, queue] of this.queues) {
      while (queue.length > 0) {
        const { request, resolve, reject } = queue.shift()!;
        try {
          const result = await request();
          resolve(result);
        } catch (e) {
          reject(e);
        }
        
        // 高优先级请求可以中断低优先级
        if (priority === RequestPriority.HIGH) {
          break;
        }
      }
    }
  }
}

四、数据压缩

1. 请求体压缩

typescript 复制代码
// Flutter请求压缩
import { gzip } from 'pako';

class CompressedHttpClient {
  
  async postCompressed<T>(url: string, data: any): Promise<T> {
    const jsonString = JSON.stringify(data);
    const compressed = gzip(jsonString);
    
    const response = await fetch(url, {
      method: 'POST',
      headers: {
        'Content-Encoding': 'gzip',
        'Content-Type': 'application/json'
      },
      body: compressed
    });
    
    return response.json();
  }
}

2. 响应压缩处理

typescript 复制代码
// 处理压缩响应
async function fetchDecompressed(url: string): Promise<any> {
  const response = await fetch(url);
  
  const contentEncoding = response.headers.get('Content-Encoding');
  let data;
  
  if (contentEncoding === 'gzip') {
    const buffer = await response.arrayBuffer();
    const decompressed = ungzip(buffer);
    const text = new TextDecoder().decode(decompressed);
    data = JSON.parse(text);
  } else if (contentEncoding === 'br') {
    const buffer = await response.arrayBuffer();
    const decompressed = brotliDecompress(buffer);
    const text = new TextDecoder().decode(decompressed);
    data = JSON.parse(text);
  } else {
    data = await response.json();
  }
  
  return data;
}

五、本地缓存

1. SQLite缓存

java 复制代码
// Android Room缓存
@Entity(tableName = "api_cache")
public class ApiCache {
    @PrimaryKey
    public String key;
    public String response;
    public long timestamp;
    public long expireTime;
}

@Dao
public interface ApiCacheDao {
    
    @Query("SELECT * FROM api_cache WHERE key = :key AND timestamp + expireTime > :currentTime")
    ApiCache getValidCache(String key, long currentTime);
    
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insert(ApiCache cache);
    
    @Query("DELETE FROM api_cache WHERE timestamp + expireTime < :currentTime")
    void deleteExpired(long currentTime);
}

public class CacheRepository {
    
    public <T> T getOrFetch(String key, long expireMs, Supplier<T> fetcher) {
        // 查缓存
        long currentTime = System.currentTimeMillis();
        ApiCache cache = cacheDao.getValidCache(key, currentTime);
        
        if (cache != null) {
            return parseJson(cache.response);
        }
        
        // 缓存未命中,请求网络
        T result = fetcher.get();
        
        // 存入缓存
        ApiCache newCache = new ApiCache();
        newCache.key = key;
        newCache.response = toJson(result);
        newCache.timestamp = currentTime;
        newCache.expireTime = expireMs;
        cacheDao.insert(newCache);
        
        return result;
    }
}

2. MMKV高速缓存

typescript 复制代码
// React Native MMKV
import { MMKV } from 'react-native-mmkv';

const storage = new MMKV({
  id: 'api-cache'
});

class MMKVCache {
  
  set<T>(key: string, value: T, expireMs?: number): void {
    const data = {
      value,
      expireTime: expireMs ? Date.now() + expireMs : null
    };
    storage.set(key, JSON.stringify(data));
  }
  
  get<T>(key: string): T | null {
    const json = storage.getString(key);
    if (!json) return null;
    
    const data = JSON.parse(json);
    
    if (data.expireTime && Date.now() > data.expireTime) {
      storage.delete(key);
      return null;
    }
    
    return data.value;
  }
}

六、离线优先

1. Service Worker缓存策略

javascript 复制代码
// Flutter Service Worker
const CACHE_NAME = 'app-cache-v1';
const OFFLINE_URL = '/offline.html';

// 缓存策略:Cache First
self.addEventListener('fetch', event => {
  if (event.request.mode === 'navigate') {
    event.respondWith(
      fetch(event.request).catch(() => {
        return caches.match(OFFLINE_URL);
      })
    );
    return;
  }
  
  event.respondWith(
    caches.match(event.request).then(response => {
      return response || fetch(event.request).then(fetchResponse => {
        return caches.open(CACHE_NAME).then(cache => {
          cache.put(event.request, fetchResponse.clone());
          return fetchResponse;
        });
      });
    })
  );
});

2. 离线数据同步

typescript 复制代码
// 离线数据队列
class OfflineQueue {
  private queue: OfflineRequest[] = [];
  
  // 网络离线时,存入队列
  async enqueue(request: OfflineRequest) {
    this.queue.push(request);
    await this.persist();
  }
  
  // 网络恢复后,同步队列
  async sync() {
    const pending = [...this.queue];
    
    for (const request of pending) {
      try {
        await this.execute(request);
        // 成功后移除
        this.queue = this.queue.filter(r => r.id !== request.id);
      } catch (e) {
        // 失败后重试
        request.retryCount++;
        if (request.retryCount >= 3) {
          // 重试超过3次,标记失败
          await this.markFailed(request);
          this.queue = this.queue.filter(r => r.id !== request.id);
        }
      }
    }
    
    await this.persist();
  }
}

七、弱网体验优化

1.骨架屏

tsx 复制代码
// 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. 渐进式加载

tsx 复制代码
// 图片渐进式加载
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>
  );
};

八、总结

移动端网络优化提升用户体验:

  • 网络检测:实时感知网络状态
  • 智能重试:指数退避提高成功率
  • 数据压缩:减少流量消耗
  • 本地缓存:减少网络请求
  • 离线优先:弱网也能使用

最佳实践:

  1. 做好网络状态检测
  2. 实现智能重试机制
  3. 使用本地缓存减少请求
  4. 优化弱网下的用户体验

个人观点,仅供参考

相关推荐
扬帆破浪15 小时前
sidecar崩溃后前端怎么续命 重启策略与状态保留
前端·人工智能·架构·开源·知识图谱
漓漾li16 小时前
每日面试题(2026-05-15)
架构·go·agent
三无推导16 小时前
OpenHuman 开源项目详解:个人 AI 助手架构与核心技术拆解
人工智能·性能优化·架构·开源·ai助手
安当加密17 小时前
AES-256直接加密就够了?微服务架构下的敏感数据加密:信封加密、格式保留加密和字段级加密全解析
微服务·云原生·架构
闵孚龙17 小时前
Claude Code 状态恢复机制全解析:自动压缩后文件、技能、计划与 Agent 上下文如何不断片?
人工智能·架构·claude
您^_^18 小时前
专家(一):Claude Code 微服务实战——6 个服务从拆分到 K8s 部署,$0.45 全套 YAML 照抄
人工智能·windows·微服务·架构·kubernetes·个人开发·claude code
小杍随笔19 小时前
【iNovel 后端架构深度解析:基于 Rust + Tauri 2 的桌面应用服务端设计】
jvm·架构·rust
小杍随笔19 小时前
Axum+Leptos全栈集成实战
开发语言·后端·架构·rust
Shota Kishi19 小时前
基于 Solana Geyser gRPC 数据流的 pump.fun 代币铸造实时检测:流式架构与 HTTP/2 协议分析
网络协议·http·架构