关于Activity的面试

来源:互联网 发布:蔡健雅 知乎 编辑:程序博客网 时间:2024/06/05 18:45

一:生命周期

二:各个周期切换

三:Activity重启时大量数据的恢复方法

四:如何安全退出Activity

tips:

onPause()跟onSavedInstanceState()的区别,都什么情况下调用

突然来电话时,onPause中需要做什么事情

一:Activity生命周期

首先上图:




nCreate:   在这里创建界面 ,做一些数据的初始化工作

  onStart:    到这一步变成用户可见不可交互 的
  onResume:   变成和用户可交互 的,(在activity 栈系统通过栈的方式管理这些个Activity的最上面,运行完弹出栈,则回到上一个Activity)
  onPause:     到这一步是可见但不可交互 的,系统会停止动画 等消耗CPU 的事情从上文的描述已经知道,应该在这里保存你的一些数据,因为这个时候你的程序的优先级降低,有可能被系统收回。在这里保存的数据,应该在onResume里读出来,注意:这个方法里做的事情时间要短,因为下一
个activity不会等到这个方法完成才启动
  onstop:     变得不可见 ,被下一个activity覆盖了
  onDestroy: 这是activity被干掉前最后一个被调用方法了,可能是外面类调用finish方法或者是系统为了节省空间将它暂时性的干掉,可以用isFinishing()来判断它,如果你有一个Progress Dialog在线程中转动,请在onDestroy里把他cancel掉,不然等线程结束的时候,调用Dialog的cancel方法会抛异常的。
  onPause,onstop, onDestroy,三种状态 下 activity都有可能被系统干掉
  为了保证程序的正确性,你要在onPause()里写上持久层操作的代码,将用户编辑的内容都保存到存储介质上(一般都是数据库)。实际工作中因为生命周期的变化而带来的问题也很多,比如你的应用程序起了新的线程在跑,这时候中断了,你还要去维护那个线程,是暂停还是杀掉还是数据回滚,是吧?因为Activity可能被杀掉,所以线程中使用的变量和一些界面元素就千万要注意了,一般我都是采用Android的消息机制 [Handler,Message]来处理多线程和界面交互的问题。

二:各个周期切换

1.启动Activity:系统会先调用onCreate方法,然后调用onStart方法,最后调用onResume,Activity进入运行状态。

2.当前Activity被其他Activity覆盖其上或被锁屏:系统会调用onPause方法,暂停当前Activity的执行。

3.当前Activity由被覆盖状态回到前台或解锁屏:系统会调用onResume方法,再次进入运行状态。

4.当前Activity转到新的Activity界面或按Home键回到主屏,自身退居后台:系统会先调用onPause方法,然后调用onStop方法,进入停滞状态。

5.用户后退回到此Activity:系统会先调用onRestart方法,然后调用onStart方法,最后调用onResume方法,再次进入运行状态。

6.当前Activity处于被覆盖状态或者后台不可见状态,即第2步和第4步,系统内存不足,杀死当前Activity,而后用户退回当前Activity:再次调用onCreate方法、onStart方法、onResume方法,进入运行状态。

7.用户退出当前Activity:系统先调用onPause方法,然后调用onStop方法,最后调用onDestory方法,结束当前Activity。


三:Activity重启时大量数据的恢复方法

某些设备配置能够在运行期间进行改变(如屏幕的方向、键盘可用性、语言等)。当这样的改变发生时,Android会重启正在运行的 Activity(onDestroy()回调之后,紧跟着调用onCreate()回调方法)。设计这种重启行为有助于应用程序通过重启,重新载入跟新 设备配置相匹配的可选资源。

要正确的处理重启,重要的是要恢复Activity之前的生存周期状态,因此在Activity被销毁之前,Android会调用 onSaveInstanceState()回调方法来保存应用程序相关的状态数据。这样在Activity的onCreate()或 onRestoreInstanceState()被调用期间就可以恢复之前的状态了。

要测试应用程序重启后的状态的完整性,就应该在程序运行时改变设备配置(如改变屏幕方向)。应用程序应该在处理配置改变、电话接入等事件的任何时候,都能够重启而且不丢失用户数据和状态。要了解Activity状态是如何被恢复的,请阅读Activity的生存周期。

但是,可能会遇到在重启应用程序时要恢复大量数据的情况,这时可能会给用户带来不好的用户体验。这样情况,有两个选择:

1.  在配置改变期间保留一个对象。

当配置改变时,允许Activity重启,但是要把状态对象携带给Activity新的实例。

2. 自行处理配置的变化

在某个配置改变期间,防止系统重启Activity,但是要在配置改变时接收一个回调方法,以便在必要时手动的更新Activity。

 

在配置变化期间保留一个对象

如果在重启Activity时,要求恢复大数据集、重建网络连接、或执行其他的敏感操作,那么由于配置的改变完全重启会降低用户体验。此外,系统也 不可能用onSaveInstanceState()回调方法的Bundle对象来完整的保存要恢复的数据,因为Bundle对象没有被设计用来携带大对 象(如位图)和大数据,这样导致Bundle对象在系列化和随后的反系列化时要占用大量的内存,从而导致配置改变缓慢。这种情况下,可以选择保留一个状态 对象来减轻恢复Activity重启时的负担。

保留运行期间配置改变对象的方法如下:

1.  重写onRetainNonConfigurationInstance()回调方法,它会返回希望保留的对象。

2.  在Activity被重建时,调用getLastNonConfigurationInstance()方法来恢复这个对象。

当Android系统由于配置的变化关掉Activity时,它会在onStop()回调和onDestroy()之间调用 onRetainNonConfigurationInstance()回调方法。在 onRetainNonConfigurationInstance()回调的实现中,为了在配置变化之后能够有效的恢复状态,它可以返回任意任何需要的 对象。

如果应用程序要从网络上加载数据,这样做就很有价值。如果用户改变了设备的方向,并且Activity重启了,应用程序就必须重新获取数据,这样会 很慢。因此可以实现onRetainNonConfigurationInstance()方法,让它返回一个带有数据的对象,然后再Activity被 重启时,再用getLastNonConfigurationInstance()方法来获取这个被保留的对象,例如:

@Override
public Object onRetainNonConfigurationInstance() {
    final MyDataObject data = collectMyLoadedData();
    return data;
}

这个方法最大的好处是:
    * 当Activity曾经通过某个资源得到一些图片或者信息,那么当再次恢复后,无需重新通过原始资源地址获取,可以快速的加载整个Activity状态信息。
    * 当Activity包含有许多线程时,在变化后依然可以持有原有线程,无需通过重新创建进程恢复原有状态。
    * 当Activity包含某些Connection Instance时,同样可以在整个变化过程中保持连接状态。

下边是需要特别注意的几点:
    * onRetainNonConfigurationInstance()在onSaveInstanceState()之后被调用。

    * 调用顺序同样介于onStop() 和 onDestroy()之间。

警告:在返回对象时,不要带有跟Activity绑定的对象,如Drawable、Adapter、View或其他跟Context关联的对象。如 果这样做了,会导致最初的Activity实例的所有View对象和资源泄漏(泄漏资源意味着应用程序占用了这些资源,因而不能被作为垃圾来回收,这样就 会导致大量的内存占用)。

Activity重启时,恢复数据的方法:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    final MyDataObject data = (MyDataObject) getLastNonConfigurationInstance();
    if (data == null) {
        data = loadMyData();
    }
    ...
}

这个例子中,getLastNonConfigurationInstance()方法返回被 onRetainNonConfigurationInstance()方法保存的数据。如果data对象是null(在由于配置改变以外的其他原因而导 致的Activity重启时,会发生这种情况),那么代码就会从初始资源中装载数据对象。

自行处理配置变化

如果在特定的配置变化期间,应用程序不需要更新资源,并且由于性能的限制,要求避免Activity的重启,那么可以声明让Activity自行处理配置的变化,从而防止系统重启Activity。

注意:自行处理配置的变化使得使用可选资源更加困难,因为系统不能自动的应用它们。这种技术应该在必须避免Activity重启(由于配置的变化)时,最后才考虑使用,并且大数应用程序不推荐使用。

要让Activity处理配置的变化,就要编辑清单文件中相应的<activity>元素,让它包含 android:configChanges属性,这个属性值代表了要处理的配置。可能的值在android:configChanges属性文档中被列 出(最常使用的值时“orientation”和“keyboardHidden”,orientation用于在屏幕方向变化时,防止Activity 的重启;keyboardHidden用于在键盘的可用性发生变化时,防止Activity重启)。通过用管道符“|”分离,可以给这个属性声明多个配置 值。

例如,下列清单代码就声明了一个自行处理屏幕方向变化和键盘可用性变化的Activity:

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

通过上述声明,当这些配置中其中之一发生变化时,MyActivity就不会重启了,相反,MyActivity会接收 onConfigurationChanged()方法的回调。这个方法被传入了一个Configuration对象,它指定了新的设备配置。通过读取 Configuration对象中的字段,就能够判断新的配置,并通过更新界面中使用的资源来进行对应的改变。在调用这个方法时,Activity的 Resources对象会被基于新配置所返回的资源更新,因此不用系统重启Activity,就能够容易的重设UI元素。

警告:从Android3.2(API级别13)开始,设备在纵向和横向之间切换时,“屏幕尺寸”也会发生改变。因此在使用API级别13或更高版 本开发时,如果想要在运行时防止由于方向的变化而导致的重启,就必须在android:configChanges的属性值中包含 “screenSize”。也就是说,必须声明android:configChanges=”orientation|screenSize”。但是, 如果应用的目标平台是API级别12或更低版本,那么Activity就会始终自行处理配置的变化(这样配置的变化就不会重启Activity,即使是在 Android3.2或更高版本的设备上运行,也不会重启Activity)。

例如,下面的onConfigurationChanged()方法实现了对当前设备方向的检查:

@Override
publicvoid 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();
    }elseif(newConfig.orientation ==Configuration.ORIENTATION_PORTRAIT){
        Toast.makeText(this,"portrait",Toast.LENGTH_SHORT).show();
    }
}

Configuration对象代表了所有的当前配置,不仅仅针对改变的那个配置。大多数时候,不必关注配置是如何变化的,只需把提供的可选资源重 新分配给正在处理的配置就可以了。例如,因为Resources对象的更新,就要用setImageResource()方法重设所有ImageView 对象,并且给新的配置使用适当的资源。

我们注意到,来自Configuration字段的值是整数,它要跟Configuration类中指定的常量进行匹配。关于这些常量的说明,请参考Configuration类说明。

记住:当声明了要Activity自行处理配置变化时,就要手动的负责重设Activity中的任何UI元素。如果声明了Activity要处理方 向的变化,并且在横向和纵向之间切换时有图标的变化,那么在onConfigurationChanged()执行期间,就应该重新分配每个资源。

如果不需要基于这些配置变化来更新应用程序,就不必实现onConfigurationChanged()方法。这样,所有的在配置变化之前使用的 资源会在变化之后依然使用,仅仅是避免了Activity的重启。但是,应用程序使用能够关闭并用之前完整的状态来重启,因此,在普通Activity的 生存期间不应该考虑使用这种技术,这不仅是因为这样不能阻止其他配置变化所导致的应用程序重启,而且也因为应该处理诸如用户离开应用程序、用户返回应用之 前获取被销毁的状态等事件。

关于在Activity中能够处理的配置变化的更多信息,请看android:configChanges文档和Configuration类。
四:如何安全退出Activity

我们在项目开发的时候可能会遇到安全退出应用的场景,如何能够安全退出多个Activity?网上有很多方法,如下:

1、抛异常退出

该方法通过抛异常,使程序Force Close。
验证可以,但是,需要解决的问题是,如何使程序结束掉,而不弹出Force Close的窗口。

2、记录打开的Activity

每打开一个Activity,就记录下来。在需要退出时,关闭每一个Activity即可。

3、发送特定广播

在需要结束应用时,发送一个特定的广播,每个Activity收到广播后,关闭即可。

4、递归退出

在打开新的Activity时使用startActivityForResult,然后自己加标志,在onActivityResult中处理,递归关闭。

第二种方法实现

定义一个Application类来存储Activity对象的引用

  1. package com.maso.wuye.activity;  
  2.   
  3. import java.util.LinkedList;  
  4. import java.util.List;  
  5.   
  6. import android.app.Activity;  
  7. import android.app.Application;  
  8.   
  9. public class ExitApplication extends Application {  
  10.   
  11.     private List<Activity> activityList = new LinkedList<Activity>();  
  12.     private static ExitApplication instance;  
  13.   
  14.     private ExitApplication() {  
  15.           
  16.     }  
  17.   
  18.     // 单例模式中获取唯一的ExitApplication实例  
  19.     public static ExitApplication getInstance() {  
  20.         if (null == instance) {  
  21.             instance = new ExitApplication();  
  22.         }  
  23.         return instance;  
  24.   
  25.     }  
  26.   
  27.     // 添加Activity到容器中  
  28.     public void addActivity(Activity activity) {  
  29.         activityList.add(activity);  
  30.     }  
  31.   
  32.     // 遍历所有Activity并finish  
  33.   
  34.     public void exit() {  
  35.   
  36.         for (Activity activity : activityList) {  
  37.             activity.finish();  
  38.         }  
  39.   
  40.         System.exit(0);  
  41.   
  42.     }  
  43. }  

注:Application类是为了那些需要保存全局变量设计的基本类,你可以在AndroidManifest.xml的<application>标签中进行自己的实现,这样的结果是:当你的application或者包被建立的时候将引起那个类被建立。就是说application是用来保存全局变量的,并且是在package创建的时候就跟着存在了。所以当我们需要创建全局变量的时候,不需 要再像j2se那样需要创建public权限的static变量,而直接在application中去实现。只需要调用Context的getApplicationContext或者Activity的getApplication方法来获得一个application对象,再做出相应 的处理。

然后在每个Activity的onCreate()方法中添加下面代码:

  1. ExitApplication.getInstance().addActivity(this);  



0 0