Files
mkdocs/docs/android面试/性能优化/网络优化.md
2026-01-15 11:53:37 +08:00

18 KiB
Raw Permalink Blame History

网络优化

目录


网络请求优化

1. 请求合并

问题:频繁请求

// ❌ 错误示例:频繁请求
for (int i = 0; i < 100; i++) {
    api.getUserInfo(i).enqueue(callback);
}

解决方案:批量请求

// ✅ 正确做法:批量请求
List<Integer> userIds = new ArrayList<>();
for (int i = 0; i < 100; i++) {
    userIds.add(i);
}
api.getBatchUserInfo(userIds).enqueue(callback);

2. 请求去重

public class RequestDeduplicator {
    private final Map<String, Call> pendingRequests = new ConcurrentHashMap<>();
    
    public <T> void execute(String key, Call<T> call, Callback<T> callback) {
        Call<T> existingCall = (Call<T>) pendingRequests.get(key);
        if (existingCall != null) {
            // 已有相同请求,取消新请求
            call.cancel();
            return;
        }
        
        pendingRequests.put(key, call);
        call.enqueue(new Callback<T>() {
            @Override
            public void onResponse(Call<T> call, Response<T> response) {
                pendingRequests.remove(key);
                callback.onResponse(call, response);
            }
            
            @Override
            public void onFailure(Call<T> call, Throwable t) {
                pendingRequests.remove(key);
                callback.onFailure(call, t);
            }
        });
    }
}

3. 请求优先级

public class PriorityInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        
        // 根据优先级设置
        int priority = request.header("Priority") != null 
            ? Integer.parseInt(request.header("Priority")) 
            : 0;
        
        // 调整请求优先级
        if (priority > 0) {
            // 高优先级请求优先处理
        }
        
        return chain.proceed(request);
    }
}

4. 请求重试

public class RetryInterceptor implements Interceptor {
    private int maxRetries;
    
    public RetryInterceptor(int maxRetries) {
        this.maxRetries = maxRetries;
    }
    
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Response response = null;
        IOException exception = null;
        
        for (int i = 0; i <= maxRetries; i++) {
            try {
                response = chain.proceed(request);
                if (response.isSuccessful()) {
                    return response;
                }
            } catch (IOException e) {
                exception = e;
                if (i == maxRetries) {
                    throw e;
                }
            }
            
            // 等待后重试
            try {
                Thread.sleep(1000 * (i + 1));
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        
        throw exception;
    }
}

5. 连接池优化

OkHttpClient client = new OkHttpClient.Builder()
    .connectionPool(new ConnectionPool(10, 5, TimeUnit.MINUTES))
    .build();

6. 超时设置

OkHttpClient client = new OkHttpClient.Builder()
    .connectTimeout(10, TimeUnit.SECONDS)
    .readTimeout(30, TimeUnit.SECONDS)
    .writeTimeout(30, TimeUnit.SECONDS)
    .build();

图片加载优化

1. 使用图片加载库

// Glide自动缓存、压缩、内存管理
Glide.with(context)
    .load(url)
    .placeholder(R.drawable.placeholder)
    .error(R.drawable.error)
    .into(imageView);

// 指定尺寸
Glide.with(context)
    .load(url)
    .override(200, 200)
    .into(imageView);

2. 图片压缩

public Bitmap compressImage(Bitmap bitmap, int maxWidth, int maxHeight, int quality) {
    int width = bitmap.getWidth();
    int height = bitmap.getHeight();
    
    float scale = Math.min((float) maxWidth / width, (float) maxHeight / height);
    if (scale < 1.0f) {
        int newWidth = Math.round(width * scale);
        int newHeight = Math.round(height * scale);
        bitmap = Bitmap.createScaledBitmap(bitmap, newWidth, newHeight, true);
    }
    
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
    byte[] data = baos.toByteArray();
    
    return BitmapFactory.decodeByteArray(data, 0, data.length);
}

3. 使用 WebP 格式

// WebP 格式压缩率高
Glide.with(context)
    .load(url)
    .format(DecodeFormat.PREFER_WEBP)
    .into(imageView);

4. 懒加载

public class LazyLoadImageView extends ImageView {
    private String imageUrl;
    
    public void setImageUrl(String url) {
        this.imageUrl = url;
        loadImage();
    }
    
    private void loadImage() {
        if (isInViewport()) {
            Glide.with(getContext())
                .load(imageUrl)
                .into(this);
        }
    }
    
    private boolean isInViewport() {
        Rect rect = new Rect();
        getGlobalVisibleRect(rect);
        return rect.width() > 0 && rect.height() > 0;
    }
}

5. 预加载

// 预加载图片到缓存
Glide.with(context)
    .load(url)
    .preload();

// 预加载并获取 Bitmap
Glide.with(context)
    .asBitmap()
    .load(url)
    .into(new SimpleTarget<Bitmap>() {
        @Override
        public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) {
            // 预加载完成
        }
    });

数据压缩

1. Gzip 压缩

// 服务器端启用 Gzip
// 客户端自动解压OkHttp 支持)

OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(new GzipRequestInterceptor())
    .build();

public class GzipRequestInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request originalRequest = chain.request();
        
        if (originalRequest.body() == null || originalRequest.header("Content-Encoding") != null) {
            return chain.proceed(originalRequest);
        }
        
        Request compressedRequest = originalRequest.newBuilder()
            .header("Content-Encoding", "gzip")
            .method(originalRequest.method(), gzip(originalRequest.body()))
            .build();
        
        return chain.proceed(compressedRequest);
    }
    
    private RequestBody gzip(final RequestBody body) {
        return new RequestBody() {
            @Override
            public MediaType contentType() {
                return body.contentType();
            }
            
            @Override
            public long contentLength() {
                return -1;
            }
            
            @Override
            public void writeTo(BufferedSink sink) throws IOException {
                BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
                body.writeTo(gzipSink);
                gzipSink.close();
            }
        };
    }
}

2. 数据格式优化

// 使用 Protobuf 替代 JSON
// Protobuf 体积更小,解析更快

// 使用 MessagePack 替代 JSON
// MessagePack 是二进制格式,体积更小

3. 分页加载

public class PagingHelper {
    private int pageSize = 20;
    private int currentPage = 0;
    private boolean hasMore = true;
    
    public void loadMore() {
        if (!hasMore) {
            return;
        }
        
        api.getData(currentPage, pageSize).enqueue(new Callback<DataResponse>() {
            @Override
            public void onResponse(Call<DataResponse> call, Response<DataResponse> response) {
                if (response.isSuccessful()) {
                    DataResponse data = response.body();
                    if (data.getItems().size() < pageSize) {
                        hasMore = false;
                    }
                    currentPage++;
                    // 处理数据
                }
            }
            
            @Override
            public void onFailure(Call<DataResponse> call, Throwable t) {
                // 处理错误
            }
        });
    }
}

缓存策略

1. HTTP 缓存

// 使用 OkHttp 缓存
int cacheSize = 10 * 1024 * 1024; // 10MB
Cache cache = new Cache(context.getCacheDir(), cacheSize);

OkHttpClient client = new OkHttpClient.Builder()
    .cache(cache)
    .build();

2. 内存缓存

public class MemoryCache {
    private final LruCache<String, Bitmap> cache;
    
    public MemoryCache() {
        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        int cacheSize = maxMemory / 8;
        
        cache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                return bitmap.getByteCount() / 1024;
            }
        };
    }
    
    public Bitmap get(String key) {
        return cache.get(key);
    }
    
    public void put(String key, Bitmap bitmap) {
        cache.put(key, bitmap);
    }
}

3. 磁盘缓存

public class DiskCache {
    private final File cacheDir;
    
    public DiskCache(Context context) {
        cacheDir = new File(context.getCacheDir(), "images");
        if (!cacheDir.exists()) {
            cacheDir.mkdirs();
        }
    }
    
    public void put(String key, Bitmap bitmap) {
        File file = new File(cacheDir, key);
        try {
            FileOutputStream fos = new FileOutputStream(file);
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public Bitmap get(String key) {
        File file = new File(cacheDir, key);
        if (file.exists()) {
            return BitmapFactory.decodeFile(file.getAbsolutePath());
        }
        return null;
    }
}

4. 三级缓存策略

public class ImageLoader {
    private MemoryCache memoryCache;
    private DiskCache diskCache;
    private NetworkLoader networkLoader;
    
    public void load(String url, ImageView imageView) {
        // 1. 检查内存缓存
        Bitmap bitmap = memoryCache.get(url);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            return;
        }
        
        // 2. 检查磁盘缓存
        bitmap = diskCache.get(url);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
            memoryCache.put(url, bitmap);
            return;
        }
        
        // 3. 从网络加载
        networkLoader.load(url, new NetworkLoader.Callback() {
            @Override
            public void onSuccess(Bitmap bitmap) {
                imageView.setImageBitmap(bitmap);
                memoryCache.put(url, bitmap);
                diskCache.put(url, bitmap);
            }
            
            @Override
            public void onFailure(Throwable t) {
                // 处理错误
            }
        });
    }
}

网络监控

1. 网络状态监听

public class NetworkMonitor {
    private ConnectivityManager connectivityManager;
    private NetworkCallback networkCallback;
    
    public void register(Context context) {
        connectivityManager = (ConnectivityManager) 
            context.getSystemService(Context.CONNECTIVITY_SERVICE);
        
        networkCallback = new ConnectivityManager.NetworkCallback() {
            @Override
            public void onAvailable(Network network) {
                // 网络可用
            }
            
            @Override
            public void onLost(Network network) {
                // 网络不可用
            }
            
            @Override
            public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
                // 网络能力变化
                if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
                    // WiFi 网络
                } else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
                    // 移动网络
                }
            }
        };
        
        NetworkRequest request = new NetworkRequest.Builder()
            .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
            .build();
        
        connectivityManager.registerNetworkCallback(request, networkCallback);
    }
    
    public void unregister() {
        if (connectivityManager != null && networkCallback != null) {
            connectivityManager.unregisterNetworkCallback(networkCallback);
        }
    }
}

2. 请求监控

public class NetworkMonitorInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        long startTime = System.currentTimeMillis();
        
        Response response = chain.proceed(request);
        
        long endTime = System.currentTimeMillis();
        long duration = endTime - startTime;
        
        // 记录请求信息
        Log.d("Network", String.format(
            "Request: %s, Duration: %dms, Size: %d bytes",
            request.url(),
            duration,
            response.body() != null ? response.body().contentLength() : 0
        ));
        
        return response;
    }
}

3. 网络质量检测

public class NetworkQualityChecker {
    public void checkNetworkQuality(Callback callback) {
        long startTime = System.currentTimeMillis();
        
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
            .url("https://www.google.com")
            .build();
        
        client.newCall(request).enqueue(new okhttp3.Callback() {
            @Override
            public void onResponse(Call call, Response response) {
                long duration = System.currentTimeMillis() - startTime;
                if (duration < 500) {
                    callback.onQuality(NetworkQuality.EXCELLENT);
                } else if (duration < 1000) {
                    callback.onQuality(NetworkQuality.GOOD);
                } else {
                    callback.onQuality(NetworkQuality.POOR);
                }
            }
            
            @Override
            public void onFailure(Call call, IOException e) {
                callback.onQuality(NetworkQuality.POOR);
            }
        });
    }
    
    enum NetworkQuality {
        EXCELLENT, GOOD, POOR
    }
    
    interface Callback {
        void onQuality(NetworkQuality quality);
    }
}

网络优化工具

1. Chrome DevTools

  • 查看网络请求
  • 分析请求时间
  • 查看请求大小
  • 检查缓存策略

2. Charles/Fiddler

  • 抓包工具
  • 查看请求响应
  • 模拟慢网络
  • 修改请求响应

3. Network Profiler

  • Android Studio -> View -> Tool Windows -> Profiler
  • 实时监控网络请求
  • 查看请求时间
  • 分析网络性能

4. Stetho

// 集成 Stetho
dependencies {
    implementation 'com.facebook.stetho:stetho:1.5.1'
    implementation 'com.facebook.stetho:stetho-okhttp3:1.5.1'
}

// 初始化
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        Stetho.initializeWithDefaults(this);
    }
}

// 添加到 OkHttpClient
OkHttpClient client = new OkHttpClient.Builder()
    .addNetworkInterceptor(new StethoInterceptor())
    .build();

面试常见问题

Q1: 如何优化网络请求?

答案:

  1. 请求合并:将多个请求合并为一个批量请求
  2. 请求去重:避免重复请求相同数据
  3. 请求优先级:重要请求优先处理
  4. 请求重试:失败请求自动重试
  5. 连接池优化:复用连接,减少建立连接时间
  6. 超时设置:合理设置超时时间

Q2: 如何优化图片加载?

答案:

  1. 使用图片加载库Glide、Picasso
  2. 图片压缩:使用合适的尺寸和格式
  3. 使用 WebP 格式
  4. 懒加载:只在可见时加载
  5. 预加载:提前加载可能需要的图片
  6. 三级缓存:内存缓存 + 磁盘缓存 + 网络

Q3: 什么是三级缓存?

答案:

  1. 内存缓存:最快,但容量有限
  2. 磁盘缓存:速度中等,容量较大
  3. 网络加载:最慢,但数据最新

Q4: 如何实现数据压缩?

答案:

  1. Gzip 压缩HTTP 请求响应压缩
  2. 数据格式优化:使用 Protobuf、MessagePack 替代 JSON
  3. 分页加载:减少单次请求数据量
  4. 字段精简:只请求必要字段

Q5: 缓存策略有哪些?

答案:

  1. HTTP 缓存:利用 HTTP 缓存头Cache-Control、ETag
  2. 内存缓存:使用 LruCache
  3. 磁盘缓存:持久化缓存
  4. 智能缓存:根据数据更新频率选择缓存策略

Q6: 如何监控网络性能?

答案:

  1. 使用 Network Profiler 实时监控
  2. 使用拦截器记录请求信息
  3. 监听网络状态变化
  4. 检测网络质量
  5. 使用 Chrome DevTools、Charles 等工具

Q7: 网络优化的最佳实践?

答案:

  1. 合并请求,减少请求次数
  2. 使用缓存,减少网络请求
  3. 压缩数据,减少传输量
  4. 优化图片,使用合适格式和尺寸
  5. 监控网络,及时发现问题
  6. 处理网络异常,提供降级方案

总结

网络优化是 Android 性能优化的重要部分,主要从以下几个方面入手:

  1. 请求优化:合并、去重、优先级、重试
  2. 图片优化:压缩、格式、懒加载、缓存
  3. 数据压缩Gzip、格式优化、分页
  4. 缓存策略:三级缓存、智能缓存
  5. 网络监控:状态监听、请求监控、质量检测

通过合理的网络优化,可以减少网络请求,提升加载速度,改善用户体验。


最后更新2024年