在我的一个应用程序中,我有一个场景,我需要做一些后台任务。为此,我使用异步任务。我也在使用自定义进度对话框。以下是自定义进度对话框的布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical|center_horizontal"
android:orientation="vertical" >
<ProgressBar
android:layout_width="60dp"
android:layout_height="60dp"
android:indeterminateDrawable="@drawable/progressloader"
android:layout_gravity="center"/>
<TextView
android:id="@+id/progressMessage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="18sp"
android:text="Please wait...." />
</LinearLayout>
一切正常但当我尝试将文本设置为TextView时,我得到java NullPointerException。
AsyncTask代码
private class InitialSetup extends AsyncTask<String, Integer, Long> {
ProgressDialog dialog = new ProgressDialog(getParent(),R.style.progressdialog);
@Override
protected void onPreExecute() {
dialog.show();
dialog.setContentView(R.layout.progressbar);
}
@Override
protected Long doInBackground(String... urls) {
// txtView.setText("Testing"); here I am getting the error
fetchDetails();
return 0;
}
@Override
protected void onPostExecute(Long result) {
if (this.dialog.isShowing()) {
this.dialog.dismiss();
}
populateUI(getApplicationContext());
}
}
MainActivity
public class SummaryActivity extends Activity {
final TextView txtView = (TextView)findbyid(R.id.progressMessage);
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.accountsummary);
new InitialSetup().execute("");
}
}
答案 0 :(得分:13)
如果我理解正确,您可以在xml文件progressbar.xml(即TextView
)中找到要设置文本的R.layout.progressbar
。设置内容视图后,可以获取此TextView
(使用setContentView()
)。在您的代码中,您在此调用之前设置它和mussharapp的代码,他正在提前调用它。也就是说,他在setContentView(R.layout.accountsummary)
来电后调用它,但不包含TextView
。因此,变量txtView
将为NULL,您将获得NullPointerException
。
您应该做的是以下内容:
onPreExecute
后,在setContentView
中设置变量txtView。对于下面的代码:
private class InitialSetup extends AsyncTask<String, Integer, Long> {
ProgressDialog dialog = new ProgressDialog(getParent(),R.style.progressdialog);
// The variable is moved here, we only need it here while displaying the
// progress dialog.
TextView txtView;
@Override
protected void onPreExecute() {
dialog.show();
dialog.setContentView(R.layout.progressbar);
// Set the variable txtView here, after setContentView on the dialog
// has been called! use dialog.findViewById().
txtView = dialog.findViewById(R.id.progressMessage);
}
@Override
protected Long doInBackground(String... urls) {
// Already suggested by Paresh Mayani:
// Use the runOnUiThread method.
// See his explanation.
runOnUiThread(new Runnable() {
@Override
public void run() {
txtView.setText("Testing");
}
});
fetchDetails();
return 0;
}
@Override
protected void onPostExecute(Long result) {
if (this.dialog.isShowing()) {
this.dialog.dismiss();
}
populateUI(getApplicationContext());
}
}
答案 1 :(得分:12)
是的,因为你试图在doInBackground()方法中设置TextView,这是不允许的,
为什么不允许?因为只有一个Thread运行是UI主线程,并且不允许从线程进程更新UI。在此处阅读更多信息:Painless Threading
因此,如果要在doInBackground()方法中设置TextView,在runOnUiThread方法中执行UI更新操作,则有一个解决方案。
否则,建议在onPostExecute()方法中执行所有与UI显示/更新相关的操作,而不是AsyncTask类的doInBackground()方法。
答案 2 :(得分:1)
(TextView)findViewByid(R.id.progressMessage);
只应在命令setContentView()之后执行。
TextView txtView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.accountsummary);
**txtView = (TextView)findbyid(R.id.progressMessage);**
new InitialSetup().execute("");
}
此外,您只能更改主UI线程中的UI元素。 doInBackground()不在主UI线程中。在onPostExecute中进行UI更改
public class InitialSetup extends AsyncTask<String, Integer, Long> {
private Activity activity;
ProgressDialog progressDialog;
public InitialSetup(Activity activity) {
this.activity = activity;
}
@Override
protected void onPreExecute() {
progressDialog = new ProgressDialog(activity);
progressDialog.setMessage("Starting task....");
progressDialog.show();
}
@Override
protected Long doInBackground(String... urls) {
// do something
//
return 0;
}
@Override
protected void onPostExecute(Long result) {
progressDialog.dismiss();
//Perform all UI changes here
**textView.setText("Text#2");**
}
}
答案 3 :(得分:1)
解释是正确的:除了创建UI的线程之外,您不要在任何线程中进行UI更改。但AsyncTask有一个名为
的方法onProgressUpdate()
总是会在UI线程中运行。因此,基于dennisg的修改,您的代码应该如下所示:
private class InitialSetup extends AsyncTask<String, String, Long> {
ProgressDialog dialog = new ProgressDialog(getParent(),R.style.progressdialog);
// The variable is moved here, we only need it here while displaying the
// progress dialog.
TextView txtView;
@Override
protected void onPreExecute() {
dialog.show();
dialog.setContentView(R.layout.progressbar);
// Set the variable txtView here, after setContentView on the dialog
// has been called! use dialog.findViewById().
txtView = dialog.findViewById(R.id.progressMessage);
}
@Override
protected Long doInBackground(String... urls) {
publishProgress("Testing");
fetchDetails();
return 0;
}
@Override
protected void onPostExecute(Long result) {
if (this.dialog.isShowing()) {
this.dialog.dismiss();
}
populateUI(getApplicationContext());
}
@Override
protected void onProgressUpdate(String... update) {
if (update.length > 0)
txtView.setText(update[0]);
}
}
请注意,onProgressUpdate的参数类型是AsyncTask中给出的第二种类型!
额外:为了使您的代码更加强大,您应该在设置文本之前检查进度对话框是否仍然存在。