zoukankan      html  css  js  c++  java
  • Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lmj623565791/article/details/37936275

    转载请表明出处:http://blog.csdn.net/lmj623565791/article/details/37936275

    1、概述

    众所周知,Activity在不明确指定屏幕方向和configChanges时,当用户旋转屏幕会重新启动。当然了,应对这种情况,Android给出了几种方案:

    a、如果是少量数据,可以通过onSaveInstanceState()和onRestoreInstanceState()进行保存与恢复。

    Android会在销毁你的Activity之前调用onSaveInstanceState()方法,于是,你可以在此方法中存储关于应用状态的数据。然后你可以在onCreate()或onRestoreInstanceState()方法中恢复。

    b、如果是大量数据,使用Fragment保持需要恢复的对象。

    c、自已处理配置变化。

    注:getLastNonConfigurationInstance()已经被弃用,被上述方法二替代。

    2、难点

    假设当前Activity在onCreate中启动一个异步线程去夹在数据,当然为了给用户一个很好的体验,会有一个ProgressDialog,当数据加载完成,ProgressDialog消失,设置数据。

    这里,如果在异步数据完成加载之后,旋转屏幕,使用上述a、b两种方法都不会很难,无非是保存数据和恢复数据。

    但是,如果正在线程加载的时候,进行旋转,会存在以下问题:

    a)此时数据没有完成加载,onCreate重新启动时,会再次启动线程;而上个线程可能还在运行,并且可能会更新已经不存在的控件,造成错误。

    b)关闭ProgressDialog的代码在线程的onPostExecutez中,但是上个线程如果已经杀死,无法关闭之前ProgressDialog。

    c)谷歌的官方不建议使用ProgressDialog,这里我们会使用官方推荐的DialogFragment来创建我的加载框,如果你不了解:请看 Android 官方推荐 : DialogFragment 创建对话框。这样,其实给我们带来一个很大的问题,DialogFragment说白了是Fragment,和当前的Activity的生命周期会发生绑定,我们旋转屏幕会造成Activity的销毁,当然也会对DialogFragment造成影响。

    下面我将使用几个例子,分别使用上面的3种方式,和如何最好的解决上述的问题。

    3、使用onSaveInstanceState()和onRestoreInstanceState()进行数据保存与恢复

    代码:

    1.  
      package com.example.zhy_handle_runtime_change;
    2.  
       
    3.  
      import java.util.ArrayList;
    4.  
      import java.util.Arrays;
    5.  
       
    6.  
      import android.app.DialogFragment;
    7.  
      import android.app.ListActivity;
    8.  
      import android.os.AsyncTask;
    9.  
      import android.os.Bundle;
    10.  
      import android.util.Log;
    11.  
      import android.widget.ArrayAdapter;
    12.  
      import android.widget.ListAdapter;
    13.  
      /**
    14.  
      * 不考虑加载时,进行旋转的情况,有意的避开这种情况,后面例子会介绍解决方案
    15.  
      * @author zhy
    16.  
      *
    17.  
      */
    18.  
      public class SavedInstanceStateUsingActivity extends ListActivity
    19.  
      {
    20.  
      private static final String TAG = "MainActivity";
    21.  
      private ListAdapter mAdapter;
    22.  
      private ArrayList<String> mDatas;
    23.  
      private DialogFragment mLoadingDialog;
    24.  
      private LoadDataAsyncTask mLoadDataAsyncTask;
    25.  
       
    26.  
      @Override
    27.  
      public void onCreate(Bundle savedInstanceState)
    28.  
      {
    29.  
      super.onCreate(savedInstanceState);
    30.  
      Log.e(TAG, "onCreate");
    31.  
      initData(savedInstanceState);
    32.  
      }
    33.  
       
    34.  
      /**
    35.  
      * 初始化数据
    36.  
      */
    37.  
      private void initData(Bundle savedInstanceState)
    38.  
      {
    39.  
      if (savedInstanceState != null)
    40.  
      mDatas = savedInstanceState.getStringArrayList("mDatas");
    41.  
       
    42.  
      if (mDatas == null)
    43.  
      {
    44.  
      mLoadingDialog = new LoadingDialog();
    45.  
      mLoadingDialog.show(getFragmentManager(), "LoadingDialog");
    46.  
      mLoadDataAsyncTask = new LoadDataAsyncTask();
    47.  
      mLoadDataAsyncTask.execute();
    48.  
       
    49.  
      } else
    50.  
      {
    51.  
      initAdapter();
    52.  
      }
    53.  
       
    54.  
      }
    55.  
       
    56.  
      /**
    57.  
      * 初始化适配器
    58.  
      */
    59.  
      private void initAdapter()
    60.  
      {
    61.  
      mAdapter = new ArrayAdapter<String>(
    62.  
      SavedInstanceStateUsingActivity.this,
    63.  
      android.R.layout.simple_list_item_1, mDatas);
    64.  
      setListAdapter(mAdapter);
    65.  
      }
    66.  
       
    67.  
      @Override
    68.  
      protected void onRestoreInstanceState(Bundle state)
    69.  
      {
    70.  
      super.onRestoreInstanceState(state);
    71.  
      Log.e(TAG, "onRestoreInstanceState");
    72.  
      }
    73.  
       
    74.  
      @Override
    75.  
      protected void onSaveInstanceState(Bundle outState)
    76.  
      {
    77.  
      super.onSaveInstanceState(outState);
    78.  
      Log.e(TAG, "onSaveInstanceState");
    79.  
      outState.putSerializable("mDatas", mDatas);
    80.  
       
    81.  
      }
    82.  
       
    83.  
      /**
    84.  
      * 模拟耗时操作
    85.  
      *
    86.  
      * @return
    87.  
      */
    88.  
      private ArrayList<String> generateTimeConsumingDatas()
    89.  
      {
    90.  
      try
    91.  
      {
    92.  
      Thread.sleep(2000);
    93.  
      } catch (InterruptedException e)
    94.  
      {
    95.  
      }
    96.  
      return new ArrayList<String>(Arrays.asList("通过Fragment保存大量数据",
    97.  
      "onSaveInstanceState保存数据",
    98.  
      "getLastNonConfigurationInstance已经被弃用", "RabbitMQ", "Hadoop",
    99.  
      "Spark"));
    100.  
      }
    101.  
       
    102.  
      private class LoadDataAsyncTask extends AsyncTask<Void, Void, Void>
    103.  
      {
    104.  
      @Override
    105.  
      protected Void doInBackground(Void... params)
    106.  
      {
    107.  
      mDatas = generateTimeConsumingDatas();
    108.  
      return null;
    109.  
      }
    110.  
       
    111.  
      @Override
    112.  
      protected void onPostExecute(Void result)
    113.  
      {
    114.  
      mLoadingDialog.dismiss();
    115.  
      initAdapter();
    116.  
      }
    117.  
      }
    118.  
       
    119.  
      @Override
    120.  
      protected void onDestroy()
    121.  
      {
    122.  
      Log.e(TAG, "onDestroy");
    123.  
      super.onDestroy();
    124.  
      }
    125.  
       
    126.  
      }


    界面为一个ListView,onCreate中启动一个异步任务去加载数据,这里使用Thread.sleep模拟了一个耗时操作;当用户旋转屏幕发生重新启动时,会onSaveInstanceState中进行数据的存储,在onCreate中对数据进行恢复,免去了不必要的再加载一遍。

    运行结果:

    当正常加载数据完成之后,用户不断进行旋转屏幕,log会不断打出:onSaveInstanceState->onDestroy->onCreate->onRestoreInstanceState,验证我们的确是重新启动了,但是我们没有再次去进行数据加载。

    如果在加载的时候,进行旋转,则会发生错误,异常退出(退出原因:dialog.dismiss()时发生NullPointException,因为与当前对话框绑定的FragmentManager为null,又有兴趣的可以去Debug,这个不是关键)。

    效果图:

    4、使用Fragment来保存对象,用于恢复数据

    如果重新启动你的Activity需要恢复大量的数据,重新建立网络连接,或者执行其他的密集型操作,这样因为配置发生变化而完全重新启动可能会是一个慢的用户体验。并且,使用系统提供的onSaveIntanceState()的回调中,使用Bundle来完全恢复你Activity的状态是可能是不现实的(Bundle不是设计用来携带大量数据的(例如bitmap),并且Bundle中的数据必须能够被序列化和反序列化),这样会消耗大量的内存和导致配置变化缓慢。在这样的情况下,当你的Activity因为配置发生改变而重启,你可以通过保持一个Fragment来缓解重新启动带来的负担。这个Fragment可以包含你想要保持的有状态的对象的引用。

    当Android系统因为配置变化关闭你的Activity的时候,你的Activity中被标识保持的fragments不会被销毁。你可以在你的Activity中添加这样的fragements来保存有状态的对象。

    在运行时配置发生变化时,在Fragment中保存有状态的对象
    a) 继承Fragment,声明引用指向你的有状态的对象
    b) 当Fragment创建时调用setRetainInstance(boolean)
    c) 把Fragment实例添加到Activity中
    d) 当Activity重新启动后,使用FragmentManager对Fragment进行恢复
    代码:

    首先是Fragment:

    1.  
      package com.example.zhy_handle_runtime_change;
    2.  
       
    3.  
      import android.app.Fragment;
    4.  
      import android.graphics.Bitmap;
    5.  
      import android.os.Bundle;
    6.  
       
    7.  
      public class RetainedFragment extends Fragment
    8.  
      {
    9.  
      // data object we want to retain
    10.  
      private Bitmap data;
    11.  
      // this method is only called once for this fragment
    12.  
      @Override
    13.  
      public void onCreate(Bundle savedInstanceState)
    14.  
      {
    15.  
      super.onCreate(savedInstanceState);
    16.  
      // retain this fragment
    17.  
      setRetainInstance(true);
    18.  
      }
    19.  
       
    20.  
      public void setData(Bitmap data)
    21.  
      {
    22.  
      this.data = data;
    23.  
      }
    24.  
       
    25.  
      public Bitmap getData()
    26.  
      {
    27.  
      return data;
    28.  
      }
    29.  
      }

    比较简单,只需要声明需要保存的数据对象,然后提供getter和setter,注意,一定要在onCreate调用setRetainInstance(true);

    然后是:FragmentRetainDataActivity

    1.  
      package com.example.zhy_handle_runtime_change;
    2.  
       
    3.  
      import android.app.Activity;
    4.  
      import android.app.DialogFragment;
    5.  
      import android.app.FragmentManager;
    6.  
      import android.graphics.Bitmap;
    7.  
      import android.graphics.Bitmap.Config;
    8.  
      import android.os.Bundle;
    9.  
      import android.util.Log;
    10.  
      import android.widget.ImageView;
    11.  
       
    12.  
      import com.android.volley.RequestQueue;
    13.  
      import com.android.volley.Response;
    14.  
      import com.android.volley.toolbox.ImageRequest;
    15.  
      import com.android.volley.toolbox.Volley;
    16.  
       
    17.  
      public class FragmentRetainDataActivity extends Activity
    18.  
      {
    19.  
       
    20.  
      private static final String TAG = "FragmentRetainDataActivity";
    21.  
      private RetainedFragment dataFragment;
    22.  
      private DialogFragment mLoadingDialog;
    23.  
      private ImageView mImageView;
    24.  
      private Bitmap mBitmap;
    25.  
       
    26.  
      @Override
    27.  
      public void onCreate(Bundle savedInstanceState)
    28.  
      {
    29.  
      super.onCreate(savedInstanceState);
    30.  
      setContentView(R.layout.activity_main);
    31.  
      Log.e(TAG, "onCreate");
    32.  
       
    33.  
      // find the retained fragment on activity restarts
    34.  
      FragmentManager fm = getFragmentManager();
    35.  
      dataFragment = (RetainedFragment) fm.findFragmentByTag("data");
    36.  
      // create the fragment and data the first time
    37.  
      if (dataFragment == null)
    38.  
      {
    39.  
      // add the fragment
    40.  
      dataFragment = new RetainedFragment();
    41.  
      fm.beginTransaction().add(dataFragment, "data").commit();
    42.  
      }
    43.  
      mBitmap = collectMyLoadedData();
    44.  
      initData();
    45.  
       
    46.  
      // the data is available in dataFragment.getData()
    47.  
      }
    48.  
       
    49.  
      /**
    50.  
      * 初始化数据
    51.  
      */
    52.  
      private void initData()
    53.  
      {
    54.  
      mImageView = (ImageView) findViewById(R.id.id_imageView);
    55.  
      if (mBitmap == null)
    56.  
      {
    57.  
      mLoadingDialog = new LoadingDialog();
    58.  
      mLoadingDialog.show(getFragmentManager(), "LOADING_DIALOG");
    59.  
      RequestQueue newRequestQueue = Volley
    60.  
      .newRequestQueue(FragmentRetainDataActivity.this);
    61.  
      ImageRequest imageRequest = new ImageRequest(
    62.  
      "http://img.my.csdn.net/uploads/201407/18/1405652589_5125.jpg",
    63.  
      new Response.Listener<Bitmap>()
    64.  
      {
    65.  
      @Override
    66.  
      public void onResponse(Bitmap response)
    67.  
      {
    68.  
      mBitmap = response;
    69.  
      mImageView.setImageBitmap(mBitmap);
    70.  
      // load the data from the web
    71.  
      dataFragment.setData(mBitmap);
    72.  
      mLoadingDialog.dismiss();
    73.  
      }
    74.  
      }, 0, 0, Config.RGB_565, null);
    75.  
      newRequestQueue.add(imageRequest);
    76.  
      } else
    77.  
      {
    78.  
      mImageView.setImageBitmap(mBitmap);
    79.  
      }
    80.  
       
    81.  
      }
    82.  
       
    83.  
      @Override
    84.  
      public void onDestroy()
    85.  
      {
    86.  
      Log.e(TAG, "onDestroy");
    87.  
      super.onDestroy();
    88.  
      // store the data in the fragment
    89.  
      dataFragment.setData(mBitmap);
    90.  
      }
    91.  
       
    92.  
      private Bitmap collectMyLoadedData()
    93.  
      {
    94.  
      return dataFragment.getData();
    95.  
      }
    96.  
       
    97.  
      }


    这里在onCreate总使用了Volley去加载 了一张美女照片,然后在onDestroy中对Bitmap进行存储,在onCreate添加一个或者恢复一个Fragment的引用,然后对Bitmap进行读取和设置。这种方式适用于比较大的数据的存储与恢复。

    注:这里也没有考虑加载时旋转屏幕,问题与上面的一致。

    效果图:

    5、配置configChanges,自己对屏幕旋转的变化进行处理

    在menifest中进行属性设置:

    1.  
      <activity
    2.  
      android:name=".ConfigChangesTestActivity"
    3.  
      android:configChanges="screenSize|orientation" >
    4.  
      </activity>
    低版本的API只需要加入orientation,而高版本的则需要加入screenSize。

    ConfigChangesTestActivity

    1.  
      package com.example.zhy_handle_runtime_change;
    2.  
       
    3.  
      import java.util.ArrayList;
    4.  
      import java.util.Arrays;
    5.  
       
    6.  
      import android.app.DialogFragment;
    7.  
      import android.app.ListActivity;
    8.  
      import android.content.res.Configuration;
    9.  
      import android.os.AsyncTask;
    10.  
      import android.os.Bundle;
    11.  
      import android.util.Log;
    12.  
      import android.widget.ArrayAdapter;
    13.  
      import android.widget.ListAdapter;
    14.  
      import android.widget.Toast;
    15.  
       
    16.  
      /**
    17.  
      * @author zhy
    18.  
      *
    19.  
      */
    20.  
      public class ConfigChangesTestActivity extends ListActivity
    21.  
      {
    22.  
      private static final String TAG = "MainActivity";
    23.  
      private ListAdapter mAdapter;
    24.  
      private ArrayList<String> mDatas;
    25.  
      private DialogFragment mLoadingDialog;
    26.  
      private LoadDataAsyncTask mLoadDataAsyncTask;
    27.  
       
    28.  
      @Override
    29.  
      public void onCreate(Bundle savedInstanceState)
    30.  
      {
    31.  
      super.onCreate(savedInstanceState);
    32.  
      Log.e(TAG, "onCreate");
    33.  
      initData(savedInstanceState);
    34.  
      }
    35.  
       
    36.  
      /**
    37.  
      * 初始化数据
    38.  
      */
    39.  
      private void initData(Bundle savedInstanceState)
    40.  
      {
    41.  
       
    42.  
      mLoadingDialog = new LoadingDialog();
    43.  
      mLoadingDialog.show(getFragmentManager(), "LoadingDialog");
    44.  
      mLoadDataAsyncTask = new LoadDataAsyncTask();
    45.  
      mLoadDataAsyncTask.execute();
    46.  
       
    47.  
      }
    48.  
       
    49.  
      /**
    50.  
      * 初始化适配器
    51.  
      */
    52.  
      private void initAdapter()
    53.  
      {
    54.  
      mAdapter = new ArrayAdapter<String>(ConfigChangesTestActivity.this,
    55.  
      android.R.layout.simple_list_item_1, mDatas);
    56.  
      setListAdapter(mAdapter);
    57.  
      }
    58.  
       
    59.  
      /**
    60.  
      * 模拟耗时操作
    61.  
      *
    62.  
      * @return
    63.  
      */
    64.  
      private ArrayList<String> generateTimeConsumingDatas()
    65.  
      {
    66.  
      try
    67.  
      {
    68.  
      Thread.sleep(2000);
    69.  
      } catch (InterruptedException e)
    70.  
      {
    71.  
      }
    72.  
      return new ArrayList<String>(Arrays.asList("通过Fragment保存大量数据",
    73.  
      "onSaveInstanceState保存数据",
    74.  
      "getLastNonConfigurationInstance已经被弃用", "RabbitMQ", "Hadoop",
    75.  
      "Spark"));
    76.  
      }
    77.  
       
    78.  
      /**
    79.  
      * 当配置发生变化时,不会重新启动Activity。但是会回调此方法,用户自行进行对屏幕旋转后进行处理
    80.  
      */
    81.  
      @Override
    82.  
      public void onConfigurationChanged(Configuration newConfig)
    83.  
      {
    84.  
      super.onConfigurationChanged(newConfig);
    85.  
       
    86.  
      if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)
    87.  
      {
    88.  
      Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
    89.  
      } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT)
    90.  
      {
    91.  
      Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
    92.  
      }
    93.  
       
    94.  
      }
    95.  
       
    96.  
      private class LoadDataAsyncTask extends AsyncTask<Void, Void, Void>
    97.  
      {
    98.  
      @Override
    99.  
      protected Void doInBackground(Void... params)
    100.  
      {
    101.  
      mDatas = generateTimeConsumingDatas();
    102.  
      return null;
    103.  
      }
    104.  
       
    105.  
      @Override
    106.  
      protected void onPostExecute(Void result)
    107.  
      {
    108.  
      mLoadingDialog.dismiss();
    109.  
      initAdapter();
    110.  
      }
    111.  
      }
    112.  
       
    113.  
      @Override
    114.  
      protected void onDestroy()
    115.  
      {
    116.  
      Log.e(TAG, "onDestroy");
    117.  
      super.onDestroy();
    118.  
      }
    119.  
       
    120.  
      }

    对第一种方式的代码进行了修改,去掉了保存与恢复的代码,重写了onConfigurationChanged;此时,无论用户何时旋转屏幕都不会重新启动Activity,并且onConfigurationChanged中的代码可以得到调用。从效果图可以看到,无论如何旋转不会重启Activity.

    效果图:

    6、旋转屏幕的最佳实践

    下面要开始今天的难点了,就是处理文章开始时所说的,当异步任务在执行时,进行旋转,如果解决上面的问题。

    首先说一下探索过程:

    起初,我认为此时旋转无非是再启动一次线程,并不会造成异常,我只要即使的在onDestroy里面关闭上一个异步任务就可以了。事实上,如果我关闭了,上一次的对话框会一直存在;如果我不关闭,但是activity是一定会被销毁的,对话框的dismiss也会出异常。真心很蛋疼,并且即使对话框关闭了,任务关闭了;用户旋转还是会造成重新创建任务,从头开始加载数据。

    下面我们希望有一种解决方案:在加载数据时旋转屏幕,不会对加载任务进行中断,且对用户而言,等待框在加载完成之前都正常显示:

    当然我们还使用Fragment进行数据保存,毕竟这是官方推荐的:

    OtherRetainedFragment

    1.  
      package com.example.zhy_handle_runtime_change;
    2.  
       
    3.  
      import android.app.Fragment;
    4.  
      import android.os.Bundle;
    5.  
       
    6.  
      /**
    7.  
      * 保存对象的Fragment
    8.  
      *
    9.  
      * @author zhy
    10.  
      *
    11.  
      */
    12.  
      public class OtherRetainedFragment extends Fragment
    13.  
      {
    14.  
       
    15.  
      // data object we want to retain
    16.  
      // 保存一个异步的任务
    17.  
      private MyAsyncTask data;
    18.  
       
    19.  
      // this method is only called once for this fragment
    20.  
      @Override
    21.  
      public void onCreate(Bundle savedInstanceState)
    22.  
      {
    23.  
      super.onCreate(savedInstanceState);
    24.  
      // retain this fragment
    25.  
      setRetainInstance(true);
    26.  
      }
    27.  
       
    28.  
      public void setData(MyAsyncTask data)
    29.  
      {
    30.  
      this.data = data;
    31.  
      }
    32.  
       
    33.  
      public MyAsyncTask getData()
    34.  
      {
    35.  
      return data;
    36.  
      }
    37.  
       
    38.  
       
    39.  
      }

    和上面的差别不大,唯一不同的就是它要保存的对象编程一个异步的任务了,相信看到这,已经知道经常上述问题的一个核心了,保存一个异步任务,在重启时,继续这个任务。
    1.  
      package com.example.zhy_handle_runtime_change;
    2.  
       
    3.  
      import java.util.ArrayList;
    4.  
      import java.util.Arrays;
    5.  
      import java.util.List;
    6.  
       
    7.  
      import android.os.AsyncTask;
    8.  
       
    9.  
      public class MyAsyncTask extends AsyncTask<Void, Void, Void>
    10.  
      {
    11.  
      private FixProblemsActivity activity;
    12.  
      /**
    13.  
      * 是否完成
    14.  
      */
    15.  
      private boolean isCompleted;
    16.  
      /**
    17.  
      * 进度框
    18.  
      */
    19.  
      private LoadingDialog mLoadingDialog;
    20.  
      private List<String> items;
    21.  
       
    22.  
      public MyAsyncTask(FixProblemsActivity activity)
    23.  
      {
    24.  
      this.activity = activity;
    25.  
      }
    26.  
       
    27.  
      /**
    28.  
      * 开始时,显示加载框
    29.  
      */
    30.  
      @Override
    31.  
      protected void onPreExecute()
    32.  
      {
    33.  
      mLoadingDialog = new LoadingDialog();
    34.  
      mLoadingDialog.show(activity.getFragmentManager(), "LOADING");
    35.  
      }
    36.  
       
    37.  
      /**
    38.  
      * 加载数据
    39.  
      */
    40.  
      @Override
    41.  
      protected Void doInBackground(Void... params)
    42.  
      {
    43.  
      items = loadingData();
    44.  
      return null;
    45.  
      }
    46.  
       
    47.  
      /**
    48.  
      * 加载完成回调当前的Activity
    49.  
      */
    50.  
      @Override
    51.  
      protected void onPostExecute(Void unused)
    52.  
      {
    53.  
      isCompleted = true;
    54.  
      notifyActivityTaskCompleted();
    55.  
      if (mLoadingDialog != null)
    56.  
      mLoadingDialog.dismiss();
    57.  
      }
    58.  
       
    59.  
      public List<String> getItems()
    60.  
      {
    61.  
      return items;
    62.  
      }
    63.  
       
    64.  
      private List<String> loadingData()
    65.  
      {
    66.  
      try
    67.  
      {
    68.  
      Thread.sleep(5000);
    69.  
      } catch (InterruptedException e)
    70.  
      {
    71.  
      }
    72.  
      return new ArrayList<String>(Arrays.asList("通过Fragment保存大量数据",
    73.  
      "onSaveInstanceState保存数据",
    74.  
      "getLastNonConfigurationInstance已经被弃用", "RabbitMQ", "Hadoop",
    75.  
      "Spark"));
    76.  
      }
    77.  
       
    78.  
      /**
    79.  
      * 设置Activity,因为Activity会一直变化
    80.  
      *
    81.  
      * @param activity
    82.  
      */
    83.  
      public void setActivity(FixProblemsActivity activity)
    84.  
      {
    85.  
      // 如果上一个Activity销毁,将与上一个Activity绑定的DialogFragment销毁
    86.  
      if (activity == null)
    87.  
      {
    88.  
      mLoadingDialog.dismiss();
    89.  
      }
    90.  
      // 设置为当前的Activity
    91.  
      this.activity = activity;
    92.  
      // 开启一个与当前Activity绑定的等待框
    93.  
      if (activity != null && !isCompleted)
    94.  
      {
    95.  
      mLoadingDialog = new LoadingDialog();
    96.  
      mLoadingDialog.show(activity.getFragmentManager(), "LOADING");
    97.  
      }
    98.  
      // 如果完成,通知Activity
    99.  
      if (isCompleted)
    100.  
      {
    101.  
      notifyActivityTaskCompleted();
    102.  
      }
    103.  
      }
    104.  
       
    105.  
      private void notifyActivityTaskCompleted()
    106.  
      {
    107.  
      if (null != activity)
    108.  
      {
    109.  
      activity.onTaskCompleted();
    110.  
      }
    111.  
      }
    112.  
       
    113.  
      }

    异步任务中,管理一个对话框,当开始下载前,进度框显示,下载结束进度框消失,并为Activity提供回调。当然了,运行过程中Activity不断的重启,我们也提供了setActivity方法,onDestory时,会setActivity(null)防止内存泄漏,同时我们也会关闭与其绑定的加载框;当onCreate传入新的Activity时,我们会在再次打开一个加载框,当然了因为屏幕的旋转并不影响加载的数据,所有后台的数据一直继续在加载。是不是很完美~~

    主Activity:

    1.  
      package com.example.zhy_handle_runtime_change;
    2.  
       
    3.  
      import java.util.List;
    4.  
       
    5.  
      import android.app.FragmentManager;
    6.  
      import android.app.ListActivity;
    7.  
      import android.os.Bundle;
    8.  
      import android.util.Log;
    9.  
      import android.widget.ArrayAdapter;
    10.  
      import android.widget.ListAdapter;
    11.  
       
    12.  
      public class FixProblemsActivity extends ListActivity
    13.  
      {
    14.  
      private static final String TAG = "MainActivity";
    15.  
      private ListAdapter mAdapter;
    16.  
      private List<String> mDatas;
    17.  
      private OtherRetainedFragment dataFragment;
    18.  
      private MyAsyncTask mMyTask;
    19.  
       
    20.  
      @Override
    21.  
      public void onCreate(Bundle savedInstanceState)
    22.  
      {
    23.  
      super.onCreate(savedInstanceState);
    24.  
      Log.e(TAG, "onCreate");
    25.  
       
    26.  
      // find the retained fragment on activity restarts
    27.  
      FragmentManager fm = getFragmentManager();
    28.  
      dataFragment = (OtherRetainedFragment) fm.findFragmentByTag("data");
    29.  
       
    30.  
      // create the fragment and data the first time
    31.  
      if (dataFragment == null)
    32.  
      {
    33.  
      // add the fragment
    34.  
      dataFragment = new OtherRetainedFragment();
    35.  
      fm.beginTransaction().add(dataFragment, "data").commit();
    36.  
      }
    37.  
      mMyTask = dataFragment.getData();
    38.  
      if (mMyTask != null)
    39.  
      {
    40.  
      mMyTask.setActivity(this);
    41.  
      } else
    42.  
      {
    43.  
      mMyTask = new MyAsyncTask(this);
    44.  
      dataFragment.setData(mMyTask);
    45.  
      mMyTask.execute();
    46.  
      }
    47.  
      // the data is available in dataFragment.getData()
    48.  
      }
    49.  
       
    50.  
       
    51.  
      @Override
    52.  
      protected void onRestoreInstanceState(Bundle state)
    53.  
      {
    54.  
      super.onRestoreInstanceState(state);
    55.  
      Log.e(TAG, "onRestoreInstanceState");
    56.  
      }
    57.  
       
    58.  
       
    59.  
      @Override
    60.  
      protected void onSaveInstanceState(Bundle outState)
    61.  
      {
    62.  
      mMyTask.setActivity(null);
    63.  
      super.onSaveInstanceState(outState);
    64.  
      Log.e(TAG, "onSaveInstanceState");
    65.  
      }
    66.  
       
    67.  
      @Override
    68.  
      protected void onDestroy()
    69.  
      {
    70.  
      Log.e(TAG, "onDestroy");
    71.  
      super.onDestroy();
    72.  
       
    73.  
      }
    74.  
      /**
    75.  
      * 回调
    76.  
      */
    77.  
      public void onTaskCompleted()
    78.  
      {
    79.  
      mDatas = mMyTask.getItems();
    80.  
      mAdapter = new ArrayAdapter<String>(FixProblemsActivity.this,
    81.  
      android.R.layout.simple_list_item_1, mDatas);
    82.  
      setListAdapter(mAdapter);
    83.  
      }
    84.  
       
    85.  
      }

    在onCreate中,如果没有开启任务(第一次进入),开启任务;如果已经开启了,调用setActivity(this);

    在onSaveInstanceState把当前任务加入Fragment

    我设置了等待5秒,足够旋转三四个来回了~~~~可以看到虽然在不断的重启,但是丝毫不影响加载数据任务的运行和加载框的显示~~~~

    效果图:

    可以看到我在加载的时候就三心病狂的旋转屏幕~~但是丝毫不影响显示效果与任务的加载~~

    最后,说明一下,其实不仅是屏幕旋转需要保存数据,当用户在使用你的app时,忽然接到一个来电,长时间没有回到你的app界面也会造成Activity的销毁与重建,所以一个行为良好的App,是有必要拥有恢复数据的能力的~~。

    查阅资料时的一些参考文档:

    http://developer.android.com/guide/topics/resources/runtime-changes.html

    http://blog.doityourselfandroid.com/2010/11/14/handling-progress-dialogs-and-screen-orientation-changes/


    有任何问题,欢迎留言 

    源码点击下载

  • 相关阅读:
    input输入框与元素间有间隙
    显示3行,还要省略号(这个属性比较合适WebKit浏览器或移动端(绝大部分是WebKit内核的)浏览器)
    input file 修改按钮名称
    文本溢出处理
    移动WEB前端开发资源的一些素材
    带弹性的导航栏
    带重力的公告栏
    淘宝放大镜效果
    【规范】javascript 变量命名规则(转)
    常见的仿Flash图片轮播效果
  • 原文地址:https://www.cnblogs.com/Im-Victor/p/9557672.html
Copyright © 2011-2022 走看看