【问题标题】:Updating TextView from Async Task which use custom program dialog从使用自定义程序对话框的异步任务更新 TextView
【发布时间】:2012-04-17 16:10:05
【问题描述】:

在我的一个应用程序中,我有一个需要执行一些后台任务的场景。为此,我正在使用异步任务。我也在使用自定义进度对话框。下面是自定义进度对话框的布局

<?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。

异步任务代码

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());
        }
    }

主活动

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("");

    }
}

【问题讨论】:

  • 您能编辑您的帖子并添加您的 Asynctask 的代码吗?此外,添加发生 NullPointerException 的行也会很有帮助。

标签: android android-asynctask textview


【解决方案1】:

如果我理解正确,您要设置文本的TextView 可以在xml 文件progressbar.xml 中找到(即R.layout.progressbar)。一旦设置了内容视图(使用setContentView()),就可以获得这个TextView。在您的代码中,您在此调用之前设置了它,而 mussharapp 的代码,他提前调用了它。即,他在不包含TextViewsetContentView(R.layout.accountsummary) 调用之后调用它。因此,变量txtView 将为NULL,您将获得NullPointerException

你应该做的是:

  • 在调用setContentView 之后,在onPreExecute 中设置变量txtView。
  • 基于 Paresh Mayani 的 explanation:使用 runOnUiThread 方法。

代码往下看:

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());
        }
    }

【讨论】:

    【解决方案2】:

    是的,因为您试图在 doInBackground() 方法中设置 TextView,这是不允许的,

    为什么不允许?因为只有一个线程在运行,即 UI 主线程,并且不允许从线程进程更新 UI。在此处阅读更多信息:Painless Threading

    所以有一个解决方案,如果你想在doInBackground()方法里面设置TextView,在runOnUiThread方法里面做UI更新操作。

    否则,建议在 onPostExecute() 方法中执行所有 UI 显示/更新相关操作,而不是 AsyncTask 类的 doInBackground() 方法。

    【讨论】:

      【解决方案3】:
      (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");**
              }
          }
      

      【讨论】:

      • 我试过了,但我仍然收到错误消息。我尝试更新的 TextView 也是自定义进度对话框布局的一部分
      【解决方案4】:

      解释是正确的:除了创建 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中给出的第二种类型!

      补充:为了使您的代码更加健壮,您应该在设置文本之前检查进度对话框是否仍然存在。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-01-15
        • 2014-12-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-05-10
        • 2012-04-17
        • 1970-01-01
        相关资源
        最近更新 更多