# 问题排查经验

## 目录
- [崩溃问题排查](#崩溃问题排查)
- [内存泄漏排查](#内存泄漏排查)
- [性能问题排查](#性能问题排查)
- [网络问题排查](#网络问题排查)
- [问题排查工具](#问题排查工具)
- [问题排查经验总结](#问题排查经验总结)
- [面试常见问题](#面试常见问题)

---

## 崩溃问题排查

### 常见崩溃类型

#### 1. NullPointerException

```java
// 问题代码
String name = user.getName(); // user 为 null
int length = name.length(); // NullPointerException

// 排查方法
// 1. 查看堆栈信息
// 2. 检查对象是否为 null
// 3. 添加空值检查

// 解决方案
if (user != null) {
    String name = user.getName();
    if (name != null) {
        int length = name.length();
    }
}
```

#### 2. IndexOutOfBoundsException

```java
// 问题代码
List<String> list = getList();
String item = list.get(10); // 列表只有 5 个元素

// 排查方法
// 1. 查看堆栈信息
// 2. 检查列表大小
// 3. 添加边界检查

// 解决方案
if (list != null && list.size() > 10) {
    String item = list.get(10);
}
```

#### 3. ClassCastException

```java
// 问题代码
Object obj = getObject();
String str = (String) obj; // obj 不是 String 类型

// 排查方法
// 1. 查看堆栈信息
// 2. 检查对象类型
// 3. 使用 instanceof 检查

// 解决方案
if (obj instanceof String) {
    String str = (String) obj;
}
```

#### 4. OutOfMemoryError

```java
// 问题代码
List<Bitmap> bitmaps = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
    Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
    bitmaps.add(bitmap); // 内存溢出
}

// 排查方法
// 1. 使用 Memory Profiler 分析内存
// 2. 检查大对象
// 3. 检查内存泄漏

// 解决方案
// 1. 压缩图片
// 2. 及时释放 Bitmap
// 3. 使用对象池
```

### 崩溃排查流程

```java
// 1. 收集崩溃信息
public class CrashHandler implements Thread.UncaughtExceptionHandler {
    @Override
    public void uncaughtException(Thread t, Throwable e) {
        // 收集崩溃信息
        String crashInfo = collectCrashInfo(e);
        
        // 保存到文件
        saveCrashInfo(crashInfo);
        
        // 上传到服务器
        uploadCrashInfo(crashInfo);
    }
    
    private String collectCrashInfo(Throwable e) {
        StringBuilder sb = new StringBuilder();
        sb.append("Exception: ").append(e.getClass().getName()).append("\n");
        sb.append("Message: ").append(e.getMessage()).append("\n");
        sb.append("Stack Trace:\n");
        
        StackTraceElement[] stackTrace = e.getStackTrace();
        for (StackTraceElement element : stackTrace) {
            sb.append(element.toString()).append("\n");
        }
        
        return sb.toString();
    }
}
```

### 崩溃日志分析

```java
// 分析崩溃日志
public class CrashAnalyzer {
    public void analyze(String crashLog) {
        // 1. 提取异常类型
        String exceptionType = extractExceptionType(crashLog);
        
        // 2. 提取堆栈信息
        List<String> stackTrace = extractStackTrace(crashLog);
        
        // 3. 定位问题代码
        String problemCode = locateProblemCode(stackTrace);
        
        // 4. 分析原因
        String reason = analyzeReason(exceptionType, problemCode);
        
        // 5. 提供解决方案
        String solution = provideSolution(reason);
    }
}
```

---

## 内存泄漏排查

### 内存泄漏常见场景

#### 1. 静态变量持有 Context

```java
// ❌ 问题代码
public class MyActivity extends AppCompatActivity {
    private static Context sContext;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        sContext = this; // 静态变量持有 Activity
    }
}

// ✅ 解决方案
public class MyActivity extends AppCompatActivity {
    private static Context sContext;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        sContext = getApplicationContext(); // 使用 Application Context
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        sContext = null; // 及时释放
    }
}
```

#### 2. Handler 内存泄漏

```java
// ❌ 问题代码
public class MainActivity extends AppCompatActivity {
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // Handler 持有 Activity 引用
        }
    };
}

// ✅ 解决方案
public class MainActivity extends AppCompatActivity {
    private static class MyHandler extends Handler {
        private WeakReference<MainActivity> mActivity;
        
        MyHandler(MainActivity activity) {
            mActivity = new WeakReference<>(activity);
        }
        
        @Override
        public void handleMessage(Message msg) {
            MainActivity activity = mActivity.get();
            if (activity == null || activity.isFinishing()) {
                return;
            }
        }
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }
}
```

#### 3. 监听器未注销

```java
// ❌ 问题代码
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SensorManager sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        sensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
        // 忘记注销
    }
}

// ✅ 解决方案
public class MainActivity extends AppCompatActivity {
    private SensorManager mSensorManager;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        mSensorManager.registerListener(this, sensor, SensorManager.SENSOR_DELAY_NORMAL);
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mSensorManager != null) {
            mSensorManager.unregisterListener(this);
        }
    }
}
```

### 内存泄漏排查工具

#### 1. LeakCanary

```java
// 集成 LeakCanary
dependencies {
    debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
}

// 自动检测内存泄漏
public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        if (LeakCanary.isInAnalyzerProcess(this)) {
            return;
        }
        LeakCanary.install(this);
    }
}
```

#### 2. Memory Profiler

```java
// 使用 Android Studio Memory Profiler
// 1. 打开 Memory Profiler
// 2. 记录内存快照
// 3. 分析内存分配
// 4. 查找内存泄漏
```

#### 3. MAT (Memory Analyzer Tool)

```java
// 使用 MAT 分析 Heap Dump
// 1. 生成 Heap Dump
// 2. 导入 MAT
// 3. 分析对象引用
// 4. 查找内存泄漏
```

---

## 性能问题排查

### 卡顿问题排查

#### 1. 主线程阻塞

```java
// ❌ 问题代码
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    // 主线程执行耗时操作
    loadDataFromDatabase(); // 阻塞主线程
    processLargeImage(); // 阻塞主线程
}

// ✅ 解决方案
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    
    // 异步执行耗时操作
    new Thread(() -> {
        loadDataFromDatabase();
        processLargeImage();
        
        runOnUiThread(() -> {
            updateUI();
        });
    }).start();
}
```

#### 2. 布局复杂

```java
// 排查方法
// 1. 使用 Layout Inspector 查看布局层级
// 2. 使用 Hierarchy Viewer 分析布局性能
// 3. 使用 Systrace 分析布局耗时

// 解决方案
// 1. 减少布局层级
// 2. 使用 ConstraintLayout
// 3. 使用 ViewStub
```

#### 3. 过度绘制

```java
// 排查方法
// 1. 启用 GPU 过度绘制检测
// 2. 查看过度绘制区域
// 3. 分析绘制次数

// 解决方案
// 1. 移除不必要的背景
// 2. 使用 clipRect
// 3. 优化自定义 View
```

### 性能排查工具

#### 1. Systrace

```bash
# 使用 Systrace 分析性能
python systrace.py -t 10 -o trace.html gfx view input
```

#### 2. CPU Profiler

```java
// 使用 Android Studio CPU Profiler
// 1. 打开 CPU Profiler
// 2. 记录 CPU 使用情况
// 3. 分析方法调用
// 4. 查找性能瓶颈
```

#### 3. GPU 渲染模式分析

```java
// 启用 GPU 渲染模式分析
// 设置 -> 开发者选项 -> GPU 渲染模式分析
// 查看每帧渲染时间
```

---

## 网络问题排查

### 网络问题常见场景

#### 1. 请求超时

```java
// 问题代码
OkHttpClient client = new OkHttpClient(); // 默认超时时间

// 排查方法
// 1. 检查网络连接
// 2. 检查服务器状态
// 3. 检查超时设置

// 解决方案
OkHttpClient client = new OkHttpClient.Builder()
    .connectTimeout(10, TimeUnit.SECONDS)
    .readTimeout(30, TimeUnit.SECONDS)
    .writeTimeout(30, TimeUnit.SECONDS)
    .build();
```

#### 2. 请求失败

```java
// 排查方法
// 1. 检查网络连接
// 2. 检查请求参数
// 3. 检查服务器响应
// 4. 查看错误日志

// 解决方案
public class NetworkInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        
        try {
            Response response = chain.proceed(request);
            // 记录请求成功
            logRequest(request, response);
            return response;
        } catch (IOException e) {
            // 记录请求失败
            logError(request, e);
            throw e;
        }
    }
}
```

#### 3. 数据解析错误

```java
// 问题代码
Gson gson = new Gson();
User user = gson.fromJson(json, User.class); // 解析失败

// 排查方法
// 1. 检查 JSON 格式
// 2. 检查数据模型
// 3. 添加异常处理

// 解决方案
try {
    User user = gson.fromJson(json, User.class);
} catch (JsonSyntaxException e) {
    Log.e("Network", "JSON 解析失败: " + json, e);
    // 处理解析错误
}
```

### 网络排查工具

#### 1. Network Profiler

```java
// 使用 Android Studio Network Profiler
// 1. 打开 Network Profiler
// 2. 记录网络请求
// 3. 分析请求时间
// 4. 查找网络问题
```

#### 2. Charles/Fiddler

```java
// 使用抓包工具
// 1. 配置代理
// 2. 抓取网络请求
// 3. 分析请求响应
// 4. 查找网络问题
```

#### 3. 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();
```

---

## 问题排查工具

### 1. 日志工具

```java
// 统一日志管理
public class Logger {
    private static final String TAG = "MyApp";
    private static final boolean DEBUG = BuildConfig.DEBUG;
    
    public static void d(String message) {
        if (DEBUG) {
            Log.d(TAG, message);
        }
    }
    
    public static void e(String message, Throwable throwable) {
        Log.e(TAG, message, throwable);
    }
}
```

### 2. 崩溃收集工具

```java
// 使用 Bugly 收集崩溃
dependencies {
    implementation 'com.tencent.bugly:crashreport:3.4.4'
}

// 初始化
Bugly.init(getApplicationContext(), "YOUR_APP_ID", false);
```

### 3. 性能监控工具

```java
// 自定义性能监控
public class PerformanceMonitor {
    public static void trackMethod(String methodName, Runnable runnable) {
        long startTime = System.currentTimeMillis();
        runnable.run();
        long endTime = System.currentTimeMillis();
        long duration = endTime - startTime;
        
        if (duration > 100) {
            Log.w("Performance", methodName + " 耗时: " + duration + "ms");
        }
    }
}

// 使用
PerformanceMonitor.trackMethod("loadData", () -> {
    loadData();
});
```

---

## 问题排查经验总结

### 排查流程

```java
// 1. 问题复现
复现问题();

// 2. 收集信息
收集日志();
收集崩溃信息();
收集性能数据();

// 3. 分析问题
分析日志();
分析堆栈();
分析性能数据();

// 4. 定位问题
定位问题代码();
定位问题原因();

// 5. 解决问题
制定解决方案();
实施解决方案();

// 6. 验证修复
验证修复效果();
持续监控();
```

### 排查技巧

1. **日志优先**：先查看日志，了解问题现象
2. **工具辅助**：使用专业工具分析问题
3. **逐步排查**：从简单到复杂，逐步缩小范围
4. **记录经验**：记录排查过程，积累经验
5. **团队协作**：与团队成员分享排查经验

---

## 面试常见问题

### Q1: 如何排查崩溃问题？

**答案：**
1. **收集崩溃信息**：使用崩溃收集工具（Bugly、Firebase）
2. **分析堆栈信息**：查看异常类型和堆栈跟踪
3. **定位问题代码**：根据堆栈信息定位问题代码
4. **分析原因**：分析崩溃原因
5. **修复问题**：修复代码并验证

### Q2: 如何排查内存泄漏？

**答案：**
1. **使用工具**：LeakCanary、Memory Profiler、MAT
2. **分析对象引用**：查看对象引用关系
3. **定位泄漏源**：找到持有引用的对象
4. **修复泄漏**：使用弱引用、及时释放等

### Q3: 如何排查性能问题？

**答案：**
1. **使用工具**：Systrace、CPU Profiler、GPU 渲染分析
2. **分析性能数据**：查看 CPU、内存、GPU 使用情况
3. **定位瓶颈**：找到性能瓶颈
4. **优化代码**：优化性能瓶颈

### Q4: 如何排查网络问题？

**答案：**
1. **使用工具**：Network Profiler、Charles、Stetho
2. **检查网络连接**：检查网络是否正常
3. **分析请求响应**：查看请求和响应数据
4. **定位问题**：找到网络问题原因

### Q5: 问题排查的流程？

**答案：**
1. 问题复现
2. 收集信息
3. 分析问题
4. 定位问题
5. 解决问题
6. 验证修复

---

## 总结

问题排查是 Android 开发中的重要技能，需要掌握各种排查工具和方法。通过系统化的排查流程，可以快速定位和解决问题，提升开发效率。

---

*最后更新：2024年*
