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

简介: 移动端网络特点

一、移动端网络特点

移动网络面临诸多挑战:

网络特点:

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

二、弱网 检测

1. 网络状态监听

// 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. 智能重试机制

// 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. 请求合并

// 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. 请求优先级

// 请求优先级队列
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. 请求体压缩

// 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. 响应压缩处理

// 处理压缩响应
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缓存

// 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高速缓存

// 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缓存 策略

// 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. 离 线数据 同步

// 离线数据队列
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.骨架屏

// 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>
  );
};

八、总结

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

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

最佳实践:

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

个人观点,仅供参考

目录
相关文章
|
15天前
|
人工智能 JSON 供应链
畅用7个月无影 JVS Claw |手把手教你把JVS改造成「科研与产业地理情报可视化大师」
LucianaiB分享零成本畅用JVS Claw教程(学生认证享7个月使用权),并开源GeoMind项目——将JVS改造为科研与产业地理情报可视化AI助手,支持飞书文档解析、地理编码与腾讯地图可视化,助力产业关系图谱构建。
23516 12
畅用7个月无影 JVS Claw |手把手教你把JVS改造成「科研与产业地理情报可视化大师」
|
3天前
|
Shell API 开发工具
Claude Code 快速上手指南(新手友好版)
AI编程工具卷疯啦!Claude Code凭借任务驱动+终端原生的特性,成了开发者的效率搭子。本文从安装、登录、切换国产模型到常用命令,手把手带新手快速上手,全程避坑,30分钟独立用起来。
1044 7
|
4天前
|
人工智能 BI 持续交付
Claude Code 深度适配 DeepSeek V4-Pro 实测:全场景通关与真实体验报告
在 AI 编程工具日趋主流的今天,Claude Code 凭借强大的任务执行、工具调用与工程化能力,成为开发者与自动化运维的核心效率工具。但随着原生模型账号稳定性问题频发,寻找一套兼容、稳定、能力在线的替代方案变得尤为重要。DeepSeek V4-Pro 作为新一代高性能大模型,提供了完整兼容 Claude 协议的 API 接口,只需简单配置即可无缝驱动 Claude Code,且在任务执行、工具调用、复杂流程处理上表现极为稳定。
1316 3
|
9天前
|
人工智能 缓存 Shell
Claude Code 全攻略:命令大全 + 实战工作流(完整版)
Claude Code 是一款运行在终端环境下的 AI 编码助手,能够直接在项目目录中理解代码结构、编辑文件、执行命令、执行开发计划,并支持持久化记忆、上下文压缩、后台任务、多模型切换等专业能力。对于日常开发、项目维护、快速重构、代码审查等场景,它可以大幅减少手动操作、提升编码效率。本文从常用命令、界面模式、核心指令、记忆机制、图片处理、进阶工作流等维度完整说明,帮助开发者快速上手并稳定使用。
2424 4
|
2天前
|
人工智能 JSON BI
DeepSeek V4-Pro 接入 Claude Code 完全实战:体验、测试与关键避坑指南
Claude Code 作为当前主流的 AI 编程辅助工具,凭借强大的代码理解、工程执行与自动化能力深受开发者喜爱,但原生模型的使用成本相对较高。为了在保持能力的同时进一步降低开销,不少开发者开始寻找兼容度高、价格更友好的替代模型。DeepSeek V4 系列的发布带来了新的选择,该系列包含 V4-Pro 与 V4-Flash 两款模型,并提供了与 Anthropic 完全兼容的 API 接口,理论上只需简单修改配置,即可让 Claude Code 无缝切换为 DeepSeek 引擎。
842 0
|
19天前
|
人工智能 缓存 BI
Claude Code + DeepSeek V4-Pro 真实评测:除了贵,没别的毛病
JeecgBoot AI专题研究 把 Claude Code 接入 DeepSeek V4Pro,跑完 Skills —— OA 审批、大屏、报表、部署 5 大实战场景后的真实体验 ![](https://oscimg.oschina.net/oscnet/up608d34aeb6bafc47f
5969 22
Claude Code + DeepSeek V4-Pro 真实评测:除了贵,没别的毛病
|
21天前
|
人工智能 JSON BI
DeepSeek V4 来了!超越 Claude Sonnet 4.5,赶紧对接 Claude Code 体验一把
JeecgBoot AI专题研究 把 Claude Code 接入 DeepSeek V4Pro 的真实体验与避坑记录 本文记录我将 Claude Code 对接 DeepSeek 最新模型(V4Pro)后的真实体验,测试了 Skills 自动化查询和积木报表 AI 建表两个场景——有惊喜,也踩
7196 18