应用启动或重建时,如何正确执行数据库数据转换长任务?
解决方案
核心思路是不要阻塞主线程(否则会触发ANR应用无响应),而是把所有依赖转换后数据的初始化逻辑,放到数据转换完成后的回调中执行,同时在后台线程完成转换,UI线程只负责显示/关闭加载对话框。
方法一:使用Handler切换线程
- 定义Handler用于后台线程完成后通知UI线程:
private Handler mMainHandler = new Handler(Looper.getMainLooper());
- 修改
convertDataInDB方法,在转换完成后关闭对话框并执行后续逻辑:
public void convertDataInDB() { // 创建并显示不可取消的加载对话框 AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setCancelable(false); builder.setView(R.layout.progress_dialog); final AlertDialog dialog = builder.create(); dialog.show(); // 后台线程执行数据转换 new Thread(() -> { db.convertData(); // 转换完成后切换回UI线程 mMainHandler.post(() -> { dialog.dismiss(); // 执行所有依赖新数据的初始化操作 initAfterConversion(); }); }).start(); }
- 重构
onCreate,将原有的后续逻辑移到initAfterConversion方法:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 初始化数据库等基础组件 initDB(); // 启动数据转换 convertDataInDB(); // 不要在这里写需要操作新数据的代码,全部移到initAfterConversion } private void initAfterConversion() { // 示例:读取转换后的数据库数据、初始化UI组件 loadConvertedData(); setupAppUI(); }
方法二:使用AsyncTask(适合Java场景)
AsyncTask封装了线程切换逻辑,代码更简洁:
private class DataConvertTask extends AsyncTask<Void, Void, Void> { private AlertDialog loadingDialog; @Override protected void onPreExecute() { super.onPreExecute(); // 显示加载对话框 AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setCancelable(false); builder.setView(R.layout.progress_dialog); loadingDialog = builder.create(); loadingDialog.show(); } @Override protected Void doInBackground(Void... voids) { // 后台执行数据转换 db.convertData(); return null; } @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); // 关闭对话框并执行后续逻辑 loadingDialog.dismiss(); initAfterConversion(); } }
在onCreate中启动任务:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initDB(); // 启动转换任务 new DataConvertTask().execute(); }
额外优化:避免重复转换
如果转换只需在配置变更后执行一次,可用SharedPreferences记录状态,避免每次启动都重复转换:
private boolean needDataConversion() { SharedPreferences sp = getSharedPreferences("app_settings", MODE_PRIVATE); // 读取当前数字格式和已转换的格式标记 String currentFormat = sp.getString("number_format", "default"); String convertedFormat = sp.getString("converted_format", ""); return !currentFormat.equals(convertedFormat); } // 转换完成后更新标记 private void markConversionComplete() { SharedPreferences sp = getSharedPreferences("app_settings", MODE_PRIVATE); String currentFormat = sp.getString("number_format", "default"); sp.edit().putString("converted_format", currentFormat).apply(); }
在后台转换逻辑中加入判断:
@Override protected Void doInBackground(Void... voids) { if (needDataConversion()) { db.convertData(); markConversionComplete(); } return null; }
内容的提问来源于stack exchange,提问作者ViH
相关产品推荐
相关产品推荐

