使用Activity之前必须得读的好文

大多数人都会用Activity,但是真的会用了吗?Android源码中Activity类的注释了解一下?宝哥将为你带来一系列关于你常用的类确不曾认真读过的源码中的注释的翻译文章。网上的信息泛滥,只有源码的注释才是精髓。

Activity

注释原文

An activity is a single, focused thing that the user can do.  Almost all
activities interact with the user, so the Activity class takes care of
creating a window for you in which you can place your UI with
{@link #setContentView}.  While activities are often presented to the user
as full-screen windows, they can also be used in other ways: as floating
windows (via a theme with {@link android.R.attr#windowIsFloating} set)
or embedded inside of another activity (using {@link ActivityGroup}).
    etc...

Code Here

中文释义

简介

Activity是一个简单的聚焦于用户能做什么。几乎所有的Activity都会与用户进行交互,所以Activity类主要专注于创建一个能够让你通过setContentView()将UI填充进去的窗口容器。尽管大多数时候,Activity会以全屏的方式呈现在我们面前,但是同样也可以通过其他方式呈现:

  • 悬浮窗口的形式(通过自定义Theme的时候设置windowIsFloating属性为true
  • 包裹与另一个Activity当中(通过使用ActivityGroup

使用

当我们使用Activity时,需要继承Activity并重写它的生命回调,通常下面两个方法几乎所有子类都会去实现的:

  • onCreate()

    这个方法是通常我们用来做初始化的工作的。更重要的是,这里通常是我们使用我们定义的UI布局资源通过setContentView(int layoutId)方法调用去设置,也是我们通过findViewById()方法来找到我们需要与用户做交互的控件对象。

  • onPause()

    是用来处理当用户离开页面时的逻辑。最重要的是,用户在页面上交互所产生的数据需要在这个时候进行保存(通常是让ContentProvider来hold数据)

如果想使用startActivity()去启动,需要我们在AndroidMenifest文件中注册对应的Activity。

几个重要的话题

Fragment

Android3.0开始,Activity可以通过Fragment来实现模块化的代码,在大屏手机上创建更复杂的用户交互UI,能够为你的应用更好的适配不同屏幕尺寸的手机(从小屏到大屏)

Activity生命周期

Activity在系统中以栈的方式进行管理。当一个新的activity启动时,将被放到整个栈的栈顶,并且成为正在运行中的Activity。旧的Activity将被保留在栈中(新的Activity的下方),直到新的Activity退出时旧的Activity才会再次回到前台与用户进行交互。
Activity会有基本的四个状态:

  • active(活跃的) or running(运行中的):

    如果Activity在系统的前台展示(在activity栈顶)

  • pause:

    如果一个Activity失去焦点但是仍然可见(一个新的非全屏的Activity或者一个透明的Activity获得焦点存在于栈顶),这时旧的Activity就是paused状态。在此状态下的Activity完全是存活的(仍然保持状态和成员信息,并且还是依附于WindowManager上),但是可以在内存极度紧张的情况下回收杀掉此状态的activity

  • stopped:

    如果新的activity完全覆盖旧的activity,此时就为stopped状态。此时仍然保持所有的状态信息和成员信息,只是此页面对用户来说已经不可见并且在其他地方需要更多内存时经常会回收此状态下的activity以释放出更多的内存供使用。

  • 空进程状态

    如果activity处在paused或者stopped状态,系统可以通过通知它去finish以便从内存中将其移除,或者直接将其进程杀死。当它需要再次展现在用户面前时,需要完全从新启动和恢复之前的状态

    三个重要生命周期
    • 完整生命周期(onCreate->onDestroy)

      onCreate里面做全局相关变量的初始化工作,在onDestroy中释放所有持有的资源。例如我们新启线程进行下载工作,可以在onCreate中新建线程,然后在onDestroy中销毁线程

    • 可见生命周期(onStart->onStop)

      在此期间用户可以看到Activity的UI,尽管,activity可能不在前台或者能够和用户直接交互。在此生命周期期间,你可以管理用来呈现给用户的各种资源。例如可以在onStart里面注册一个广播模拟一些条件变化导致你的UI跟随变化,在onStop里注销广播,因为此时用户无法看到activity呈现的UI了。因为页面可能会可见可能会隐藏,所以onStart和onStop可以被调用多次。

    • 前台生命周期(onResume->onPause)

      此生命周期期间,activity在所有activity之上,并且是正在和用户进行交互的。activity可以非常流畅的在onResume和onPause之间进行切换–例如当系统进入睡眠模式,当activity提交了一个result(关闭Activity,并回调结果),当提交了一个intent(启动Activity)–所以onResume里面的代码应该是很轻量级的,不宜有耗时的操作。

完整的生命周期如下所示,所有的方法都和生命周期关联起来了,你可以在生命周期发生变化时做适当的处理工作:

方法 | 描述 | 是否可被杀 | 下一个方法
|——|——|——|——|
onCreate| Activity第一次创建的时候被调用,一般可做创建View,绑定数据到列表中。并且提供bundle存储上一次的数据用于恢复。| 否 |onStart
onRestart | 在activity stopped之后调用,再次被启动之前 | 否|onStart
onStart| 当activity被用户可见时调用 | 否|如果activity要进入前台:onResume;如果要被隐藏:onStop
onResume| 在activity可与用户进行交互时调用 |否|onPause
onPause| 有新的页面要启动 |是|重新展示给用户:onResume;用户不可见:onStop
onStop| 当前Activity不可见,有新的页面完全覆盖在上面 |是|如果activity重新再展现到用户面前:onReStart;页面结束:onDestroy
onDestroy| 你的页面被销毁前回调的最后一个方法,这个方法被触发是可能因为用户调用finish方法或者系统回收杀死Activity以节约更多的空间,可以通过isFinishing来区分两者 |是|没有

上表中被标记可被杀的生命周期中,在方法执行完后。可被系统在任何时刻杀死,因此,你应该在onPause中将持久化的数据进行保存。另外,onSaveInstanceState(Bundle)会在activity被销毁前调用,以便在activity离开时保存一些动态的数据到指定的Bundle中,并且将在activity被恢复时通过onCreate(Bundle)中的bungle参数将数据传回。注意将保存持久化的数据操作放在onPause中而不是在onSaveInstanceState中,因为onSaveInstance方法并不是生命周期的回调方法,所以正如他的文档中所描述的一样并不是所有的情况都会被调用。但是从Android3.0开始,可被杀的状态是在onStop被调用返回后。导致onSaveInstanceState()方法可能会在onPause后onStop之前调用。

CofigurationChanges

  • 如果设备的配置(定义在Resources.Configuration类)发生了改变,接下来所有的呈现在用户面前的UI需要更新以适应新的配置。因为Activity是与用户进行交互的最主要的机制,所以它包含一些特殊的支持来应对配置的改变。除非有特殊的指定(AndroidMinifest配置),当Configuration发生改变(例如:横竖屏切换、切换语言、切换输入设备等等)时,将导致当前的activity通过通用的生命周期被销毁(onPause->onStop->onDestroy)。如果activity是在前台或者对用户是可见状态,只要onDestroy被调用,当前activity的新的实例附上上个实例从onSaveInstanceState中被保存的数据一起将被创建出来。这样在系统Configuration发生改变时,包括activity的layout在内的任何资源都会完成改变。综上,处理Configuration改变的唯一安全的方法是重新创建所有的资源,包括布局,drawable文件,strings等等。因为activity一定知道怎样去保存它们的状态并且根据保存的状态去重新创建,这是一个非常方便的当Configuration反生改变时去重新创建自身的方法。

  • 在一些特殊的时候,我们不想重新创建我们的activity当一些config发生改变的时候。我们可以在manifest文件中通过android:configChanges属性中申明一些情况来做到。只要有任何你申明过类型的Configuration发生改变时,你将在activity中的onConfigurationChanged方法中得到回调取代重新创建activity。如果出现了未在申明中的Configuration的变化,activity仍然会被销毁重建,并且不会回调onConfigurationChanged方法。

启动Activity(StartingActivitiese)

  • startActivity:

    用于启动一个新的activity,新的activity将被放在栈顶。此方法需要一个Intent(描述被启动Activity的一些信息)类型参数

  • startActivityForResult:

    用于启动一个activity后从启动的activity中得到返回数据,被启动的activity可以通过setResult返回数据,当然可以通过Intent传递更多希望得到的数据

保存持久化的状态(Saving Persistent State):

通常有两种类型的持久化状态需要处理:

  • 共享的类文件型数据
  • 内部状态数据

对于ContentProvider类型的数据,我们建议activity使用“edit in place” 用户模型。也就是,任何用户正在编辑的东西在不需要任何确认的步骤下被立即有效的保存下来。为了支持这种模型,通常需要遵守下面两个规则:

  • 当你正在新建一个新的文档,此文档的备份数据库或者文件也会立即被创建出来。举个例子,如果用户正在写新的电子邮件,一份新的邮件备份文件随着你输入内容而尽快的创建出来。所以在用户跳转到别的页面时,邮件的备份就会出现在草稿箱里。
  • 当一个Activity的onPause()被调用时,这时应该提交任何更改到备份的ContentProvider或者文件中。这就能保证这些改动能够被即将运行的activity所看见。你也许想在activity的生命周期的关键时刻更加积极的提交保存你的数据。例如:在启动一个新的activity之前,在主动结束你的activity之前,用户切换输入相关的等等(几个重要时间点)

这个模型是设计用来防止数据丢失当用户在activity间跳转时,而且允许系统在执行onPause后的任意时间安全回收activity(因为其他地方需要需要一些系统资源)。注意着表明当用户按返回键时并不意味对正在做的事情进行“取消”–它意味着离开当前activity并且正在编辑的东西得到保存。取消正在编辑的东西一定需要一个明确的“重置”或者“撤销”的选项。

关于ContentProvider的更多信息请查阅ContentProvider。我们这里主要关注不同的activity之间怎么互相调用以及数据传递。

Activity类同样提供了一个可以管理内部持久化数据的API。你可以通过getPreferences方法管理内部的存储数据,它允许你将以name/value的形式修改或者获取Activity相关的数据。如果想在应用中各组件之间(activity,receiver,service,provider)共享数据,可以使用Context.getSharedPreferences()方法获取一个preferences对象使用具体的一个标识名来存储。(注意没法通过上面的方法将设置里的数据共享给不同的应用–如果真要这么做你需要使用ContentProvider)

这里有一个存储用户对日历在展示界面上的偏好设置的例子:

public class CalendarActivity extends Activity {
 *     ...
 *
 *     static final int DAY_VIEW_MODE = 0;
 *     static final int WEEK_VIEW_MODE = 1;
 *
 *     private SharedPreferences mPrefs;
 *     private int mCurViewMode;
 *
 *     protected void onCreate(Bundle savedInstanceState) {
 *         super.onCreate(savedInstanceState);
 *
 *         SharedPreferences mPrefs = getSharedPreferences();
 *         mCurViewMode = mPrefs.getInt("view_mode", DAY_VIEW_MODE);
 *     }
 *
 *     protected void onPause() {
 *         super.onPause();
 *
 *         SharedPreferences.Editor ed = mPrefs.edit();
 *         ed.putInt("view_mode", mCurViewMode);
 *         ed.commit();
 *     }
 * }

权限(Permissions)

Activity能够被被启动时因为在AndroidManifest中用activity标签做了申明。这样,其他应用如果想要启动这个activity就需要申明对应的权限(访问该Activity的权限)。

进程生命周期(ProcessLifecycle)

安卓系统希望能够尽量长时间的保持应用进程是活的,但是在可用内存很低的时候会杀掉旧的进程。正如在“Activity的生命周期”中描述的一样,决定那个进程被移除取决于进程与用户的交互状态。总的来说,这里有四种进程所处的状态基于运行在其中的activity的状态,下面以重要性从高到低排列。系统会优先杀掉重要性更低的进程。

  • 前台activity

    activity在屏幕的最上方,正在和用户进行交互,这时的activity被认为是最重要的。仅仅是在当它需要用的内存超过它在设备中的可用内存时,作为最后的手段会杀死它所属的进程。

  • 可见activity

    可见但不是前台应用,例如在一个窗口后面的Activity,此时也是被认为是相当重要的并且除非在系统为了保证前台activity正常运行才不得以杀掉。

  • 后台activity

    用户已经无法可见,此时已经不再重要了。系统会为前台进程或者可见进程回收后台进程所占用的内存。如果进程被杀掉后,用户再次跳回该activity,它将调用onCreate方法并且带上上一次被杀死时通过onSaveInstanceState方法保存的一些信息用来重新恢复到被杀死前的额状态。

  • 空进程

    进程中没有任何组件在运行。在可用内存变低时,它将很快被杀掉。基于这个原因,任何在activity之外的后台任务都必须在广播或者服务中来确保系统知道这个进程需要保持进程不被杀掉。

有时候activity也许需要执行一个独立存在于activity的生命之外的长耗时的操作。一个例子是相机应用允许你上传你的图片到网站上。上传的过程也许会花费很长时间,这期间应用应该允许用户离开当前应用。为了实现这个目标,你的Activity应该启动一个服务来进行上传。这将允许系统将你的进程优先级提高(考虑它比其他不可见应用更重要)在你上传的期间,无论原始的Activity是暂停,停止还是结束状态。

你可能感兴趣的:(Android官方文档翻译)