Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案

本文主要是介绍Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  • 初始化数据

*/

private void initData(Bundle savedInstanceState)

{

if (savedInstanceState != null)

mDatas = savedInstanceState.getStringArrayList(“mDatas”);

if (mDatas == null)

{

mLoadingDialog = new LoadingDialog();

mLoadingDialog.show(getFragmentManager(), “LoadingDialog”);

mLoadDataAsyncTask = new LoadDataAsyncTask();

mLoadDataAsyncTask.execute();

} else

{

initAdapter();

}

}

/**

  • 初始化适配器

*/

private void initAdapter()

{

mAdapter = new ArrayAdapter(

SavedInstanceStateUsingActivity.this,

android.R.layout.simple_list_item_1, mDatas);

setListAdapter(mAdapter);

}

@Override

protected void onRestoreInstanceState(Bundle state)

{

super.onRestoreInstanceState(state);

Log.e(TAG, “onRestoreInstanceState”);

}

@Override

protected void onSaveInstanceState(Bundle outState)

{

super.onSaveInstanceState(outState);

Log.e(TAG, “onSaveInstanceState”);

outState.putSerializable(“mDatas”, mDatas);

}

/**

  • 模拟耗时操作

  • @return

*/

private ArrayList generateTimeConsumingDatas()

{

try

{

Thread.sleep(2000);

} catch (InterruptedException e)

{

}

return new ArrayList(Arrays.asList(“通过Fragment保存大量数据”,

“onSaveInstanceState保存数据”,

“getLastNonConfigurationInstance已经被弃用”, “RabbitMQ”, “Hadoop”,

“Spark”));

}

p​
rivate class LoadDataAsyncTask extends AsyncTask<Void, Void, Void>

{

@Override

protected Void doInBackground(Void… params)

{

mDatas = generateTimeConsumingDatas();

return null;

}

@Override

protected void onPostExecute(Void result)

{

mLoadingDialog.dismiss();

initAdapter();

}

}

@Override

protected void onDestroy()

{

Log.e(TAG, “onDestroy”);

super.onDestroy();

}

}

界面为一个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:

package com.example.zhy_handle_runtime_change;

import android.app.Fragment;

import android.graphics.Bitmap;

import android.os.Bundle;

public class RetainedFragment extends Fragment

{

// data object we want to retain

private Bitmap 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(Bitmap data)

{

this.data = data;

}

public Bitmap getData()

{

return data;

}

}

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

然后是:FragmentRetainDataActivity

package com.example.zhy_handle_runtime_change;

import android.app.Activity;

import android.app.DialogFragment;

import android.app.FragmentManager;

import android.graphics.Bitmap;

import android.graphics.Bitmap.Config;

import android.os.Bundle;

import android.util.Log;

import android.widget.ImageView;

import com.android.volley.RequestQueue;

import com.android.volley.Response;

import com.android.volley.toolbox.ImageRequest;

import com.android.volley.toolbox.Volley;

public class FragmentRetainDataActivity extends Activity

{

private static final String TAG = “FragmentRetainDataActivity”;

private RetainedFragment dataFragment;

private DialogFragment mLoadingDialog;

private ImageView mImageView;

private Bitmap mBitmap;

@Override

public void onCreate(Bundle savedInstanceState)

{

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

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

fm.beginTransaction().add(dataFragment, “data”).commit();

}

mBitmap = collectMyLoadedData();

initData();

// the data is available in dataFragment.getData()

}

/**

  • 初始化数据

*/

private void initData()

{

mImageView = (ImageView) findViewById(R.id.id_imageView);

if (mBitmap == null)

{

mLoadingDialog = new LoadingDialog();

mLoadingDialog.show(getFragmentManager(), “LOADING_DIALOG”);

RequestQueue newRequestQueue = Volley

.newRequestQueue(FragmentRetainDataActivity.this);

ImageRequest imageRequest = new ImageRequest(

“https://img-my.csdn.net/uploads/201407/18/1405652589_5125.jpg”,

new Response.Listener()

{

@Override

public void onResponse(Bitmap response)

{

mBitmap = response;

mImageView.setImageBitmap(mBitmap);

// load the data from the web

dataFragment.setData(mBitmap);

mLoadingDialog.dismiss();

}

}, 0, 0, Config.RGB_565, null);

newRequestQueue.add(imageRequest);

} else

{

mImageView.setImageBitmap(mBitmap);

}

}

@Override

public void onDestroy()

{

Log.e(TAG, “onDestroy”);

super.onDestroy();

// store the data in the fragment

dataFragment.setData(mBitmap);

}

private Bitmap collectMyLoadedData()

{

return dataFragment.getData();

}

}

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

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

效果图:

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

在menifest中进行属性设置:

<activity

android:name=“.ConfigChangesTestActivity”

android:configChanges=“screenSize|orientation” >

低版本的API只需要加入orientation,而高版本的则需要加入screenSize。

ConfigChangesTestActivity

package com.example.zhy_handle_runtime_change;

import java.util.ArrayList;

import java.util.Arrays;

import android.app.DialogFragment;

import android.app.ListActivity;

import android.content.res.Configuration;

import android.os.AsyncTask;

import android.os.Bundle;

import android.util.Log;

import android.widget.ArrayAdapter;

import android.widget.ListAdapter;

import android.widget.Toast;

/**

  • @author zhy

*/

public class ConfigChangesTestActivity extends ListActivity

{

private static final String TAG = “MainActivity”;

private ListAdapter mAdapter;

private ArrayList mDatas;

private DialogFragment mLoadingDialog;

private LoadDataAsyncTask mLoadDataAsyncTask;

@Override

public void onCreate(Bundle savedInstanceState)

{

super.onCreate(savedInstanceState);

Log.e(TAG, “onCreate”);

initData(savedInstanceState);

}

/**

  • 初始化数据

*/

private void initData(Bundle savedInstanceState)

{

mLoadingDialog = new LoadingDialog();

mLoadingDialog.show(getFragmentManager(), “LoadingDialog”);

mLoadDataAsyncTask = new LoadDataAsyncTask();

mLoadDataAsyncTask.execute();

}

/**

  • 初始化适配器

*/

private void initAdapter()

{

mAdapter = new ArrayAdapter(ConfigChangesTestActivity.this,

android.R.layout.simple_list_item_1, mDatas);

setListAdapter(mAdapter);

}

/**

  • 模拟耗时操作

  • @return

*/

private ArrayList generateTimeConsumingDatas()

{

try

{

Thread.sleep(2000);

} catch (InterruptedException e)

{

}

return new ArrayList(Arrays.asList(“通过Fragment保存大量数据”,

“onSaveInstanceState保存数据”,

“getLastNonConfigurationInstance已经被弃用”, “RabbitMQ”, “Hadoop”,

“Spark”));

}

/**

  • 当配置发生变化时,不会重新启动Activity。但是会回调此方法,用户自行进行对屏幕旋转后进行处理

*/

@Override

public void onConfigurationChanged(Configuration newConfig)

{

super.onConfigurationChanged(newConfig);

if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE)

{

Toast.makeText(this, “landscape”, Toast.LENGTH_SHORT).show();

} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT)

{

Toast.makeText(this, “portrait”, Toast.LENGTH_SHORT).show();

}

}

private class LoadDataAsyncTask extends AsyncTask<Void, Void, Void>

{

@Override

protected Void doInBackground(Void… params)

{

mDatas = generateTimeConsumingDatas();

return null;

}

@Override

protected void onPostExecute(Void result)

{

mLoadingDialog.dismiss();

initAdapter();

}

}

@Override

protected void onDestroy()

{

Log.e(TAG, “onDestroy”);

super.onDestroy();

}

}

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

效果图:

6、旋转屏幕的最佳实践

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

首先说一下探索过程:

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

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

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

OtherRetainedFragment

package com.example.zhy_handle_runtime_change;

import android.app.Fragment;

import android.os.Bundle;

/**

  • 保存对象的Fragment

  • @author zhy

*/

public class OtherRetainedFragment 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;

}

}

和上面的差别不大,唯一不同的就是它要保存的对象编程一个异步的任务了,相信看到这,已经知道经常上述问题的一个核心了,保存一个异步任务,在重启时,继续这个任务。

package com.example.zhy_handle_runtime_change;

import java.util.ArrayList;

import java.util.Arrays;

import java.util.List;

import android.os.AsyncTask;

public class MyAsyncTask extends AsyncTask<Void, Void, Void>

{

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

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

} catch (InterruptedException e)

{

}

return new ArrayList(Arrays.asList(“通过Fragment保存大量数据”,

“onSaveInstanceState保存数据”,

“getLastNonConfigurationInstance已经被弃用”, “RabbitMQ”, “Hadoop”,

“Spark”));

}

/**

  • 设置Activity,因为Activity会一直变化

  • @param activity

*/

public void setActivity(FixProblemsActivity activity)

{

// 如果上一个Activity销毁,将与上一个Activity绑定的DialogFragment销毁

if (activity == null)

{

mLoadingDialog.dismiss();

}

// 设置为当前的Activity

this.activity = activity;

// 开启一个与当前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提供回调。当然了,运行过程中Activity不断的重启,我们也提供了setActivity方法,onDestory时,会setActivity(null)防止内存泄漏,同时我们也会关闭与其绑定的加载框;当onCreate传入新的Activity时,我们会在再次打开一个加载框,当然了因为屏幕的旋转并不影响加载的数据,所有后台的数据一直继续在加载。是不是很完美~~

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取
y

*/

public void setActivity(FixProblemsActivity activity)

{

// 如果上一个Activity销毁,将与上一个Activity绑定的DialogFragment销毁

if (activity == null)

{

mLoadingDialog.dismiss();

}

// 设置为当前的Activity

this.activity = activity;

// 开启一个与当前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提供回调。当然了,运行过程中Activity不断的重启,我们也提供了setActivity方法,onDestory时,会setActivity(null)防止内存泄漏,同时我们也会关闭与其绑定的加载框;当onCreate传入新的Activity时,我们会在再次打开一个加载框,当然了因为屏幕的旋转并不影响加载的数据,所有后台的数据一直继续在加载。是不是很完美~~

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

[外链图片转存中…(img-liDIMz3V-1719079759321)]一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取

这篇关于Android 屏幕旋转 处理 AsyncTask 和 ProgressDialog 的最佳方案的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1086589

相关文章

resultMap如何处理复杂映射问题

《resultMap如何处理复杂映射问题》:本文主要介绍resultMap如何处理复杂映射问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录resultMap复杂映射问题Ⅰ 多对一查询:学生——老师Ⅱ 一对多查询:老师——学生总结resultMap复杂映射问题

在Android平台上实现消息推送功能

《在Android平台上实现消息推送功能》随着移动互联网应用的飞速发展,消息推送已成为移动应用中不可或缺的功能,在Android平台上,实现消息推送涉及到服务端的消息发送、客户端的消息接收、通知渠道(... 目录一、项目概述二、相关知识介绍2.1 消息推送的基本原理2.2 Firebase Cloud Me

Java图片压缩三种高效压缩方案详细解析

《Java图片压缩三种高效压缩方案详细解析》图片压缩通常涉及减少图片的尺寸缩放、调整图片的质量(针对JPEG、PNG等)、使用特定的算法来减少图片的数据量等,:本文主要介绍Java图片压缩三种高效... 目录一、基于OpenCV的智能尺寸压缩技术亮点:适用场景:二、JPEG质量参数压缩关键技术:压缩效果对比

使用Python实现一键隐藏屏幕并锁定输入

《使用Python实现一键隐藏屏幕并锁定输入》本文主要介绍了使用Python编写一个一键隐藏屏幕并锁定输入的黑科技程序,能够在指定热键触发后立即遮挡屏幕,并禁止一切键盘鼠标输入,这样就再也不用担心自己... 目录1. 概述2. 功能亮点3.代码实现4.使用方法5. 展示效果6. 代码优化与拓展7. 总结1.

SpringBoot首笔交易慢问题排查与优化方案

《SpringBoot首笔交易慢问题排查与优化方案》在我们的微服务项目中,遇到这样的问题:应用启动后,第一笔交易响应耗时高达4、5秒,而后续请求均能在毫秒级完成,这不仅触发监控告警,也极大影响了用户体... 目录问题背景排查步骤1. 日志分析2. 性能工具定位优化方案:提前预热各种资源1. Flowable

Python FastAPI+Celery+RabbitMQ实现分布式图片水印处理系统

《PythonFastAPI+Celery+RabbitMQ实现分布式图片水印处理系统》这篇文章主要为大家详细介绍了PythonFastAPI如何结合Celery以及RabbitMQ实现简单的分布式... 实现思路FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理完整

C#使用SQLite进行大数据量高效处理的代码示例

《C#使用SQLite进行大数据量高效处理的代码示例》在软件开发中,高效处理大数据量是一个常见且具有挑战性的任务,SQLite因其零配置、嵌入式、跨平台的特性,成为许多开发者的首选数据库,本文将深入探... 目录前言准备工作数据实体核心技术批量插入:从乌龟到猎豹的蜕变分页查询:加载百万数据异步处理:拒绝界面

Android中Dialog的使用详解

《Android中Dialog的使用详解》Dialog(对话框)是Android中常用的UI组件,用于临时显示重要信息或获取用户输入,本文给大家介绍Android中Dialog的使用,感兴趣的朋友一起... 目录android中Dialog的使用详解1. 基本Dialog类型1.1 AlertDialog(

Spring Boot 配置文件之类型、加载顺序与最佳实践记录

《SpringBoot配置文件之类型、加载顺序与最佳实践记录》SpringBoot的配置文件是灵活且强大的工具,通过合理的配置管理,可以让应用开发和部署更加高效,无论是简单的属性配置,还是复杂... 目录Spring Boot 配置文件详解一、Spring Boot 配置文件类型1.1 applicatio

Java进行文件格式校验的方案详解

《Java进行文件格式校验的方案详解》这篇文章主要为大家详细介绍了Java中进行文件格式校验的相关方案,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、背景异常现象原因排查用户的无心之过二、解决方案Magandroidic Number判断主流检测库对比Tika的使用区分zip