Activity生命周期管理

Creating a Fragment

你可以认为fragment是activity的模块化组件,它拥有自己的生命周期,接受它自己的输入事件,你也可以在运行activity的时候添加或者移除它(有点像“子activity”你可以在不同的activity中重用) 这节课演示怎么样使用Support Library继承Fragment类,如此你的app(应用)就能与运行android1.6老版本的系统设备兼容 。

注意:如果你因为一些其他原因决定你的app需要的最低API版本为11或者更高,那你就没必要使用Support Library,而可以直接使用框架内置的Fragment类和相关API,要知道这节课主要关注于使用来自Support Library的API,使用特定包名下的API跟内置在平台(API)的还是略有不同的

创建Fragment类 (Create a Fragment Class)

要创建一个fragment需要继承Fragment类,然后重写关键的生命周期方法插入你自己的应用逻辑,操作的方式跟创建一个Activity类似。 不同的是当你创建一个Fragment时,你必须使用onCreateView()回调去定义你的布局,事实上,这是唯一你需要让fragment获得运行的回调函数。 例如:这里有一个简单的指定了自己布局的fragment:

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.ViewGroup;
 
public class ArticleFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
        Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.article_view, container, false);
    }
}
 

就跟activity一样,fragment也应该要实现其他的生命周期方法来管理它的状态,当fragment被activity添加或者删除,当activity生命周期状态转换时(类似这种操作都会影响到fragment的状态); 例如:当activity的onPuase()方法被调用的时候,所有activity中的fragment也都会接收到onPause()方法的调用。

更多关于fragment生命周期和回调函数的方法都可以在Fragment 开发指南中找到。

使用XML添加Fragment到Activity (Add a Fragment to an Activity using XML)

fragment是可重用的,模块化的UI组件,每一个Fragment实例必须与父类FragmentActivity相关联,你可以通过在你 Activity布局XML文件中定义每一个fragment来获得关联。 注意:FragmentActivity是在系统版本低于API level 11时由Support Library提供用来管理fragment的特殊activity,如果你支持的最低系统版本是API level 11或者更高,那你可以直接使用常规的Activity。 以下是一个例子:当设备屏幕被认为“大”的时候,一个布局文件添加了两个fragment到activity 译者注:当屏幕比较大的时候(比如平板)是可以同时显示两个fragment的,但是屏幕比较小(比如普通手机)同一时间只能显示一个fragment, 这是由于它们的屏幕尺寸造成的,后续的课程也会提到这个 这个布局文件被指定在“高”分辨率的目录名下。(译者注:请注意下面xml的目录结构:是在res目录下的layout-large目录下,这样的目录下 存放的文件通常都是用来支持高分辨率的布局文件)

res/layout-large/news_articles.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
 
    <fragment android:name="com.example.android.fragments.HeadlinesFragment"
              android:id="@+id/headlines_fragment"
              android:layout_weight="1"
              android:layout_width="0dp"
              android:layout_height="match_parent" />
 
    <fragment android:name="com.example.android.fragments.ArticleFragment"
              android:id="@+id/article_fragment"
              android:layout_weight="2"
              android:layout_width="0dp"
              android:layout_height="match_parent" />
 
</LinearLayout>

注:更多关于为不同屏幕大小创建布局,请参照Supporting Different Screen Sizes(支持不同的屏幕大小)

这里列举了在activity中怎样应用布局:

import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
 
public class MainActivity extends FragmentActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.news_articles);
    }
}

暂停和恢复Activity

在应用程序正常时,当前的Activity有时会因为其他可视化组件阻塞(obstructed)而导致Activity暂停。例如,当打开一个半 透明(semi-transparent)的活动(比如,对话框),先前的Activity就会暂停。 只要这个Activity仍然是部分可见,它就一直处于暂停状态。

然而,一旦先前的Activity被完全阻塞而不可见,它就进入停止状态 (这是在下一节中讨论)。

当Activity进入暂停状态,系统调用Activity中的onPause()方法,这个方法会停止当前正在进行的操作,比如暂停视频 播放,同时保存那些有可能需要长期保存的信息。如果用户返回处于暂停状态的Activity,系统会恢复那些数据并执行onResume()方法。

Note:当你的Activity接受到调用onPause()的信息时,那可能意味者activity将被暂停一段时间,用户可能会返回到你的activity。但是,那也是用户要离开你的activtiy的第一个信号。

Figure 1. 如图1所示,当一个半透明的activity阻塞你的activity时,系统会调用onPause()方法并且这个activity会停留在 Paused state (1). 如果用户在这个activity还是在暂停状态时回到这个activity,系统则会调用它的onResume()方法(2).

暂停Activity

当系统调用一个activity中的onPause()方法, 从技术上讲意味着这个activity仍然处于部分可见的状态,但是大多数时候,那意味着用户正在离开这个activity并很快会进入停止状态. 通常应该在onPause()回调方法里面完成以下操作:

  • 停止会耗费CPU的动画或者是其他正在运行的操作。
  • 提交没有保存的改变,但是仅仅是在用户离开时期待保存的内容(例如邮件草稿)。
  • 释放系统资源,例如广播接收器, 传感器(如GPS), 或者是其他任何会影响到电量的资源。


例如, 如果你的程序使用照相机, onPause()会是一个做那些释放资源的操作的好去处。java 代码如下:

@Override
   public void onPause() {
       super.onPause();  // 首先调用父类的方法
 
       // 释放照相机,当处于暂停时用不到并且别的Activity可能会用到它
       if (mCamera != null) {
           mCamera.release()
           mCamera = null;
       }
   }

通常,不能使用onPause()来保存用户改变的数据 (例如填入表格中的个人信息) 到永久磁盘上。仅仅确认用户期待那些改变能够被自动保存的时候(例如书写邮件草稿时)才可以把那些数据存到永久磁盘上。然而,应该避免在 onPause()时执行CPU密集的工作,例如写数据到数据库,因为它会导致切换到下一个activity变得缓慢(那些重负荷的操作应该放到 onStop()方法中完成)。

如果activity实际上是要被Stop,为了切换的顺畅应该减少在OnPause()方法里面的工作量。

Note:当Activity处于暂停状态,Activity实例是驻留在内存中的,并且在Activity 恢复的时候重新调用。因而不需要在恢复到Resumed状态的一系列回调方法中重新初始化组件。

恢复Activity

当用户把一个Activity从Paused状态恢复,系统会调用onResume()方法。

需要注意的是,系统每次调用这个方法对应的activity都处于最前台,包括第一次创建的时候。所以,在实现onResume()方法时 应该初始化那些在onPause方法里面释放掉的组件,并执行那些activity每次进入恢复状态都需要的初始化动作 (例如开始动画与初始化那些只有在获取用户焦点时才需要的组件)。

下面的onResume()例子是与上面onPause()例子相对应的,所以要初始化在暂停时释放的照相机。

@Override
   public void onResume() {
      super.onResume();  // 首先调用父类的方法
 
      // activity获取用户焦点时初始化相机实例
      if (mCamera == null) {
        initializeCamera(); // 相机初始化的本地操作
    }
}

停止与重启Activity

在activity生命周期中,恰当的停止与重启activity是很重要的,这样能确保用户感知到程序的存在并不会丢失他们的进度。在下面一些关键的场景中会涉及到停止与重启:

  • 用户打开“最近使用的程序(Recent Apps)”的菜单并从当前app切换到另外一个app,这个时候先前的app是被停止的。如果用

户通过“主屏幕加载图标(Home screen launcher icon”或“最近使用的程序(Recent Apps)”重新回到这个app,则activity会重启。

  • 用户在当前的app里面执行启动一个新的activity的操作时,当前activity会在第二个activity被创建后停止。如果用户点

击back按钮,之前的activtiy会被重启。

  • 用户在运行app时接受到一个来电通话。

Activity类提供了onStop()方法与onRestart()方法用于在activity停止与重启时进行调用。和暂停状态时部分阻塞用户接口不同,停止状态时UI不可见并且用户的焦点转移到另一个activity中。

注意:因为系统在Activity停止时会在内存中保存了Activity实例,所以很多时候不需要实现onStop()方法与 onRestart()方法,甚至是onStart()方法。 因为大多数的Activity相对比较简单,Activity会自动正常停止与重启,只需要使用onPause()来停止正在运行的动作并断开系统资源链接。

[图1]

如图1所示:当用户离开Activity时系统会调用onStop()来停止Activity (1)。这个时候如果用户返回,系统会调用onRestart()(2), 紧接着会调用onStart()(3)与onResume()(4)。需要注意的是:无论什么原因导致Activity停止,系统总是会在onStop()之前调用 onPause()方法。


停止Activity

当activity调用onStop()方法时,该activity不再可见并且应该释放所有不再需要的资源。一旦activity停止了,系统可 能会摧毁activity的实例以回收内存,甚至,系统会不执行activity的onDestroy()回调方法而直接杀死你的app进程, 因此需要使用onStop()来释放资源,从而避免内存泄漏。

尽管onPause()方法是在onStop()之前调用,通常应该使用onStop()来执行CPU密集型的关闭操作,例如把数据写入数据库。

例如,下面是一个在onStop()的方法里面保存笔记草稿到永久存储介质的示例:

@Override
protected void onStop() {
    super.onStop();  // Always call the superclass method first
 
    // Save the note's current draft, because the activity is stopping
    // and we want to be sure the current note progress isn't lost.
    ContentValues values = new ContentValues();
    values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText());
    values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle());
 
    getContentResolver().update(
            mUri,    // The URI for the note to update.
            values,  // The map of column names and new values to apply to them.
            null,    // No SELECT criteria are used.
            null     // No WHERE columns are used.
            );
}

当Activity已经停止,Activity对象会保存在内存中,并且在Activity恢复的时候被重新调用到。不需要在恢复到Resumed state状态前重新初始化那些被保存在内存中的组件。系统同样保存了每一个在布局中的视图的当前状态,如果用户在EditText组件中输入了文本,也 会被保存,因此不需要保存与恢复它。

注意:即使系统会在Activity停止的时候销毁这个Activity,系统仍然会保存视图对象(如文本编辑框中的文本)到一个 Bundle中,并且在用户返回这个Activity时恢复他们(下一节会介绍在Activity销毁与重建时如何使用Bundle来保存其他数据的状 态)。

启动与重启Activity

当Activity从Stopped状态回到前台时会调用onRestart(),系统再调用onStart()方法,onStart()方法在每 次Activity可见时都会被调用。onRestart()方法则是只在Activity从stopped状态恢复时才会被调用,因此可以使用它来执行 一些特殊的恢复工作,请注意之前是被stop而不是destrory。

使用onRestart()来恢复Activity状态并不常见,因此对于这个方法如何使用没有指导说明。但是,由于onStop()方法 要做清除所有Activity资源的操作,在重新启动Activtiy时需要重新实例化被清除的资源,同样,在Activity第一次创建时要实例化那些 资源。因为系统会在创建Activity与从停止状态重启Activity时都会调用onStart(),应该使用onStart()作为 onStop()所对应的方法。

例如:因为用户很可能在回到Activity之前需要过一段时间,所以onStart()方法是一个比较好的用来验证某些必须的功能是否已经准备好的地方。

@Override
protected void onStart() {
    super.onStart();  // Always call the superclass method first
 
    // The activity is either being restarted or started for the first time
    // so this is where we should make sure that GPS is enabled
    LocationManager locationManager = 
            (LocationManager) getSystemService(Context.LOCATION_SERVICE);
    boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
 
    if (!gpsEnabled) {
        // Create a dialog here that requests the user to enable GPS, and use an intent
        // with the android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS action
        // to take the user to the Settings screen to enable GPS when they click "OK"
    }
}
 
@Override
protected void onRestart() {
    super.onRestart();  // Always call the superclass method first
 
    // Activity being restarted from stopped state    
}

当系统Destory一个Activity,它会为Activity调用onDestroy()方法。由于会在onStop方法里面做释放资源的操作,而 onDestory方法则是最后去清除那些可能导致内存泄漏的地方,因此需要确保那些线程都被Destroy并且所有的操作都被停止。

重建Activity

有几个情况下Activity会由于正常的程序行为而被Destory,例如当用户点击返回按钮或者是Activity通过调用finish()发 出了停止信号。系统也有可能会在Activity处于stop状态且长时间不被使用、或在前台Activity需要更多系统资源的时候关闭后台进程以获取 更多的内存。

当Activity是因为用户点击Back按钮或者是Activity通过调用finish()结束自己时,系统就丢失了Activity 这个实例,因为前面的行为意味着不再需要这个Activity了。然而,如果因为系统资源紧张而导致Activity被Destory, 用户回到这个Activity时系统会有这个Activity存在过的记录,系统会使用那些保存的记录数据(描述了当Activity被Destory时 的状态)来重新创建一个新的Account实例。那些被系统用来恢复之前状态而保存的数据叫做"instance state" ,它是一些存放在Bundle对象中的键 值对.

警告:Activity会在每次旋转屏幕时被Destroy与Recreate。当屏幕改变方向时,系统会Destory与Recreate前台的Activity,因为屏幕配置被改变,相应的Activity可能需要加载一些可选的资源(例如layout).

默认情况下, 系统使用Bundle实例来保存每一个视图对象中的信息(例如输入EditText中的文本内容)。因此,如果Activity被Destroy与 Recreate, 那么layout的状态信息会自动恢复到之前的状态。然而,Activity也许存在更多需要恢复的状态信息,例如记录用户进程的成员变量。

为了可以保存额外更多的数据到saved instance state,在Activity的声明周期里面需要一个添加的回调函数onSaveInstanceState()。这个回调函数并没有在前面课程的图片 示例中显示。当用户离开Activity时,系统会调用它。当调用这个函数时,系统会在Activity被异常Destory时传递Bundle对象,从 而可以增加额外的信息到Bundle中并保存与系统中。然后如果系统在Activity被Destory之后想重新创建这个Activity实例时,之前 的那个Bundle对象会(系统)被传递到Activity的onRestoreInstanceState()方法与onCreate()方法中。

【图】

如图所示:当系统开始停止某个Activity时会调用onSaveInstanceState()(1) ,因此可以在Activity实例需要重新创建的情况下, 指定特定的附加状态数据到Bundle中。如果这个Activity被Destroy而且同样的实例被重新创建,系统会传递在 (1) 中的状态数据到 onCreate() (2) 与onRestoreInstanceState() (3).


保存Activity状态

当Activity开始Stop时,系统会调用onSaveInstanceState(),因此Activity可以用键值对的集合来保存状态信 息。这个方法会默认保存Activity视图的状态信息,例如在EditText组件中的文本或者是ListView的滑动位置。 为了给Activity保存额外的状态信息,必须实现onSaveInstanceState()并增加键值对到Bundle对象中,例如:

static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
...
 
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // Save the user's current game state
    savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
    savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);
 
    // Always call the superclass so it can save the view hierarchy state
    super.onSaveInstanceState(savedInstanceState);
}

警告:必须要调用onSaveInstanceState()方法的父类实现,这样默认的父类实现才能保存视图状态的信息。

恢复Activity状态

当Activity在被Destory后进行重建,可以从系统传递给Activity的Bundle中恢复保存的状态。onCreate()与onRestoreInstanceState()回调方法都接收到了同样的Bundle,里面包含了同样的实例状态信息。

因为onCreate()方法会在第一次创建新的Activity实例与重新创建之前被Destory的实例时都被调用,所以必须在尝试读 取Bundle对象前检查它是否为空。如果它为空,系统则是创建一个新的Activity实例,而不是恢复之前被Destory的Activity。 下面是一个示例:演示在onCreate方法里面恢复一些数据:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState); // Always call the superclass first
 
    // Check whether we're recreating a previously destroyed instance
    if (savedInstanceState != null) {
        // Restore value of members from saved state
        mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
        mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
    } else {
        // Probably initialize members with default values for a new instance
    }
    ...
}

警告:与前面的一样,总是需要调用onRestoreInstanceState()方法的父类实现,这样默认的父类实现才能保存视图状态的信息。

如果想了解更多关于运行时状态改变引起的recreate activity,请参考Handling Runtime Changes。

posted @ 2013-02-03 17:15  pockry  阅读(152)  评论(0)    收藏  举报