自定义ProgressDialog组件空指针问题及周期性更新方法咨询
自定义ProgressDialog更新时的NullPointerException解决及周期性更新实现
问题背景
原生ProgressDialog无法满足布局需求,需要实现一个垂直堆叠的自定义ProgressDialog:顶部居中标题,中间居中Spinner,底部显示可实时更新的完成百分比文本。但调用updateDialog方法更新标题和消息时触发NullPointerException。
错误原因
DialogFragment的show()方法是异步执行的,调用show()后立刻调用updateDialog时,onCreateDialog还未执行完毕,progressDialogTitle和progressDialogMessage这两个TextView实例还未初始化,此时调用setText()就会触发空指针异常。
解决方案
1. 缓存更新内容,延迟初始化后设置
在自定义DialogFragment中添加缓存变量,当控件未初始化时先缓存要更新的内容,等onCreateDialog初始化控件后再应用这些内容;同时在更新方法中增加非空判断,确保安全调用。
2. 确保UI更新在主线程
因为更新操作可能来自后台线程,需要确保setText()在UI线程执行,避免线程安全问题。
3. 周期性更新实现
使用Handler结合postDelayed来实现周期性的内容更新,避免阻塞主线程。
修改后的代码
ProgressDialog.java
public class ProgressDialog extends AppCompatDialogFragment { private TextView progressDialogMessage; private TextView progressDialogTitle; private AlertDialog aldialogInst; // 缓存待更新的标题和消息 private String pendingTitle; private String pendingMessage; private String title = ""; private String message = ""; public ProgressDialog() { } public ProgressDialog(String title, String message) { this.title = title; this.message = message; this.pendingTitle = title; this.pendingMessage = message; } @Override public AlertDialog onCreateDialog(@Nullable Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.CustomAlertDialog); LayoutInflater inflater = getActivity().getLayoutInflater(); View view = inflater.inflate(R.layout.activity_progress_dialog, null); progressDialogMessage = view.findViewById(R.id.progressDialogMessage); progressDialogTitle = view.findViewById(R.id.progressDialogTitle); builder.setView(view).setTitle(null); // 初始化时先设置缓存的内容 if (pendingTitle != null) { progressDialogTitle.setText(pendingTitle); } if (pendingMessage != null) { progressDialogMessage.setText(pendingMessage); } aldialogInst = builder.create(); return aldialogInst; } public void updateDialog(String title, @Nullable String message) { // 确保在UI线程执行更新 if (getActivity() != null) { getActivity().runOnUiThread(() -> { if (progressDialogTitle != null) { progressDialogTitle.setText(title); } else { pendingTitle = title; } if (progressDialogMessage != null) { progressDialogMessage.setText(message); } else { pendingMessage = message; } }); } else { // 上下文为空时缓存内容 pendingTitle = title; pendingMessage = message; } } public boolean isReady() { return aldialogInst != null && progressDialogTitle != null && progressDialogMessage != null; } public AlertDialog getAlertDialog() { return aldialogInst; } }
MainActivity.java(添加周期性更新示例)
public class MainActivity extends AppCompatActivity { private ProgressDialog progressDialog; private Handler updateHandler = new Handler(); private int currentProgress = 0; @RequiresApi(api = Build.VERSION_CODES.TIRAMISU) @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); progressDialog = new ProgressDialog("Downloading Provenance", "0%"); progressDialog.show(getSupportFragmentManager(), "progdialog"); // 启动周期性更新 startPeriodicUpdate(); } private void startPeriodicUpdate() { updateHandler.postDelayed(new Runnable() { @Override public void run() { currentProgress += 10; if (currentProgress <= 100) { progressDialog.updateDialog("Downloading Provenance", currentProgress + "%"); // 继续下一次更新 updateHandler.postDelayed(this, 1000); } else { // 更新完成后关闭对话框 progressDialog.dismiss(); } } }, 1000); } @Override protected void onDestroy() { super.onDestroy(); // 移除未执行的回调,避免内存泄漏 updateHandler.removeCallbacksAndMessages(null); } }
关键说明
- 缓存机制:当控件未初始化时,先把要更新的内容存在
pendingTitle和pendingMessage中,等onCreateDialog初始化控件后自动应用。 - UI线程保障:使用
getActivity().runOnUiThread()确保所有UI操作都在主线程执行,避免跨线程操作UI的问题。 - 周期性更新:通过
Handler.postDelayed()实现每秒更新一次进度,更新完成后自动关闭对话框,同时在onDestroy中移除回调防止内存泄漏。
内容的提问来源于stack exchange,提问作者thotheolh
相关产品推荐
相关产品推荐

