【Android应用开发技术:应用组件】:Activity基本原理

来源:互联网 发布:老男孩linux运维视频 编辑:程序博客网 时间:2024/05/17 19:16

作者:郭孝星
微博:郭孝星的新浪微博
邮箱:allenwells@163.com
博客:http://blog.csdn.net/allenwells
Github:https://github.com/AllenWells

【Android应用开发技术:应用组件】章节列表

当用户进入、退出、回到App时,程序中的Activity实例都经历了生命周期中的不同状态。

  • 当Activity第一次启动的时候,它来到系统的前台,开始接受用户的焦点。在此期间,Android系统调用了一系列的生命周期中的方法。
  • 如果用户执行了启动另一个Activity或者切换到另一个App的操作,此时虽然当前Activity不可见,但其实例与数据仍然存在,系统又会调用一些生命周期中的方法。

在生命周期的回调方法里面,我们可以声明当用户离开或者重新进入这个Activity所需要执行的操作。

举例

Streaming Video Player进行播放时, 当用户切换到另外一个App的时候,应该暂停Video并终止网络连接。当用户返回时,重新建立网络连接并允许用户从同样的位置恢复播放。

在一个Activity的生命周期中,系统会像金字塔模型一样去调用一系列的生命周期回调方法。Activity生命周期的每一个阶段就像金字塔中的台阶。当系统创建了一个新的Activity实例,每一个回调函数会向上一阶移动Activity状态。处在金字塔顶端意味着当前Activity处在前台并且是用户可以与它进行交互的状态。当用户退出这个Activity时,为了回收这个Activity,系统会调用其它方法来向下一阶移动Activity状态。在某些情况下,Activity会隐藏在金字塔下等待,比如当用户切换到其他App,这个时候Activity可以重新回到顶端,如果用户回到这个Activity并且恢复用户离开时的状态。

Activity常见的状态如下所示:

  • Resumed:在这个状态,Activity处在前台,用户可以与它进行交互。(通常也被理解为”running” 状态)
  • Paused:在这个状态,Activity被另外一个Activity所遮盖:另外的Activity来到前台,但是半透明的,不会覆盖整个屏幕。被暂停的Activity不会再接受用户的输入且不会执行任何代码。这里的不会执行任何代码并不代表了任何后台线程都不会工作。
  • Stopped:在这个状态, Activity完全被隐藏,不被用户可见。可以认为是在后台。当stopped, Activity实例与它的所有状态信息都会被保留,但是Activity不能执行任何代码。其它状态 (Created与Started)都是短暂的,系统快速的执行那些回调函数并通过执行下一阶段的回调函数移动到下一个状态。也就是说,在系统调用onCreate(), 之后会迅速调用onStart(), 之后再迅速执行onResume()。上面就是基本的activity生命周期。

Activity的生命周期如下图所示:

这里写图片描述

根据Activity的复杂度,我们并不需要实现所有的生命周期方法。然而,我们需要知道每一个方法的功能,并确保App能够像用户期望的那样执行,如下所示:

  • 当使用你的App的时候,不会因为有来电通话或者切换到其他App而导致程序崩溃。
  • 当用户没有激活某个组件的时候不要消耗宝贵的系统资源。
  • 当离开App并且一段时间后返回,不要丢失用户的使用进度。
  • 当设备发送屏幕旋转的时候,应用不会崩溃或者丢失用户的使用进度。

下面我们来分别介绍Activity在不同时期的回调方法。

一 启动与销毁Activity

1.1 启动Activity

当用户从主界面点击你的程序图标时,系统会调用你的App里面定义的主Activity,主Activity的定义如下所示:

<activity android:name=".MainActivity" android:label="@string/app_name">    <intent-filter>        <action android:name="android.intent.action.MAIN" />        <category android:name="android.intent.category.LAUNCHER" />    </intent-filter></activity>

如果你的程序中没有一个Activity声明了MAIN action 或者LAUNCHER category,那么在设备的主界面列表里面不会呈现你的App图标。

大多数App都包括许多不同的Activity,使得用户可以执行不同的动作。不论这个Activity是当用户点击应用图标创建的主Activtiy还是为了响应用户行为而创建的其他Activity,系统都会调用新的Activity实例中的onCreate()方法。我们需要实现onCreate()方法来执行程序启动所需要的基本逻辑。

TextView mTextView; // Member variable for text view in the layout@Overridepublic void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    // Set the user interface layout for this Activity    // The layout file is defined in the project res/layout/main_activity.xmlfile    setContentView(R.layout.main_activity);    // Initialize member TextView so we can manipulate it later    mTextView = (TextView) findViewById(R.id.text_message);    // Make sure we're running on Honeycomb or higher to use ActionBar APIs    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {        // For the main activity, make sure the app icon in the action bar        // does not behave as a button        ActionBar actionBar = getActionBar();        actionBar.setHomeButtonEnabled(false);    }}

一旦结束onCreate()操作,系统会迅速调用onStart()与onResume()方法。Activity不会在Created或者Started状态停留。技术上来说, Activity在onStart()被调用后会开始被用户可见,但是onResume()会迅速被执行使得Activity停留在Resumed状态,直到一些因素发生变化才会改变这个状态。例如接受到一个来电,用户切换到另外一个Activity,或者是设备屏幕关闭。

onCreate(),onStart()和onResume()的执行流程如下所示:

这里写图片描述

当这三个顺序执行的回调函数完成后,Activity会到达Resumed状态。

1.2 销毁Activity

Activity的第一个生命周期回调函数是onCreate(),它最后一个回调是onDestroy(),当系统收到需要将该Activity彻底移除的信号时,系统会调用这个方法。

大多数App并不需要实现这个方法,因为局部类的references会随着Activity的销毁而销毁,并且Activity应该在onPause()与onStop()中执行清除activity资源的操作。然而,如果Activity含有在onCreate()调用时创建的后台线程,或者是其他有可能导致内存泄漏的资源,我们就需要在OnDestroy()时杀死后台线程,进行资源清理。

@Overridepublic void onDestroy() {    super.onDestroy();  // Always call the superclass    // Stop method tracing that the activity started during onCreate()    android.os.Debug.stopMethodTracing();}

系统通常是在执行了onPause()与onStop()之后再调用onDestroy(),但是如果Activity只是做了一个临时的逻辑跳转的功能,它只是用来决定跳转到哪一个Activity,这种情况下,我们需要在onCreate()里面去调用finish方法,这样系统会直接就调用onDestory()方法,其它生命周期的方法则不会被执行。

二 暂停与恢复Activity

在使用通常的App时,前面的Activity有时候会被其他可见的组件而阻塞(obstructed),这样会导致当前的Activity进入Pause状态。当打开一个半透明的activity时(例如以对话框的形式),之前的Activity会被暂停。 只要之前的Activity仍然被部分可见,这个Activity就会一直处于Paused状态。

一旦之前的Activity被完全阻塞并不可见,它则会进入Stop状态当Activity进入Paused状态,系统会调用Activity中的onPause()方法, 在这个方法里面可以执行停止目前正在运行的任务的操作,比如暂停视频播放或者是保存那些有可能需要长期保存的信息。如果用户从暂停状态回到当前Activity,系统应该恢复那些数据并执行onResume()方法。

onPause()和onPause()调用关系如下图所示:

这里写图片描述

当一个半透明的Activity阻塞activity时,系统会调用onPause()方法并且这个Activity会停留在Paused状态。如果用户在这个Activity还是在Paused状态时回到这个Activity,系统则会调用它的onResume()

2.1 暂停Activity

当系统调用Activity中的onPause(),从技术上讲,那意味着activity仍然处于部分可见的状态.但大多数时候,那意味着用户正在离开这个Activity并马上会进入Stopped state. 我们通常在onPause()回调方法里面做下面的事情:

  • 停止动画或者是其他正在运行的操作,那些都会导致CPU的浪费。
  • 提交没有保存的改变,但是仅仅是在用户离开时期待保存的内容,比如邮件草稿。
  • 释放系统资源,例如broadcast receivers, sensors (比如GPS), 或者是其他任何会影响到电量的资源。例如, 如果你的程序使用Camera,onPause()会是一个比较好的地方去做那些释放资源的操作。
@Overridepublic void onPause() {    super.onPause();  // Always call the superclass method first    // Release the Camera because we don't need it when paused    // and other activities might need to use it.    if (mCamera != null) {        mCamera.release()        mCamera = null;    }}

通常,不应该使用onPause()来保存用户改变的数据 (例如填入表格中的个人信息) 到永久存储(File或者DB)上。仅当确认用户期待那些改变能够被自动保存的时候,比如正在撰写邮件草稿,我们可以把那些数据存到永久存储。然而,我们应该避免在onPause()时执行CPU-Intensive的工作,例如写数据到DB,因为它会导致切换到下一个Activity变得缓慢,我们应该把那些heavy-load的工作放到onStop()去做。如果我们的Activity实际上是要被Stop,为了切换的顺畅,我们需要减少在OnPause()方法里面的工作量。

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

2.2 恢复Activity

当用户从Paused状态恢复Activity时,系统会调用onResume()方法。系统每次调用这个方法时,Activity都处于前台,包括第一次创建的时候。所以,我们需要实现onResume()来初始化那些在onPause方法里面释放掉的组件,并执行那些Activity每次进入Resumed state都需要的初始化动作,比如开始动画与初始化那些只有在获取用户焦点时才需要的组件。

@Overridepublic void onResume() {    super.onResume();  // Always call the superclass method first    // Get the Camera instance as the activity achieves full user focus    if (mCamera == null) {        initializeCamera(); // Local method to handle camera init    }}

三 停止与重启Activity

恰当的停止与重启Activity是很重要的,在Activity生命周期中,它们能确保用户感知到程序的存在并不会丢失他们的进度。

涉及到停止与重启的常见场景如下所示:

  • 用户打开最近使用App的菜单并切换你的App1到App2,此时App1是被停止的。如果用户通过手机主界面的启动程序图标或者最近使用程序的窗口回到App1,那么App1的Activity会重启。
  • 用户在App1里面执行启动一个新的Activity的操作,当前Activity会在第二个Activity被创建后stop。如果用户点击back按钮,第一个Activtiy会被重启。
  • 用户在使用App时接受到一个来电通话。

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

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

如下图所示:

这里写图片描述

当用户离开我们的Activity时,系统会调用onStop()来停止Activity**①. 这个时候如果用户返回,系统会调用onRestart(),之后会迅速调用onStart()与onResume()④**。

注意:无论什么原因导致activity停止,系统总是会在onStop()之前调用onPause()方法。

3.1 停止Activity

当Activity调用onStop()方法, Activity不再可见,并且应该释放那些不再需要的所有资源。一旦Activity停止了,系统会在不再需要这个Activity时摧毁它的实例,这个通常和栈结构有关,通常back操作会导致前一个activity被销毁。在极端情况下,系统会直接杀死App进程,并且不执行activity的onDestroy()回调方法, 因此我们需要调用onStop()来释放资源,从而避免内存泄漏。尽管onPause()方法是在onStop()之前调用,我们应该使用onStop()来执行那些CPU intensive的shut-down操作。

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

即使系统会在Activity Stop的时候停止这个Activity,它仍然会保存View对象的状态,比如EditText中的文字,到一个Bundle中,并且在用户返回这个Activity时恢复它们。

@Overrideprotected 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.            );}

3.2 重启activity

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

使用onRestart()来恢复Activity状态是不太常见的,因此对于这个方法如何使用没有任何的guidelines。然而,因为onStop()方法应该做清除所有Activity资源的操,在重新启动Activtiy时重新实例化那些被清除的资源。同样, 我们 也需要在activity第一次创建时实例化那些资源。介于以上原因,我们应该使用onStart()作为onStop()所对应方法。因为系统会在创建activity与从停止状态重启activity时都会调用onStart()。

总结来说:在onStop()里面做了哪些清除的操作就应该在onStart()里面重新把那些清除掉的资源重新创建出来。

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

@Overrideprotected 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"    }}@Overrideprotected void onRestart() {    super.onRestart();  // Always call the superclass method first    // Activity being restarted from stopped state}

四 重新创建Activity

Activity是由于正常的程序行为而被Destory,相关场景如下所示:

  • 当用户点击返回按钮或者是你的Activity通过调用finish()来发出停止信号。
  • 系统也有可能会在Activity处于stop状态且长时间不被使用或者是在前台Activity需要更多系统资源的时候把关闭后台进程,这样来获取更多的内存。
  • 屏幕旋转

在这些场景下,系统就丢失了对Activity实例的引用,因为前面的行为意味着不再需要这个Activity了。然而,如果因为系统资源紧张而导致Activity的Destory,系统会在用户回到这个Activity时有这个Activity存在过的记录,系统会使用那些保存的记录数据来重新创建一个新的Activity实例。

记录数据:描述了当Activity被Destory时的状态,被称为instance state,它是一些存放在Bundle对象中的key-value pairs。

举例

Activity会在每次旋转屏幕时被destroyed与recreated。当屏幕改变方向时,系统会Destory与Recreate前台的activity,因为屏幕配置被改变,你的Activity可能需要加载一些alternative的资源,比如layout,默认情况下, 系统使用Bundle实例来保存每一个View(视图)对象中的信息(例如输入EditText 中的文本内容)。因此,如果你的Activity被destroyed与recreated, 那么layout的状态信息会自动恢复到之前的状态。然而,你的activity也许存在更多你想要恢复的状态信息,例如记录用户Progress的成员变量(member variables)。

那么如何进行recreated

  1. 为了能使Android系统能够恢复Activity中的View的状态,每个View都必须有一个唯一ID,由android:id定
    义。
  2. 为了让你可以保存额外更多的数据到saved instance state。在Activity的生命周期里面存在一个额外的回调函数,我们需要重写这个函数。这个回调函数并没有在前面课程的图片示例中显示。这个方法是onSaveInstanceState(),当用户离开Activity时,系统会调用它。当系统调用这个函数时,系统会在你的Activity被异常Destory时传递Bundle对象,这样你可以增加额外的信息到Bundle中并保存到系统中。然后如果系统在Activity被Destory之后想重新创建这个Activity实例时,之前的那个Bundle对象会(系统)被传递到Activity的onRestoreInstanceState()方法与onCreate()方法中。

Figure 2:当系统开始停止你的Activity时,只有在Activity实例会需要重新创建的情况下才会调用

然后这个实例又需要被重新创建时,系统会传递在 (1) 中的状态数据到 onCreate() (2) 与
onRestoreInstanceState()(3).
(通常来说,跳转到其他的activity或者是点击Home都会导致当前的activity执行onSaveInstanceState,因为这种情况下
的activity都是有可能会被destory并且是需要保存状态以便后续恢复使用的,而从跳转的activity点击back回到前一个
activity,那么跳转前的activity是执行退栈的操作,所以这种情况下是不会执行onSaveInstanceState的,因为这个
activity不可能存在需要重建的操作)

如下图所示:

这里写图片描述

当系统开始停止Activity时,只有在Activity实例会需要重新创建的情况下才会调用到onSaveInstanceState(),在这个方法里面可以指定额外的状态数据到Bundle中。如果这个Activity被destroyed然后这个实例又需要被重新创建时,系统会传递在中的状态数据到onCreate()与onRestoreInstanceState().

4.1 保存Activity状态

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

static final String STATE_SCORE = "playerScore";static final String STATE_LEVEL = "playerLevel";...@Overridepublic 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() 方法的父类实现,这样默认的父类实现才能保存视图状态的信息。

4.2 恢复Activity状态

当Activity从Destory中重建。我们可以从系统传递给你的Activity的Bundle中恢复保存的状态。 onCreate()与onRestoreInstanceState()回调方法都接收到了同样的Bundle,里面包含了同样的实例状态信息。因为onCreate()方法会在第一次创建新的Activity实例与重新创建之前被Destory的实例时都被调用,我们需要在尝试读取 Bundle对象前检测它是否为null。如果它为null,系统则是创建一个新的Activity实例,而不是恢复之前被Destory的Activity。

@Overrideprotected 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(),而是不是在onCreate方法里面恢复数据。onRestoreInstanceState()方法会在onStart()方法之后执行. 系统仅仅会在存在需要恢复的状态信息时才会调用onRestoreInstanceState(),因此你不需要检查Bundle是否为null。

public void onRestoreInstanceState(Bundle savedInstanceState) {    // Always call the superclass so it can restore the view hierarchy    super.onRestoreInstanceState(savedInstanceState);    // Restore state members from saved instance    mCurrentScore = savedInstanceState.getInt(STATE_SCORE);    mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);}

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

0 0
原创粉丝点击