在对LoopViewPagerLayout Android无限轮播库的代码审查中,发现并修复了3个重要的bug,包括内存泄漏、性能问题和逻辑错误。以下是详细的分析和修复说明。
位置: library/src/main/java/com/github/why168/LoopViewPagerLayout.java 第59-71行
严重程度: 高危 - 可能导致内存泄漏
问题详情: 原代码中Handler是以非静态内部类的形式定义的:
private Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void dispatchMessage(Message msg) {
// ... 处理逻辑
}
};问题原因:
- 内存泄漏风险: 非静态内部类持有外部类的隐式引用,当Activity/Fragment被销毁时,如果Handler中还有未处理的消息,会阻止外部类被垃圾回收
- 线程安全问题:
removeCallbacksAndMessages(MESSAGE_LOOP)参数使用错误,应该使用null来移除所有消息 - 空指针风险: 没有对handler进行null检查
-
使用静态内部类 + WeakReference模式:
private static class LoopHandler extends Handler { private final WeakReference<LoopViewPagerLayout> weakReference; public LoopHandler(LoopViewPagerLayout layout) { super(Looper.getMainLooper()); this.weakReference = new WeakReference<>(layout); } @Override public void handleMessage(Message msg) { LoopViewPagerLayout layout = weakReference.get(); if (layout != null && msg.what == MESSAGE_LOOP) { // 处理逻辑 } } }
-
修复消息移除逻辑:
public void stopLoop() { if (handler != null) { handler.removeCallbacksAndMessages(null); // 修复参数 } }
-
添加null检查: 在startLoop()和stopLoop()方法中添加handler的null检查
- ✅ 解决内存泄漏问题
- ✅ 提高线程安全性
- ✅ 防止空指针异常
位置: library/src/main/java/com/github/why168/adapter/LoopPagerAdapterWrapper.java 第36行
严重程度: 中危 - 性能问题和资源浪费
问题详情:
@Override
public int getCount() {
return Short.MAX_VALUE; // 32767
}问题原因:
- 资源浪费: 创建过多虚拟页面,占用不必要的内存
- 性能影响: 大量的ViewPager页面可能影响滑动性能
- 整数溢出风险: 在某些计算中可能导致意外行为
-
使用合理的循环数量:
@Override public int getCount() { // 使用更合理的数值实现无限循环,避免性能问题 // 1000 * bannerInfos.size() 足以提供良好的循环体验 return bannerInfos == null || bannerInfos.size() == 0 ? 0 : 1000 * bannerInfos.size(); }
-
更新相关逻辑: 修改LoopViewPagerLayout中所有使用Short.MAX_VALUE的地方,使用动态计算的总数量
-
优化初始位置计算:
int totalCount = loopPagerAdapterWrapper.getCount(); int index = totalCount / 2 - (totalCount / 2) % bannerInfos.size();
- ✅ 显著减少内存占用
- ✅ 提升滑动性能
- ✅ 保持无限循环功能
位置: library/src/main/java/com/github/why168/LoopViewPagerLayout.java ViewPageChangeListener类
严重程度: 中危 - 运行时崩溃风险
问题详情:
float length = ((position % bannerInfos.size()) + positionOffset) / (bannerInfos.size() - 1);问题原因:
- 除零错误: 当
bannerInfos.size() == 1时,分母为0,导致除零异常 - 边界检查缺失: 没有对计算结果进行边界检查
- 空指针风险: 没有检查bannerInfos是否为null
-
添加边界检查:
if (bannerInfos != null && bannerInfos.size() > 0) { // 防止除零错误:当只有一张图片时,直接返回 if (bannerInfos.size() == 1) { animIndicator.setTranslationX(0); return; } // ... 正常计算逻辑 }
-
改进计算逻辑:
int currentIndex = position % bannerInfos.size(); float length = (currentIndex + positionOffset) / (bannerInfos.size() - 1); // 边界检查:防止指示器滑出范围 if (length > 1.0f) { length = 1.0f; } else if (length < 0.0f) { length = 0.0f; }
-
优化单图片处理: 专门处理只有一张图片的情况
- ✅ 防止除零异常
- ✅ 改善边界情况处理
- ✅ 提高代码健壮性
- 修复文件数量: 2个
- 修复代码行数: 约50行
- 安全性提升: 解决了内存泄漏和崩溃风险
- 性能优化: 减少内存占用,提升滑动性能
- 代码审查: 建议定期进行代码审查,特别关注内存管理和边界条件
- 单元测试: 增加边界情况的单元测试,如单图片、空数据等场景
- 性能监控: 建议添加性能监控,及时发现类似问题
所有修复都保持了原有API的兼容性,不会影响现有的使用方式。修复后的代码更加稳定、安全且高效。