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

简介: 移动端网络特点

一、移动端网络特点

移动网络面临诸多挑战:

网络特点:

  • 高延迟(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. 优化弱网下的用户体验

个人观点,仅供参考

目录
相关文章
|
2月前
|
安全 索引 Python
使用Python轻松玩转Excel工作表:添加与删除实战指南
本文以轻松对话形式,介绍如何用Python(openpyxl库)高效批量处理Excel:三行代码添加/删除工作表,一个循环搞定二十多个文件;附避坑指南与实战工具,助办公族告别重复操作,提升效率。(239字)
237 2
|
编解码 Ubuntu Linux
Linux应用开发基础知识——Framebuffer 应用编程(四)
Linux应用开发基础知识——Framebuffer 应用编程(四)
433 0
Linux应用开发基础知识——Framebuffer 应用编程(四)
|
存储 分布式计算 Hadoop
基于docker的Hadoop环境搭建与应用实践(脚本部署)
本文介绍了Hadoop环境的搭建与应用实践。对Hadoop的概念和原理进行了简要说明,包括HDFS分布式文件系统和MapReduce计算模型等,主要通过脚本的方式进行快捷部署,在部署完成后对HDFS和mapreduce进行了测试,确保其功能正常。
|
8月前
|
JavaScript 开发工具 Android开发
如何在原生 App 中调用 Uniapp 的页面?
如何在原生 App 中调用 Uniapp 的页面?
2142 138
|
4月前
|
Web App开发 存储 JavaScript
多平台文章自动同步助手插件,发文提效工具——开源免费推荐
SyncCaster 是一款本地运行的 Chrome 扩展,支持网页采集、Markdown 编辑与一键同步至掘金、CSDN、知乎、微信公众号等 17+ 平台,内置 LaTeX、Mermaid 和样式保留功能,全程离线、隐私无忧。(239字)
834 5
|
8月前
|
存储 数据采集 人工智能
拔俗AI一体化教学平台:让教、学、评真正“一键打通”
在教育信息化2.0背景下,针对数据孤岛、个性化不足等痛点,本文基于阿里云AI与大数据技术,构建“三横三纵”云原生架构的AI一体化教学平台,实现教、学、管全流程智能化。通过多模态数据采集、教育数据湖治理、大模型驱动的智能引擎及多角色应用协同,打通系统壁垒,提升教学效率与个性化水平,并已在省级重点中学成功落地,显著提效增质。
726 0
|
搜索推荐 定位技术 图形学
用unity给老婆设计一个单机版大富翁
用unity给老婆设计一个单机版大富翁
Threejs用下个点方法实现模型沿着轨道行驶
这篇文章讲解了如何在Three.js中通过计算下一个路径点来控制模型沿轨迹行驶的方向,使用`lookAt`方法使模型面向行驶方向,实现了更加自然的移动效果。
449 1
|
数据可视化 数据挖掘 Python
绘制带误差分析的柱状图
【9月更文挑战第1天】在数据分析与科研中,带误差分析的柱状图能直观展示数据分布与不确定性。本文介绍使用Python的Matplotlib库和Excel绘制此类图表的方法,包括安装库、准备数据、绘制图表及添加误差线等步骤,帮助用户根据需求进行调整与定制。
571 5
|
存储 前端开发 IDE
【华为鸿蒙系统学习】- 如何利用鸿蒙系统进行App项目开发|自学篇
【华为鸿蒙系统学习】- 如何利用鸿蒙系统进行App项目开发|自学篇
1150 0