Android开发中的常见Bug汇总与解决方案
引言
作为一名Android开发者,在日常开发中难免会遇到各种奇怪的bug。有些bug一闪而过,有些则让人抓狂数小时。今天我将系统性地汇总Android开发中常见的bug类型,并提供解决方案,希望能帮助大家少走弯路。
一、闪退类Bug(Crash)
1.1 空指针异常(NullPointerException)
**出现频率:** ★★★★★
```java
// 常见场景1:未初始化直接使用
String text = null;
Log.d("TAG", text.length()); // Crash!
// 常见场景2:从Bundle/Intent中获取数据
String data = getIntent().getStringExtra("key");
if (data.equals("value")) { // 可能为null
// ...
}
// 解决方案:
// 1. 使用Objects.requireNonNull()进行显式检查
// 2. 使用Optional类(Java 8+)
// 3. 启用Kotlin的空安全特性
```
1.2 数组越界(ArrayIndexOutOfBoundsException)
```java
// 常见场景:
String[] array = new String[5];
String item = array[5]; // 索引0-4,5越界
// 解决方案:
// 1. 始终检查数组长度
// 2. 使用增强for循环
for (String item : array) {
// 安全遍历
}
```
1.3 内存不足(OutOfMemoryError)
**触发场景:**
-
加载大尺寸图片未压缩
-
Activity/Fragment内存泄漏
-
大量数据缓存未清理
**解决方案:**
```kotlin
// 1. 使用图片加载库(Glide/Picasso)的缓存机制
Glide.with(context)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(imageView)
// 2. 及时释放资源
override fun onDestroy() {
super.onDestroy()
// 取消网络请求
// 清空大对象引用
// 移除回调监听
}
```
未释放资源导致点击应用立马闪退,日志里面什么都没有报
具体代码是:
private final NLDeviceStream ds = new NLDevice(NLDeviceStream.DevClass.DEV_CDC);原因:以上代码只使用open打开io来使用,未在适当的时候进行释放导致的立马闪退。
应该在ondestroy释放
if (ds.isOpen()) { try { ds.close(); } catch (Exception e) { LogRyb.INSTANCE.write("【ReplenishActivity】ds的close异常"+e.getMessage()); } }
二、界面显示异常
2.1 布局错乱/重叠
**常见原因:**
-
不同屏幕尺寸适配问题
-
ConstraintLayout约束冲突
-
RelativeLayout依赖关系循环
**解决技巧:**
```xml
<!-- 使用百分比布局 -->
<androidx.percentlayout.widget.PercentFrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
app:layout_widthPercent="50%"
app:layout_heightPercent="50%" />
</androidx.percentlayout.widget.PercentFrameLayout>
<!-- 使用SafeArea处理刘海屏 -->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
```
2.2 RecyclerView显示异常
```java
// Bug 1: 数据更新但UI不刷新
adapter.notifyDataSetChanged(); // 正确方式
// Bug 2: 图片闪烁/重复
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
// 必须清除旧数据!
holder.imageView.setImageDrawable(null);
loadImage(position, holder.imageView);
}
// Bug 3: 滚动卡顿
// 解决方案:使用DiffUtil增量更新
public class MyDiffCallback extends DiffUtil.Callback {
// 实现比较逻辑
}
```
三、多线程并发问题
3.1 主线程阻塞(ANR)
```java
// 错误示例:在主线程执行耗时操作
new Thread(() -> {
// 网络请求
// 数据库操作
// 文件读写
}).start();
// 正确使用AsyncTask/Loader/RxJava/Coroutine
lifecycleScope.launch {
val data = withContext(Dispatchers.IO) {
// IO操作
}
// 更新UI(自动回到主线程)
updateUI(data)
}
```
3.2 线程安全问题
```kotlin
// 共享变量访问冲突
var count = 0
fun increment() {
// 多线程同时访问会导致数据错误
count++
}
// 解决方案:
// 1. 使用synchronized
@Synchronized
fun safeIncrement() {
count++
}
// 2. 使用Atomic变量
val atomicCount = AtomicInteger(0)
fun atomicIncrement() {
atomicCount.incrementAndGet()
}
```
四、数据存储与解析异常
4.1 数据库操作异常
```java
// 忘记关闭Cursor
Cursor cursor = db.query(...);
// 使用后必须关闭!
cursor.close();
// 使用try-with-resources(API 19+)
try (Cursor cursor = db.query(...)) {
// 自动关闭
}
// Room数据库中的常见问题
@Dao
interface UserDao {
// 返回LiveData或Flow可自动观察数据变化
@Query("SELECT * FROM user")
fun getAll(): LiveData<List<User>>
}
```
4.2 JSON解析异常
```kotlin
// 使用安全的JSON解析库
val json = JSONObject(jsonString)
// 可能抛出JSONException
// 推荐使用Gson/Moshi + try-catch
try {
val user = Gson().fromJson(jsonString, User::class.java)
} catch (e: JsonSyntaxException) {
// 处理解析失败
}
// 或使用Kotlin的扩展函数
fun String.safeToJson(): JsonObject? = try {
JsonParser.parseString(this).asJsonObject
} catch (e: Exception) {
null
}
```
五、网络请求相关Bug
5.1 网络权限缺失
```xml
<!-- 必须声明网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- Android 9+需要额外配置 -->
<application
android:usesCleartextTraffic="true" <!-- 允许HTTP -->
android:networkSecurityConfig="@xml/network_security_config">
```
5.2 请求回调内存泄漏
```java
// 错误:持有Activity引用
api.getData(new Callback() {
@Override
public void onSuccess(Data data) {
// 如果Activity已销毁,这里可能崩溃
activity.updateUI(data);
}
});
// 正确:使用弱引用或Lifecycle
api.getData(new WeakCallback<Activity>(activity) {
@Override
public void onSuccess(Activity ref, Data data) {
if (ref != null && !ref.isDestroyed()) {
ref.updateUI(data);
}
}
});
```
六、兼容性问题
6.1 版本兼容
```java
// 检查系统版本
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// 使用新API
requestPermissions(permissions, requestCode);
} else {
// 降级处理
}
// 使用兼容库替代
// 例如:使用AppCompatActivity代替Activity
// 使用Fragment代替android.app.Fragment
```
6.2 厂商定制系统Bug
**常见问题:**
-
后台限制(华为、小米、OPPO等)
-
通知栏权限
-
自启动管理
**解决方案:**
```kotlin
// 1. 添加到白名单引导
fun checkBackgroundRestriction(context: Context) {
if (Build.MANUFACTURER.equals("xiaomi", ignoreCase = true)) {
// 引导用户开启自启动
showXiaomiAutoStartGuide()
}
}
// 2. 使用WorkManager处理后台任务
val workRequest = PeriodicWorkRequestBuilder<MyWorker>(
15, TimeUnit.MINUTES
).build()
WorkManager.getInstance(context).enqueue(workRequest)
```
七、调试与预防技巧
7.1 日志记录策略
```kotlin
object AppLogger {
private const val TAG = "MyApp"
fun d(msg: String) {
if (BuildConfig.DEBUG) {
Log.d(TAG, msg)
} else {
// 生产环境:上传到服务器
uploadLogToServer(msg)
}
}
fun crash(throwable: Throwable) {
// 使用Firebase Crashlytics等崩溃统计
FirebaseCrashlytics.getInstance().recordException(throwable)
}
}
```
7.2 单元测试与UI测试
```kotlin
// 单元测试
@Test
fun `test login with valid credentials`() {
val viewModel = LoginViewModel()
viewModel.login("user", "pass123")
assertTrue(viewModel.isLoggedIn.value!!)
}
// UI测试(Espresso)
@RunWith(AndroidJUnit4::class)
class LoginActivityTest {
@Test
fun testLoginButton() {
onView(withId(R.id.login_btn))
.perform(click())
onView(withId(R.id.welcome_text))
.check(matches(isDisplayed()))
}
}
```
7.3 代码审查清单
-
\] 所有外部输入都经过验证
-
\] 网络请求有超时和重试机制
-
\] 敏感信息不硬编码
-
\] 权限申请有拒绝处理
八、常用工具推荐
-
**LeakCanary** - 内存泄漏检测
-
**Stetho** - Facebook调试工具
-
**BlockCanary** - UI卡顿检测
-
**Android Profiler** - 官方性能分析工具
-
**Lint** - 代码静态检查
-
**Firebase Crashlytics** - 崩溃统计
结语
Bug是程序员成长路上的必经之路。面对bug,我们要有:
-
**耐心** - 仔细分析日志和堆栈信息
-
**方法论** - 使用二分法、断点调试等技巧
-
**预防意识** - 编写健壮的代码,添加充分的测试
-
**学习心态** - 每个bug都是学习的机会
记住:没有不会写bug的程序员,只有不会调试的程序员。希望这份汇总能帮助大家在Android开发路上走得更加顺畅!
**持续更新中...** 如果你有其他常见的bug案例,欢迎在评论区分享!