# 网络优化

## 目录
- [网络请求优化](#网络请求优化)
- [图片加载优化](#图片加载优化)
- [数据压缩](#数据压缩)
- [缓存策略](#缓存策略)
- [网络监控](#网络监控)
- [网络优化工具](#网络优化工具)
- [面试常见问题](#面试常见问题)

---

## 网络请求优化

### 1. 请求合并

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

#### 解决方案：批量请求
```java
// ✅ 正确做法：批量请求
List<Integer> userIds = new ArrayList<>();
for (int i = 0; i < 100; i++) {
    userIds.add(i);
}
api.getBatchUserInfo(userIds).enqueue(callback);
```

### 2. 请求去重

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

```java
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. 请求重试

```java
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. 连接池优化

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

### 6. 超时设置

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

---

## 图片加载优化

### 1. 使用图片加载库

```java
// 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. 图片压缩

```java
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 格式

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

### 4. 懒加载

```java
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. 预加载

```java
// 预加载图片到缓存
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 压缩

```java
// 服务器端启用 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. 数据格式优化

```java
// 使用 Protobuf 替代 JSON
// Protobuf 体积更小，解析更快

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

### 3. 分页加载

```java
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 缓存

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

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

### 2. 内存缓存

```java
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. 磁盘缓存

```java
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. 三级缓存策略

```java
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. 网络状态监听

```java
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. 请求监控

```java
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. 网络质量检测

```java
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

```java
// 集成 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年*
