【问题标题】:Android AsyncTask ProgressDialog rotation different configurationsAndroid AsyncTask ProgressDialog 轮换不同配置
【发布时间】:2012-09-12 14:37:25
【问题描述】:

我已经设法在屏幕旋转期间使用了带有不确定进度条的 Asynctask。 Asynctask 只启动一次,进度条按我的意愿旋转恢复。

我有不同的纵向布局和布局方向。布局包括一个按钮和一个文本视图。 layout-land中textview的大小和文本颜色不同。方向是横向的。

问题是当我在 asynctask 运行时旋转屏幕时,它无法更新 onPostExecute 方法中的文本视图。当我旋转时,它会使用 layout-land 文件重新创建活动。但是为什么我不能更新我的 Textview?

布局\activity_main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:orientation="vertical">

    <Button 
        android:text="Start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="startClicked"
        />
    <TextView
        android:id="@+id/hello"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world"
        tools:context=".MainActivity" />

</LinearLayout>

layout-land\activity_main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    android:orientation="horizontal">

    <Button 
        android:text="Start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="startClicked"
        />
    <TextView
        android:textSize="36dp"
        android:textColor="#ff0000"
        android:id="@+id/hello"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world"
        tools:context=".MainActivity" />

</LinearLayout>

MainActivity.java:

package com.example.asynctaskconfig;

import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

    public class MainActivity extends Activity {
        static String data;
        static ProgressDialog pd;
        MyAsyncTask task;
        TextView tv;

        @Override
        public void onCreate(Bundle icicle) {
            super.onCreate(icicle);
            setContentView(R.layout.activity_main);

            tv = (TextView) findViewById(R.id.hello);

            if (getLastNonConfigurationInstance() != null) {
                task = (MyAsyncTask) getLastNonConfigurationInstance();
                if (task != null) {
                    if (!(task.getStatus().equals(AsyncTask.Status.FINISHED))) {
                        showProgressDialog();
                    }
                }
            }
        }

        @Override
        public Object onRetainNonConfigurationInstance() {
            if (pd != null)
                pd.dismiss();
            if (task != null)
                return (task);
            return super.onRetainNonConfigurationInstance();
        }


        private void showProgressDialog() {
            if (pd == null || !pd.isShowing()) {
                pd = new ProgressDialog(MainActivity.this);
                pd.setIndeterminate(true);
                pd.setTitle("DOING..");
                pd.show();
            }
        }

        private void dismissProgressDialog() {
            if (pd != null && pd.isShowing())
                pd.dismiss();
        }

        public class MyAsyncTask extends AsyncTask<String, Void, Boolean> {
            @Override
            protected void onPreExecute() {
                showProgressDialog();
            }

            @Override
            protected Boolean doInBackground(String... args) {
                try {
                    Thread.sleep(5000);
                    data = "result from ws";
                } catch (Exception e) {
                    return true;
                }
                return true;
            }

            protected void onPostExecute(Boolean result) {
                if (result) {
                    dismissProgressDialog();
                    updateUI();
                }
            }
        }

        private void updateUI() {
            tv.setText(data == null ? "null" : data);
        }

        public void startClicked(View target) {
            task = new MyAsyncTask();
            task.execute("start");
        }
    }

【问题讨论】:

    标签: android android-asynctask progressdialog screen-rotation


    【解决方案1】:

    我所做的如下:

    1-android:freezesText="true" 添加到我所有的TextViews。这使 TextViews 能够在配置更改时保存它们的状态。

    2- 将您的 AsyncTask 设为 static inner class.

    3- 修改AsyncTask 以保留对它所在的Activity 的引用。因此AsyncTask 可以通过该引用访问Activity 的UI 小部件。

    4- 在这里,在屏幕旋转期间保持有效的活动引用很重要。因此,重写onDestroy 方法并解除Activity 与AsyncTask 的绑定。因此,任务不会保留旧的(死亡的)活动。

    5-onRetainNonConfigurationInstance中,如果任务仍在运行,则将其活动引用更新为当前活动,以便成功绑定到新活动。

    6-最后,在onPostExecuteMethod,通过活动引用访问Activity的UI元素。

    完整的工作解决方案:

    布局\activity_main.xml:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent" 
        android:orientation="vertical">
    
        <Button 
            android:text="Start"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="startClicked"
            />
        <TextView
            android:freezesText="true"
            android:id="@+id/hello"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/hello_world"
            tools:context=".MainActivity" />
    </LinearLayout>
    

    layout-land\activity_main.xml:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent" 
        android:orientation="horizontal">
    
        <Button 
            android:text="Start"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:onClick="startClicked"
            />
        <TextView
            android:freezesText="true"
            android:textSize="36dp"
            android:textColor="#ff0000"
            android:id="@+id/hello"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/hello_world"
            tools:context=".MainActivity" />
    </LinearLayout>
    

    MainActivity.java:

    package com.example.asynctaskconfig;
    
    import android.app.Activity;
    import android.app.ProgressDialog;
    import android.os.AsyncTask;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.TextView;
    
    public class MainActivity extends Activity {
        static ProgressDialog pd;
        MyAsyncTask task;
        TextView tv;
    
        @Override
        public void onCreate(Bundle icicle) {
            super.onCreate(icicle);
            setContentView(R.layout.activity_main);
    
            tv = (TextView) findViewById(R.id.hello);
    
            if (getLastNonConfigurationInstance() != null) {
                task = (MyAsyncTask) getLastNonConfigurationInstance();
                if (task != null) {
                    task.activity = this;
                    if (!(task.getStatus().equals(AsyncTask.Status.FINISHED))) {
                        showProgressDialog();
                    }
                }
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            if (task != null) {
                task.activity = null;
            }
        }
    
        @Override
        public Object onRetainNonConfigurationInstance() {
            if (pd != null)
                pd.dismiss();
            if (task != null)
                return (task);
            return super.onRetainNonConfigurationInstance();
        }
    
        private void showProgressDialog() {
            if (pd == null || !pd.isShowing()) {
                pd = new ProgressDialog(MainActivity.this);
                pd.setIndeterminate(true);
                pd.setTitle("DOING..");
                pd.show();
            }
        }
    
        private void dismissProgressDialog() {
            if (pd != null && pd.isShowing())
                pd.dismiss();
        }
    
        static class MyAsyncTask extends AsyncTask<String, Void, String> {
            MainActivity activity;
    
            public MyAsyncTask(MainActivity activity) {
                this.activity = activity;
            }
    
            @Override
            protected void onPreExecute() {
                activity.showProgressDialog();
            }
            @Override
            protected String doInBackground(String... args) {
                try {
                    Thread.sleep(8000);
                    return "data from ws";
                } catch (Exception e) {
                    return "exception";
                }
            }
    
            protected void onPostExecute(String result) {
                activity.dismissProgressDialog();
                activity.tv.setText(result == null ? "null" : result);
            }
        }
    
        public void startClicked(View target) {
            task = new MyAsyncTask(this);
            task.execute("start");
        }
    }
    

    【讨论】:

      【解决方案2】:

      您的问题本质上是您尝试更改的 TextView 不再是屏幕上可见的 TextView。轮换导致 Android 丢弃旧的 Activity 并构建一个新的 - 完成 xml 文件中的所有视图。因此,您的 TextView 'tv' 仍然是旧活动的一部分,更改将无济于事。

      现在获得所需行为的最简单方法是再次查找 textview,即在 updateUI 方法中再次使用“findViewById”,您应该没问题!

      【讨论】:

      • findViewById 仍然会找到旧 Activity 的 TextView,因为它的视图在其 setContentView() 中被夸大了。
      【解决方案3】:

      在这种情况下,您可能不希望在方向更改时重新创建活动。您可以通过将其添加到清单中的活动来处理活动中的方向更改:

      android:configChanges="keyboardHidden|orientation"
      

      然后覆盖 onConfigurationChanged:

      @Override
      public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        setContentView(R.layout.activity_main);
        tv = (TextView) findViewById(R.id.hello);
      }
      

      【讨论】:

      • 不,我希望每次都重新创建它。我正在为此目的使用 layout-land\activity_main.xml。
      • 是的,但是调用 setContentView 时正确的视图会膨胀
      • 不是吗?您是否确认没有重新创建活动并且 tv 获得了新值?
      • 那就是问题所在。你可能会在这个帖子中找到线索:http://stackoverflow.com/questions/6457659/android-onconfigurationchanged-not-being-called
      • 好的。这将使用文本“ws 的结果”更新 textview,但是当我将其旋转回来时,布局将恢复到 textview 内容为“hello world”的原始状态。当我们再次从横向回到纵向时该怎么办。
      【解决方案4】:

      试试这个:

      private void updateUI() {
          final TextView tv = (TextView) findViewById(R.id.hello);
          tv.setText(data == null ? "null" : data);
      }
      

      如果失败,是否可能是时间问题?也就是说,会不会是方​​向变化时完成的任务?为了安全起见,如果任务已完成,您可以通过添加 updateUI() 调用来修改您的 onCreate 方法:

       @Override
          public void onCreate(Bundle icicle) {
              super.onCreate(icicle);
              setContentView(R.layout.activity_main);
      
              tv = (TextView) findViewById(R.id.hello);
      
              if (getLastNonConfigurationInstance() != null) {
                  task = (MyAsyncTask) getLastNonConfigurationInstance();
                  if (task != null) {
                      if (!(task.getStatus().equals(AsyncTask.Status.FINISHED))) {
                          showProgressDialog();
                      } else
                          updateUI();
                  }
              }
          }
      

      【讨论】:

      • 这不起作用,因为 tv 仍将是旧活动的 TextView。
      • 是的。但解决方案是什么?为什么我们无法访问新电视?
      • @ekholm,不应该这样,因为 onCreate 在方向变化时被调用,并且应该膨胀特定于方向的布局。蒂姆,您能否在 onCreate 中添加一些断点以验证它确实被调用并且 TextView 解析。出于测试目的,将其中一个 TextView 重命名为 tvHelloLand 并查看 FindViewById 是否可以找到它。如果是这样,丑陋的解决方案是先找到 tvHello,如果它为空,则找到 tvHelloLand。理论上,您的解决方案应该可以工作,因为它会在布局膨胀后找到 TextView。
      • 还是没有运气。 Textview 膨胀了 2 次,第二次,我认为它是从横向布局中获得的。但仍不更新其文本。
      • 好的,现在我们处于调试模式;)添加一些 Log.d 并在更新时转储“数据”的内容,这样你就知道你实际上正在改变一些东西......和/或设置一个已知的更改文本。 (即 static int count = 1; tv.setText( String.format( "My text: %d", count++ ) ); )
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-05-12
      • 2014-12-18
      • 1970-01-01
      • 2017-10-24
      • 2018-01-28
      • 1970-01-01
      • 2012-08-13
      相关资源
      最近更新 更多