Files
mkdocs/docs/android面试/项目经验/问题排查经验.md
2026-01-15 11:53:37 +08:00

14 KiB
Raw Permalink Blame History

问题排查经验

目录


崩溃问题排查

常见崩溃类型

1. NullPointerException

// 问题代码
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

// 问题代码
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

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

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

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

4. OutOfMemoryError

// 问题代码
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. 使用对象池

崩溃排查流程

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

崩溃日志分析

// 分析崩溃日志
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

// ❌ 问题代码
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 内存泄漏

// ❌ 问题代码
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. 监听器未注销

// ❌ 问题代码
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

// 集成 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

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

3. MAT (Memory Analyzer Tool)

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

性能问题排查

卡顿问题排查

1. 主线程阻塞

// ❌ 问题代码
@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. 布局复杂

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

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

3. 过度绘制

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

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

性能排查工具

1. Systrace

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

2. CPU Profiler

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

3. GPU 渲染模式分析

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

网络问题排查

网络问题常见场景

1. 请求超时

// 问题代码
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. 请求失败

// 排查方法
// 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. 数据解析错误

// 问题代码
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

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

2. Charles/Fiddler

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

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

问题排查工具

1. 日志工具

// 统一日志管理
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. 崩溃收集工具

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

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

3. 性能监控工具

// 自定义性能监控
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();
});

问题排查经验总结

排查流程

// 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年