我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android屏幕旋转 处理Activity与AsyncTask的最佳解决方案

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android屏幕旋转 处理Activity与AsyncTask的最佳解决方案

一、概述

运行时变更就是设备在运行时发生变化(例如屏幕旋转、键盘可用性及语言)。发生这些变化,Android会重启Activity,这时就需要保存activity的状态及与activity相关的任务,以便恢复activity的状态。

为此,google提供了三种解决方案:

对于少量数据: 通过onSaveInstanceState(),保存有关应用状态的数据。 然后在 onCreate() 或 onRestoreInstanceState() 期间恢复 Activity 状态。 对于大量数据:用 Fragment 保留需要回复的对象。 自行处理配置变更,不重启Activity。

下面会逐一介绍三种情况,其实保存一些变量对象很简单,难的是当Activity创建异步线程去加载数据时,旋转屏幕时,怎么保存线程的状态。比如,在线程的加载过程中,旋转屏幕,就会存在问题:此时数据没有完成加载,onCreate重新启动时,会再次启动线程;而上个线程可能还在运行,并且可能会更新已经不存在的控件,造成错误。下面会一一解决这些问题。本文较长,主要是代码多,可以先下载demo,源码下载:http://download.csdn.net/detail/jycboy/9720486对比着看。

 二、使用onSaveInstanceState,onRestoreInstanceState​

代码如下:



public class SavedInstanceStateActivity extends ListActivity
{
 private static final String TAG = "MainActivity";
 private ListAdapter mAdapter;
 private ArrayList<String> 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)
 {
 if (savedInstanceState != null)
 mDatas = savedInstanceState.getStringArrayList("mDatas");
 if (mDatas == null)
 {
 mLoadingDialog = new LoadingDialog();
 mLoadingDialog.show(getFragmentManager(), "LoadingDialog");
 mLoadDataAsyncTask = new LoadDataAsyncTask();
 mLoadDataAsyncTask.execute();
 //mLoadDataAsyncTas
 } else
 {
 initAdapter();
 }
 }
 
 private void initAdapter()
 {
 mAdapter = new ArrayAdapter<String>(
 SavedInstanceStateActivity.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);
 }
 
 private ArrayList<String> generateTimeConsumingDatas()
 {
 try
 {
 Thread.sleep(3000);
 } catch (InterruptedException e)
 { e.printStackTrace();
 }
 return new ArrayList<String>(Arrays.asList("通过Fragment保存大量数据",
 "onSaveInstanceState保存数据",
 "getLastNonConfigurationInstance已经被弃用", "RabbitMQ", "Hadoop",
 "Spark"));
 }
 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();
 }
}

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

运行结果:

12-24 20:13:41.814 1994-1994/? E/MainActivity: onCreate
12-24 20:13:46.124 1994-1994/? E/MainActivity: onSaveInstanceState
12-24 20:13:46.124 1994-1994/? E/MainActivity: onDestroy
12-24 20:13:46.154 1994-1994/? E/MainActivity: onCreate
12-24 20:13:46.164 1994-1994/? E/MainActivity: onRestoreInstanceState

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

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

效果图:

三、使用Fragment保留对象,恢复数据

果重启 Activity 需要恢复大量数据、重新建立网络连接或执行其他密集操作,依靠系统通过onSaveInstanceState() 回调为您保存的 Bundle,可能无法完全恢复 Activity 状态,因为它并非设计用于携带大型对象(例如位图),而且其中的数据必须先序列化,再进行反序列化,这可能会消耗大量内存并使得配置变更速度缓慢。 在这种情况下,如果 Activity 因配置变更而重启,则可通过保留 Fragment 来减轻重新初始化 Activity 的负担。此片段可能包含对您要保留的有状态对象的引用。

当 Android 系统因配置变更而关闭 Activity 时,不会销毁您已标记为要保留的 Activity 的片段。 您可以将此类片段添加到 Activity 以保留有状态的对象。

要在运行时配置变更期间将有状态的对象保留在片段中,请执行以下操作:

扩展 Fragment 类并声明对有状态对象的引用。 在创建片段后调用 setRetainInstance(boolean)。 将片段添加到 Activity。 重启 Activity 后,使用 FragmentManager 检索片段。

例如,按如下方式定义片段:


public class RetainedFragment extends Fragment {
 // data object we want to retain
 private MyDataObject 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(MyDataObject data) {
 this.data = data;
 }
 public MyDataObject getData() {
 return data;
 }
}

注意:尽管您可以存储任何对象,但是切勿传递与 Activity 绑定的对象,例如,Drawable、Adapter、View 或其他任何与 Context 关联的对象。否则,它将使Activity无法被回收造成内存泄漏。(泄漏资源意味着应用将继续持有这些资源,但是无法对其进行垃圾回收,因此可能会丢失大量内存)

下面举一个实际的例子:

1.RetainedFragment


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

只是保持Bitmap对象的引用,你可以用Fragment保存多个对象。

2.FragmentRetainDataActivity:


public class FragmentRetainDataActivity extends Activity
{
 private static final String TAG = "FragmentRetainData";
 private RetainedFragment dataFragment;
 private DialogFragment mLoadingDialog;
 private ImageView mImageView;
 private Bitmap mBitmap;
 BitmapWorkerTask bitmapWorkerTask;
 @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();
 }
 // the data is available in dataFragment.getData()
 mBitmap = dataFragment.getData();
 initView();
 }
 
 private void initView()
 {
 mImageView = (ImageView) findViewById(R.id.id_imageView);
 if(mBitmap != null)
 mImageView.setImageBitmap(mBitmap);
 //图片为空时,加载图片;有时候即使dataFragment!=null时,图片也不一定就加载完了,比如在加载的过程中,旋转屏幕,此时图片就没有加载完
 else{
 mLoadingDialog = new LoadingDialog();
 mLoadingDialog.show(getFragmentManager(), "LOADING_DIALOG");
 bitmapWorkerTask = new BitmapWorkerTask(this);
 bitmapWorkerTask.execute("http://images2015.cnblogs.com/blog/747969/201612/747969-20161222164357995-1098775233.jpg");
 }
 }
 
 private static class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {
 //图片的URL地址
 private String imageUrl;
 //保存外部activity的弱引用
 private WeakReference<Context> weakReference;
 public BitmapWorkerTask(Context context) {
 weakReference = new WeakReference<>(context);
 }
 @Override
 protected Bitmap doInBackground(String... params) {
 imageUrl = params[0];
 //为了演示加载过程,阻塞2秒
 try
 {Thread.sleep(2000);
 } catch (InterruptedException e)
 { e.printStackTrace();
 }
 return downloadUrlToStream(imageUrl);
 }
 @Override
 protected void onPostExecute(Bitmap bitmap) {
 super.onPostExecute(bitmap);
 if(bitmap !=null){
 FragmentRetainDataActivity retainDataActivity= (FragmentRetainDataActivity) weakReference.get();
 //调用回调方法
 retainDataActivity.onLoaded(bitmap);
 }
 }
 
 private Bitmap downloadUrlToStream(String urlString) {
 HttpURLConnection urlConnection = null;
 Bitmap bitmap = null;
 try {
 final URL url = new URL(urlString);
 urlConnection = (HttpURLConnection) url.openConnection();
 if(urlConnection.getResponseCode()==HttpURLConnection.HTTP_OK){ //连接成功
  InputStream is = urlConnection.getInputStream();
  bitmap = BitmapFactory.decodeStream(is);
  is.close();
  return bitmap;
 }else{
  return null;
 }
 } catch (final IOException e) {
 e.printStackTrace();
 } finally {
 if (urlConnection != null) {
  urlConnection.disconnect();
 }
 }
 return null;
 }
 }
 //加载完毕的回掉
 public void onLoaded(Bitmap bitmap){
 mBitmap = bitmap;
 mLoadingDialog.dismiss();
 mImageView.setImageBitmap(mBitmap);
 // load the data from the web
 dataFragment.setData(mBitmap);
 Log.e(TAG, "onLoaded");
 }
 public void onPause(){
 super.onPause();
 Log.e(TAG, "onPause");
 if(getFragmentManager() != null && mLoadingDialog != null)
 mLoadingDialog.dismiss();
 }
 @Override
 public void onDestroy()
 {
 super.onDestroy();
 Log.e(TAG, "onDestroy");
 if(bitmapWorkerTask !=null)
 bitmapWorkerTask.cancel(true);
 // store the data in the fragment
 dataFragment.setData(mBitmap);
 }
}

这里边用BitmapWorkerTask异步下载图片,downloadUrlToStream封装了下载图片的代码;

BitmapWorkerTask用弱引用保持外部Activity对象防止内存泄漏,下载完毕后用onLoaded回调方法更新UI。

通过检查dataFragment、mBitmap判断是否已经加载过,加载过直接用就可以。

效果图:

在gif里可以看到,如果未加载完毕就旋转屏幕,它会重新启动异步线程去下载,这种效果并不好,我们会在最后解决这个问题。

四、自行处理配置变更

如果应用在特定配置变更期间无需更新资源,并且因性能限制您需要尽量避免重启,则可声明 Activity 将自行处理配置变更,这样可以阻止系统重启 Activity。

要声明由 Activity 处理配置变更,需设置清单文件manifest:


<activity android:name=".MyActivity" android:configChanges="orientation|keyboardHidden"
 android:label="@string/app_name">

"orientation" 和 "keyboardHidden",分别用于避免因屏幕方向和可用键盘改变而导致重启)。您可以在该属性中声明多个配置值,方法是用管道 | 字符分隔这些配置值。

注意:API 级别 13 或更高版本的应用时,若要避免由于设备方向改变而导致运行时重启,则除了 "orientation" 值以外,您还必须添加 "screenSize" 值。 也就是说,您必须声明 android:configChanges="orientation|screenSize"。

当其中一个配置发生变化时,MyActivity 不会重启。相反,MyActivity 会收到对onConfigurationChanged() 的调用。向此方法传递Configuration 对象指定新设备配置。您可以通过读取Configuration 中的字段,确定新配置,然后通过更新界面中使用的资源进行适当的更改。

例如,以下onConfigurationChanged() 实现检查当前设备方向:


@Override
public void onConfigurationChanged(Configuration newConfig) {
 super.onConfigurationChanged(newConfig);
 // Checks the orientation of the screen
 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();
 }
}

示例代码:


public class ConfigChangesTestActivity extends ListActivity
{
 private static final String TAG = "MainActivity";
 private ListAdapter mAdapter;
 private ArrayList<String> 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<String>(ConfigChangesTestActivity.this,
 android.R.layout.simple_list_item_1, mDatas);
 setListAdapter(mAdapter);
 }
 
 private ArrayList<String> generateTimeConsumingDatas()
 {
 try
 {
 Thread.sleep(2000);
 } catch (InterruptedException e)
 {
 }
 return new ArrayList<String>(Arrays.asList("通过Fragment保存大量数据",
 "onSaveInstanceState保存数据",
 "getLastNonConfigurationInstance已经被弃用", "RabbitMQ", "Hadoop",
 "Spark"));
 }
 
 @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。

但是自行处理配置变可能导致备用资源的使用更为困难,因此不到万不得已不用,大部分情况推荐用Fragment。

效果图:

五、旋转屏幕时如果任务线程未执行完,如何保存恢复

解决上边提到的问题,在未加载完毕的情况下,旋转屏幕,保存任务线程,重启activity不重新执行异步线程。那么他的难点就是保存任务线程,不销毁它,用上边三个方法都可以实现,推荐用1,2种方法。下边就主要介绍用第二种方法Fragment,那么你也肯定想到了,用Fragment保存AsyncTask就可以了。

代码如下:

1. OtherRetainedFragment



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

和之前差不多就是保存对象是MyAsyncTask。

2.MyAsyncTask:


public class MyAsyncTask extends AsyncTask<Void, Void, Void>
{
 //保存外部activity的弱引用
 private WeakReference<Context> weakReference;
 public MyAsyncTask(Context context) {
 weakReference = new WeakReference<>(context);
 }
 private FixProblemsActivity activity;
 
 private boolean isCompleted;
 
 private LoadingDialog mLoadingDialog;
 private List<String> items;
 
 @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;
 }
 
 @Override
 protected void onPostExecute(Void unused)
 {
 isCompleted = true;
 notifyActivityTaskCompleted();
 if (mLoadingDialog != null)
 mLoadingDialog.dismiss();
 }
 public List<String> getItems()
 {
 return items;
 }
 private List<String> loadingData()
 {
 try
 {
 Thread.sleep(5000);
 } catch (InterruptedException e)
 {
 }
 return new ArrayList<String>(Arrays.asList("通过Fragment保存大量数据",
 "onSaveInstanceState保存数据",
 "getLastNonConfigurationInstance已经被弃用", "RabbitMQ", "Hadoop",
 "Spark"));
 }
 
 public void setActivity(Context activity)
 { weakReference = new WeakReference<>(activity);
 // 设置为当前的Activity
 this.activity = (FixProblemsActivity) activity;
 // 开启一个与当前Activity绑定的等待框
 if (activity != null && !isCompleted)
 {
 mLoadingDialog = new LoadingDialog();
 mLoadingDialog.show(this.activity.getFragmentManager(), "LOADING");
 }
 // 如果完成,通知Activity
 if (isCompleted)
 {
 notifyActivityTaskCompleted();
 }
 }
 
 public void dialogDismiss(){
 if(mLoadingDialog != null){
 mLoadingDialog.dismiss();
 }
 }
 private void notifyActivityTaskCompleted()
 {
 if (null != activity)
 {
 activity.onTaskCompleted();
 }
 }
}
和之前一样保留Activity的虚引用,防止内存泄漏。 setActivity()方法用于任务未完成时,重启activity相应的创建一个新的dialog。 dialogDismiss用于在在Activity不可见时,关闭dialog,防止以前的dialog造成内存泄漏。 当任务完成时,调用activity的回调方法onTaskCompleted更新UI。

3.FixProblemsActivity


public class FixProblemsActivity extends ListActivity
{
 private static final String TAG = "MainActivity";
 private ListAdapter mAdapter;
 private List<String> mDatas;
 private OtherRetainedFragment 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 = (OtherRetainedFragment) 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 onDestroy()
 {
 Log.e(TAG, "onDestroy");
 super.onDestroy();
 }
 @Override
 //在这里关闭Dialog,否则容易造成内存泄漏
 protected void onPause() {
 super.onPause();
 mMyTask.dialogDismiss();
 }
 
 public void onTaskCompleted()
 {
 mDatas = mMyTask.getItems();
 mAdapter = new ArrayAdapter<String>(FixProblemsActivity.this,
 android.R.layout.simple_list_item_1, mDatas);
 setListAdapter(mAdapter);
 }
}

主要在onCreate方法中执行一些逻辑判断,如果没有开启任务(第一次进入),开启任务;如果已经开启了,调用setActivity(this);

在onPause中关闭dialog,防止内存泄漏。

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

效果图:

写着写着就这么晚了,本以为会写的很快。。中间Androidstudio的录制视频的功能还不好使啦,逼得我在手机上下了个。。。好分析到此结束。

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

https://developer.android.google.cn/guide/topics/resources/runtime-changes.html#HandlingTheChange

http://blog.csdn.net/lmj623565791/article/details/37936275

https://developer.android.google.cn/guide/components/activities.html

您可能感兴趣的文章:Android实现屏幕旋转方法总结Android开发 旋转屏幕导致Activity重建解决方法详解Android中Runtime解决屏幕旋转问题(推荐)Android webview旋转屏幕导致页面重新加载问题解决办法Android6.0 固定屏幕功能实现方法及实例Android6.0 屏幕固定功能详解详解Android权限管理之Android 6.0运行时权限及解决办法Android6.0动态申请权限所遇到的问题小结Android 6.0调用相机图册崩溃的完美解决方案Android适配安卓6.0蓝牙通讯实现过程Android 6.0权限申请详解及权限资料整理Android6.0开发中屏幕旋转原理与流程分析


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android屏幕旋转 处理Activity与AsyncTask的最佳解决方案

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android屏幕旋转 处理Activity与AsyncTask的最佳解决方案

一、概述 运行时变更就是设备在运行时发生变化(例如屏幕旋转、键盘可用性及语言)。发生这些变化,Android会重启Activity,这时就需要保存activity的状态及与activity相关的任务,以便恢复activity的状态。 为此,
2022-06-06

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录