Android中ViewPager加载10年日期导致Activity加载慢的优化咨询
优化方案
1. 移除冗余的日期解析逻辑,直接格式化Calendar
原代码中getDate方法先拼接日期字符串,再解析成Date对象,最后重新格式化,这是完全多余的操作,会额外消耗大量CPU资源。直接对Calendar的时间进行格式化即可:
// 用ThreadLocal保证SimpleDateFormat的线程安全 private ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = ThreadLocal.withInitial( () -> new SimpleDateFormat("dd/MMM/yyyy", Locale.getDefault()) ); public String getDate(Calendar cld) { return dateFormatThreadLocal.get().format(cld.getTime()); }
2. 取消预生成全量日期,改为按需计算
10年跨度的日期约有3650条,预生成整个列表会阻塞主线程、占用大量内存。利用ViewPager2的按需加载特性,我们只需保存起始日期和总天数,在需要时根据position计算对应日期:
修改Activity核心代码
public class MainActivity extends AppCompatActivity { ViewPager2 myViewPager2; ViewPagerDateAdapter viewPagerDateAdapter; private Calendar startCalendar; private int totalDays; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myViewPager2 = findViewById(R.id.view_pager); // 计算起始日期(当前日期往前推5年) Calendar c = Calendar.getInstance(); c.add(Calendar.YEAR, -5); startCalendar = (Calendar) c.clone(); // 计算结束日期(起始日期往后推10年) c.add(Calendar.YEAR, 10); Calendar endCalendar = (Calendar) c.clone(); // 计算总天数 totalDays = (int) ((endCalendar.getTimeInMillis() - startCalendar.getTimeInMillis()) / (1000 * 60 * 60 * 24)) + 1; // 初始化Adapter,传入起始日期和总天数 viewPagerDateAdapter = new ViewPagerDateAdapter(this, startCalendar, totalDays); myViewPager2.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL); myViewPager2.setAdapter(viewPagerDateAdapter); // 设置当前页为中间位置(对应当前日期) int currentPosition = (totalDays + 1) / 2 - 1; myViewPager2.setCurrentItem(currentPosition); // 页面监听逻辑保留不变... } }
修改Adapter实现按需计算
让Adapter直接根据position推导日期,无需依赖全量列表:
public class ViewPagerDateAdapter extends RecyclerView.Adapter<ViewPagerDateAdapter.ViewHolder> { private Context context; private Calendar startCalendar; private int totalDays; private ThreadLocal<SimpleDateFormat> dateFormatThreadLocal; public ViewPagerDateAdapter(Context context, Calendar startCalendar, int totalDays) { this.context = context; this.startCalendar = startCalendar; this.totalDays = totalDays; this.dateFormatThreadLocal = ThreadLocal.withInitial( () -> new SimpleDateFormat("dd/MMM/yyyy", Locale.getDefault()) ); } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(context).inflate(R.layout.item_date, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { // 根据position计算对应日期 Calendar tempCalendar = (Calendar) startCalendar.clone(); tempCalendar.add(Calendar.DAY_OF_MONTH, position); String dateStr = dateFormatThreadLocal.get().format(tempCalendar.getTime()); holder.dateTextView.setText(dateStr); } @Override public int getItemCount() { return totalDays; } public static class ViewHolder extends RecyclerView.ViewHolder { TextView dateTextView; public ViewHolder(@NonNull View itemView) { super(itemView); dateTextView = itemView.findViewById(R.id.tv_date); } } }
3. 可选:子线程处理额外数据加载
如果每个日期页需要加载关联数据,可在onBindViewHolder中用协程或AsyncTask在子线程处理,避免阻塞主线程:
// 需引入Coroutine依赖 @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { CoroutineScope(Dispatchers.IO).launch { // 计算日期或加载关联数据 Calendar tempCalendar = (Calendar) startCalendar.clone(); tempCalendar.add(Calendar.DAY_OF_MONTH, position); String dateStr = dateFormatThreadLocal.get().format(tempCalendar.getTime()); // 切回主线程更新UI withContext(Dispatchers.Main) { holder.dateTextView.setText(dateStr); } } }
优化效果
- 启动时间从5-6秒压缩至几百毫秒,彻底消除预生成全量数据的主线程阻塞
- 内存占用大幅降低,无需保存3000+条日期字符串
- 滑动时按需计算日期,页面切换更流畅
内容的提问来源于stack exchange,提问作者Deepak Bhatt
相关产品推荐
相关产品推荐

