android执行耗时操作,屏幕旋转怎么办

【声明:】本文是作者(蘑菇v5)原创,版权归作者 蘑菇v5所有,侵权必究。本文首发在。如若转发,请注明作者和来源地址!未经授权,严禁私自转载!

解决方案:

线程在异步加载数据时,屏幕旋转不会中断任务线程,等待加载框在加载完成之前一直正常显示,并且使用Fragment进行数据保存,因为这是官方推荐的,适用于比较大的数据的存储与恢复(如 bitmap)。

RetainedFragment:

/** 
 * 保存对象的Fragment 
 *  
 * @author zsnlh 
 *  
 */  
public class RetainedFragment extends Fragment {  
  
    // data object we want to retain  
    // 保存一个异步的任务  
    private MyAsyncTask data;  
    // this method is only called once for this fragment  
    @Override  
    public void onCreate(Bundle savedInstanceState)  {  
        super.onCreate(savedInstanceState);  
        // retain this fragment  
        setRetainInstance(true);  
    }  
    public void setData(MyAsyncTask data) {  
        this.data = data;  
    }  
    public MyAsyncTask getData()   {  
        return data;  
    }        
}  

关键思想:保存一个异步任务,在重启时,继续这个任务。

MyAsyncTask:

/**
*  MyAsyncTask
* @author zsnlh 
*/
public class MyAsyncTask extends AsyncTask {  
 //保存外部activity的弱引用
 private WeakReference weakReference;
 public MyAsyncTask(Context context) {
 weakReference = new WeakReference<>(context);
 }
    private FixProblemsActivity activity;  
    /** 
     * 是否完成 
     */  
    private boolean isCompleted;  
    /** 
     * 进度框 
     */  
    private LoadingDialog mLoadingDialog;  
    private List items;  
  
    public MyAsyncTask(FixProblemsActivity activity) {  
        this.activity = activity;  
    }  
  
    /** 
     * 开始时,显示加载框 
     */  
    @Override  
    protected void onPreExecute() {  
        mLoadingDialog = new LoadingDialog();  
        activity = (FixProblemsActivity) weakReference.get();
if(activity != null){
        mLoadingDialog.show(activity.getFragmentManager(), "LOADING");  
  }
}  
  
    /** 
     * 加载数据 
     */  
    @Override  
    protected Void doInBackground(Void... params)  {  
        items = loadingData();  
        return null;  
    }  
  
    /** 
     * 加载完成回调当前的Activity 
     */  
    @Override  
    protected void onPostExecute(Void unused) {  
        isCompleted = true;  
        notifyActivityTaskCompleted();  
        if (mLoadingDialog != null)  
            mLoadingDialog.dismiss();  
    }  
  
    public List getItems() {  
        return items;  
    }  
  
    private List loadingData()  {  
        try {  
            Thread.sleep(3000);  
        } catch (InterruptedException e) {  
        }  
        return new ArrayList(Arrays.asList("通过Fragment保存大量数据",  
                "onSaveInstanceState保存数据",  
                "getLastNonConfigurationInstance已经被弃用", "穿越火线", "英雄联盟",  
                "王者荣耀"));  
    }  
  
    /** 
     * 设置Activity,因为Activity会一直变化 
     *  
     * @param activity 
     */  
    public void setActivity(FixProblemsActivity activity)  { 
        weakReference = new WeakReference<>(activity);
        // 设置为当前的Activity
         this.activity = (FixProblemsActivity) activity; 
        // 如果上一个Activity销毁,将与上一个Activity绑定的DialogFragment销毁  
        if (activity == null) {  
           dialogDismiss(); 
        } 
        // 开启一个与当前Activity绑定的等待框  
        if (activity != null && !isCompleted)  {  
            mLoadingDialog = new LoadingDialog();  
            mLoadingDialog.show(activity.getFragmentManager(), "LOADING");  
        }  
        // 如果完成,通知Activity  
        if (isCompleted) {  
            notifyActivityTaskCompleted();  
        }  
    }  
  
    private void notifyActivityTaskCompleted() {  
        if (null != activity)  {  
            activity.onTaskCompleted();  
        }  
    } 
/**
 * 在Activity不可见时,关闭dialog
 */
 public void dialogDismiss(){
 if(mLoadingDialog != null){
 mLoadingDialog.dismiss();
   }
 }  
}  

和之前一样保留Activity的虚引用,防止内存泄漏。
setActivity()方法用于任务未完成时,重启activity相应的创建一个新的dialog
dialogDismiss用于在在Activity不可见时,关闭dialog,防止以前的dialog造成内存泄漏。
当任务完成时,调用activity的回调方法onTaskCompleted更新UI
异步任务中,管理一个对话框,当开始下载前,进度框显示,下载结束进度框消失,并为Activity提供回调。当然了,运行过程中Activity不断的重启,我们也提供了setActivity方法,onDestory时,会setActivity(null)防止内存泄漏,同时我们也会关闭与其绑定的加载框;当onCreate传入新的Activity时,我们会在再次打开一个加载框,当然了因为屏幕的旋转并不影响加载的数据,所有后台的数据一直继续在加载。

Activity:

public class FixProblemsActivity extends ListActivity {  
    private static final String TAG = "MainActivity";  
    private ListAdapter mAdapter;  
    private List mDatas;  
    private RetainedFragment dataFragment;  
    private MyAsyncTask mMyTask;  
  
    @Override  
    public void onCreate(Bundle savedInstanceState)  {  
        super.onCreate(savedInstanceState);  
        Log.e(TAG, "onCreate");  
        // find the retained fragment on activity restarts  
        FragmentManager fm = getFragmentManager();  
        dataFragment = (RetainedFragment) fm.findFragmentByTag("data");  
        // create the fragment and data the first time  
        if (dataFragment == null)  {  
            // add the fragment  
            dataFragment = new OtherRetainedFragment();  
            fm.beginTransaction().add(dataFragment, "data").commit();  
        }  
        mMyTask = dataFragment.getData();  
        if (mMyTask != null) {  
            //与新的Activity进行绑定
            mMyTask.setActivity(this);  
        } else {  
            //启动一个新的任务
            mMyTask = new MyAsyncTask(this);  
            dataFragment.setData(mMyTask);  
            mMyTask.execute();  
        }  
        // the data is available in dataFragment.getData()  
    }  
    @Override  
    protected void onRestoreInstanceState(Bundle state)  {  
        super.onRestoreInstanceState(state);  
        Log.e(TAG, "onRestoreInstanceState");  
    }  
    @Override  
    protected void onSaveInstanceState(Bundle outState)  {  
        mMyTask.setActivity(null);  
        super.onSaveInstanceState(outState);  
        Log.e(TAG, "onSaveInstanceState");  
    }    
    @Override  
    protected void onDestroy() {  
        Log.e(TAG, "onDestroy");  
        super.onDestroy();    
    }  
     @Override
 //在这里关闭Dialog,否则容易造成内存泄漏
 protected void onPause() {
 super.onPause();
 mMyTask.dialogDismiss();
 }
 /**
 * 回调方法,更新UI
 * 这里如果在加载的过程中按下返回键返回主Activity时,会出现异常,
*setAdapter on a null object reference。因为activity被销毁,
 * 要解决这个问题,可以监听返回键事件做相应处理。
 */ 
    public void onTaskCompleted()  {  
        mDatas = mMyTask.getItems();  
        mAdapter = new ArrayAdapter(FixProblemsActivity.this,  android.R.layout.simple_list_item_1, mDatas);  
        setListAdapter(mAdapter);  
    }   
}  

onCreate中,如果没有开启任务(第一次进入),开启任务;如果已经开启了,调用setActivity(this);在onSaveInstanceState把当前任务加入Fragment,我设置了等待5秒,足够旋转三四个来回了,可以看到虽然在不断的重启,但是丝毫不影响加载数据任务的运行和加载框的显示

效果图:

android执行耗时操作,屏幕旋转怎么办_第1张图片
效果图.gif

可以看到我在加载的时候就丧心病狂的旋转屏幕,但是丝毫不影响显示效果与任务的加载。
  最后,说明一下,其实不仅是屏幕旋转需要保存数据,当用户在使用你的app时,忽然接到一个来电,长时间没有回到你的app界面也会造成Activity的销毁与重建,所以一个行为良好的App,是有必要拥有恢复数据的能力的。

你可能感兴趣的:(android执行耗时操作,屏幕旋转怎么办)