
本文介绍如何解决因预生成 10 年日期列表导致 activity 启动延迟 5–6 秒的问题,核心方案是避免主线程阻塞、取消冗余预计算,并采用按需计算日期的懒加载适配器设计。
本文介绍如何解决因预生成 10 年日期列表导致 activity 启动延迟 5–6 秒的问题,核心方案是避免主线程阻塞、取消冗余预计算,并采用按需计算日期的懒加载适配器设计。
在 MainActivity 中,当前逻辑在 onCreate() 内同步调用 getList(start, end) —— 该方法遍历约 3650 天(10 年),对每一天执行 Calendar 操作 + SimpleDateFormat 解析与格式化,全部运行在主线程。这不仅造成严重卡顿,还极易触发 ANR(Application Not Responding)。更关键的是:ViewPager2 并不需要一次性持有全部日期字符串——它只渲染当前页及相邻缓存页(默认 1 页前后),其余项完全可“按需生成”。
✅ 正确做法:彻底移除预生成列表,改用数学驱动的懒加载适配器
1. 将日期范围抽象为「起始日 + 总天数」
无需 ArrayList
Android文件存取与数据库编程知识,文件操作主要是读文件、写文件、读取静态文件等,同时还介绍了创建添加文件内容并保存,打开文件并显示内容;数据库编程方面主要介绍了SQLite数据库的使用、包括创建、删除、打开数据库、非查询SQL操作指令、查询SQL指令-游标Cursors等知识。
private final Calendar startDate = Calendar.getInstance(); private final int totalDays; // = 3650(或动态计算) // 初始化时设置:5年前 → 5年后 = 10年 = ~3650天 startDate.add(Calendar.YEAR, -5); totalDays = 3650;
2. 改写适配器:不依赖数据源数组,而是按索引实时计算日期
public class LazyDateAdapter extends RecyclerView.Adapter<lazydateadapter.viewholder> {
private final Calendar baseDate; // 起始日期(如 2019-01-01)
private final int totalItems;
public LazyDateAdapter(Calendar start, int days) {
this.baseDate = (Calendar) start.clone();
this.totalItems = days;
}
@Override
public int getItemCount() {
return totalItems;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_date, parent, false);
return new ViewHolder(v);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
// ✅ 关键:仅在绑定时计算当前 position 对应的日期字符串
Calendar target = (Calendar) baseDate.clone();
target.add(Calendar.DAY_OF_YEAR, position); // O(1) 运算
String formatted = formatDate(target);
holder.textView.setText(formatted);
}
private String formatDate(Calendar cal) {
// 推荐使用 ThreadLocal 或 AndroidX Core 的 SimpleDateFormat(线程安全)
return String.format(Locale.getDefault(), "%d/%s/%d",
cal.get(Calendar.DAY_OF_MONTH),
getMonthAbbr(cal.get(Calendar.MONTH)),
cal.get(Calendar.YEAR));
}
private String getMonthAbbr(int month) {
String[] abbr = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
return abbr[month];
}
static class ViewHolder extends RecyclerView.ViewHolder {
TextView textView;
ViewHolder(View itemView) {
super(itemView);
textView = itemView.findViewById(R.id.date_text);
}
}
}</lazydateadapter.viewholder>
3. 在 Activity 中轻量初始化(毫秒级)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewPager2 viewPager = findViewById(R.id.view_pager);
// ⚡ 极速初始化:无循环、无字符串生成
Calendar start = Calendar.getInstance();
start.add(Calendar.YEAR, -5);
int totalDays = 3650; // 或用 endDate.getTime() - startDate.getTime() / 86400000 精确计算
LazyDateAdapter adapter = new LazyDateAdapter(start, totalDays);
viewPager.setAdapter(adapter);
// 设置默认居中页:从 -5 年到 +5 年,中间即第 5 年(索引 = 5 * 365 ≈ 1825)
int centerPosition = (int) (totalDays * 0.5f);
viewPager.setCurrentItem(centerPosition, false);
}
⚠️ 注意事项与进阶建议
-
禁止在 onBindViewHolder 中创建 SimpleDateFormat 实例:它非线程安全且开销大。优先使用 String.format() 或 java.time.format.DateTimeFormatter(API 26+);若需兼容低版本,可用 ThreadLocal
封装。 - 避免 Calendar.clone() 频繁调用:虽比 new Calendar() 轻量,但仍有开销。可考虑用 long 时间戳代替 Calendar,通过 TimeUnit.DAYS.toMillis(position) 偏移计算毫秒值再格式化。
- 添加加载状态(可选):若首次滑动稍有延迟(极罕见),可在 ViewPager2 外层包裹 ProgressBar,onPageSelected 触发后隐藏。
- 内存优势:原方案占用约 3650 × 16B ≈ 58KB 字符串内存;新方案常驻内存
通过此重构,Activity 启动时间将从 5–6 秒降至 50ms 以内,同时显著降低内存占用与 GC 压力,真正实现高性能、可扩展的日期浏览体验。










