14 KiB
14 KiB
问题排查经验
目录
崩溃问题排查
常见崩溃类型
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. 验证修复
验证修复效果();
持续监控();
排查技巧
- 日志优先:先查看日志,了解问题现象
- 工具辅助:使用专业工具分析问题
- 逐步排查:从简单到复杂,逐步缩小范围
- 记录经验:记录排查过程,积累经验
- 团队协作:与团队成员分享排查经验
面试常见问题
Q1: 如何排查崩溃问题?
答案:
- 收集崩溃信息:使用崩溃收集工具(Bugly、Firebase)
- 分析堆栈信息:查看异常类型和堆栈跟踪
- 定位问题代码:根据堆栈信息定位问题代码
- 分析原因:分析崩溃原因
- 修复问题:修复代码并验证
Q2: 如何排查内存泄漏?
答案:
- 使用工具:LeakCanary、Memory Profiler、MAT
- 分析对象引用:查看对象引用关系
- 定位泄漏源:找到持有引用的对象
- 修复泄漏:使用弱引用、及时释放等
Q3: 如何排查性能问题?
答案:
- 使用工具:Systrace、CPU Profiler、GPU 渲染分析
- 分析性能数据:查看 CPU、内存、GPU 使用情况
- 定位瓶颈:找到性能瓶颈
- 优化代码:优化性能瓶颈
Q4: 如何排查网络问题?
答案:
- 使用工具:Network Profiler、Charles、Stetho
- 检查网络连接:检查网络是否正常
- 分析请求响应:查看请求和响应数据
- 定位问题:找到网络问题原因
Q5: 问题排查的流程?
答案:
- 问题复现
- 收集信息
- 分析问题
- 定位问题
- 解决问题
- 验证修复
总结
问题排查是 Android 开发中的重要技能,需要掌握各种排查工具和方法。通过系统化的排查流程,可以快速定位和解决问题,提升开发效率。
最后更新:2024年