Androidz之Activity概要学习

Androidz之Activity概要学习

 

1.     Activity类概述

Activity(活动)是一个单独的、能获取焦点的,且能与用户交互的东西。所以我们通常在Activity类中的onCreate方法中调用setContentView(View)来在UI布局中创建窗口(window)。Activities经常占满全屏,当然,它们也可以作为浮动的窗口(通过windowIsFloating方法实现),或是嵌入到另一个Activity(使用ActivityGroup)中。

 

所有的Activity子类中有两个方法是要实现的:

(1)   onCreate(Bundle)

我们在双击应用图标时,系统根据AndroidManifest.xml的android:name来创建Activity,然后调用此回调函数,最重要的是我们可用定义UI布局资源文件作为参数调用setContentView(int)来设置用户界面,和使用findViewById(int)来获得UI中的widgets(小组件,比如按键),这需要通过编译的方式来实现(相对于xml定义的方式)。

 

下面是AndroidManifest.xml文件的相关部分:

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

(2)   onPause()

用户离开(尽管这不总是意味着该Activity将被销毁)activity时候系统调用此回调函数来处理,最重要的是用户的任何改变在这点上应该提交(通常是ContentProvider保持这些数据),也就是需要完成保存数据的工作以便用户后面回到你的Activity以恢复之前的状态。

 

为了使用Context.startActivity()来启动Activity,必须在Android应用的清单文件(AndroidManifest.xml)中申明Activity才可以在应用中使用该Activity。你可以通过打开Android应用的清单文件,然后添加一个<activity>元素作为<application>的子节点。见上面给出的内容。

 

2.     开发人员指南

Activity类是应用程序整个生命周期的一个重要组成部分,Activities的启动方式和组合(put together)是Android平台应用模式的一个基础部分。关于Android应用更详细的观点(perspective)和activities的行动方式,请参考Application Fundamentals(http://developer.android.com/reference/android/app/Activity.html)和Tasks and Back Stack

(http://developer.android.com/guide/components/tasks-and-back-stack.html)。

 

 

3.     Fragments

Fragement(段)用Android3.0开始引入,如果之前版本的系统需要支持,就要导入android-support-v4.jar库。Activity实现可以使用Fragment类来更好模块化代码,为大显示屏构建更复杂的用户接口和帮助应用程序更灵活是英语小和大屏幕的设备。下面给出一个图来理解Fragment,更详细的后面再学习:


图1

 

 

 

4.     Activity生命周期

系统中的Activities被一个activity栈(activity stack),当启动一个新的activity,它被放到栈顶和成为正在运行的activity。前一个activity保留在栈顶的下一个位置,除非新的activity退出,否则前一个activity不会回到前台。

 

一个activity本质上有4个状态:

(1)  Active/running

表示activity处于屏幕前台,也就是在栈顶。

 

(2)  Pause

表示activity失去焦点,但依然可见,也就是说,有其它可见的activity位于本activity(这是相对于activity在stack的位置来说的)。处于paused状态的activity依然是活动的(它保持所有状态和信息(member)的信息,且仍然由windows manager管理)但在系统空闲内存极低时有可能被Android系统杀掉(kill)。

 

(3)  Stopped

activity完全被另一个activity覆盖,虽然它依然保持所有状态和成员信息,但它不在可见,所以它的窗口是隐藏的。并在如果系统在需要内存时将杀掉此activity

 

(4)  如果一个activity处于paused或者stopped,系统能够命令其finish或者简单

kill其进程。当它重新在用户面前显示的时候,它必须完全重新启动并且将其关闭之前的状态全部恢复回来。

 

下面的图展示了activity重要的状态路径,方矩形表示回调函数,我们可以实现这些方法从而使activity在状态改变时执行我们制定的操作,带颜色的椭圆形是activity的主要状态:


图2

 

在监控我们activity时,有三个重要的循环(loop):

(1)  整个生命周期(entire lifetime)

从开始调用onCreate(Bundle)到最终调用onDestroy(),activity在onCreate()中创建所有的全局状态,在onDestroy()中释放所有的资源。比如,如果它有一个从网络下载数据的后台(background)线程,那activity就会在onCreate()中创建线程,而在onDestroy()中停止这个线程。

 

(2)  可见的生命周期(visible lifetime)

从调用onStart()到调用相应的onStop(),在这段时间用户能够在屏幕中看这个activity,尽管它不一定在前台也不一定和用户交互。在这两个办法之间我们可以维护(maintain)需要显示给用户的资源。比如,我们可以在onStart()中注册一个BroadcastReceiver来监控那些影响我们UI的变化,在用户不在需要我们的显示时,可在onStop()中注销这个BroadcastReceiver。onStart()和onStop()办法可以多次被调用,因为activity对于用户来说可以是可见或是隐藏的。

 

(3)  前台生命周期(foreground lifetime)

从调用onResume()到调用相应的onPause(),在这段时间activity在所有其他activity的前面,且和用户交互。一个activity可以经常在resumed和paused状态之间切换,比如当设备进入sleep状态,activity的结果返回时,且新的intent到来时,所以这个两个方法的代码应该非常简短。

 

下面的方法定义了activity的整个生命周期,如下面所示:

public classActivity extends ApplicationContext {
     protected void onCreate(BundlesavedInstanceState);
 
     protected void onStart();
    
     protected void onRestart();
 
     protected void onResume();
 
     protected void onPause();
 
     protected void onStop();
 
     protected void onDestroy();
 }


所有的这些都是hook,我们可以重载这些方法从而使activity改变状态时执行适当的操作。所有的activity都要实现onCreate(Bundle)来进行初始化配置,大多数也实现onPause()来提交数据的改变和准备停止和用户的交互。在实现这些方法时,我们还应该要调用activity的父类,比如:

@Override
    protected void onCreate(BundlesavedInstanceState) {
        super.onCreate(savedInstanceState);
…
}

大体上activity生命周期的变化看起来像下图描述的:


图3

(1)  onCreate()

当activity第一次被创建调用onCreate(Bundle),这里我们应该进行所有的一般静态设置:创建view,绑定数据到list中,等等。如果activity之前是冻结状态,此方法的Bundle参数包含了状态信息。

如果是首次创建,本方法之后将会调用onStart(),如果activity是停止后重新启动,将调用onRestart()。

 

(2)  onRestart()

在activity停止之后重新启动时调用此方法,总是紧跟着onStart()方法。

 

(3)  onStart()

当activity对用户即将可见时调用,如果activity作为前台接着蒋调用onResume(),如果要隐藏activity,接着将调用onStop()。

 

(4)  onResume()

当activity即将和用户交互时调用,此时activity处于activity栈顶,已经获取用户的输入,如果其他的activity需要恢复显示,接着将调用onPause()。

 

(5)  onPause()

当系统准备恢复前一个activity时调用,此方法一般用于提交被改变且没保存的永久性数据,停止动画和其他消耗CPU资源的事情,等等。此方法的实现必须是非常快速,因为下一个activity不能恢复直到此方法返回。如果activity返回到前端,接着调用onResume();如果对于用户隐藏,接着调用onStop()。

 

(6)  onStop()

当activity不再对用户可见时调用,因为其他activity已经被恢复和覆盖整个activity。在一个新activity启动和一个已经存在的activity被切换到前台,或是整个activity即将退出(destroyed)时都会发生这样的场景。

如果activity重新回到前台和用户交互时接着调用onRestart();如果activity要退出则调用onDestory()。

 

(7)  onDestroy()

在activity被销毁前调用的最后一个方法,当activity完成时出现这种情况(对activity调用finish()方法,或是系统为了节省空间临时销毁此activity的实例。)。我们可以通过调用isFinishing()方法来区分这两种情况。

 

主要上表上”killable”这一列—对于那些标记了killable的方法,在这些方法返回后,activity的进展可能随时被系统kill而不再执行activity中任何代码。所以,我们应该在onPause()方法中保存何永久数据(比如用户编辑)。除此之外,onSaveInstanceState(Bundle)方法在activity切换为后台状态时被调用,允许我们保存activity的任何动态实例状态到Bundle中,如何需要重新创建此activity,此参数后来被onCreate(Bundle)函数接收。

@Override
    protected void onCreate(BundlesavedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        if (savedInstanceState == null) {
           getSupportFragmentManager().beginTransaction()
                    .add(R.id.container, newPlaceholderFragment())
                    .commit();
        }
}

请注意,在onPause()中保存永久数据非常重要,而不是onSaveInstanceState(Bundle),因为后者不是生命周期回调(liftcycle callbacks)的一部分。

 

大家了解一下,相对于HONEYCOMB(Android3.0)之前的Android版本,后面的系统这些语意(semantics)稍微有些改变。从HONEYCOMB版本开始,只有onStop()返回后才能kill一个应用。

 

5.     配置改变(Configuration Changes)

如果设备的配置(在Resources.Configuration类中定义)发生改变,那么所有用户界面上的显示需要更新来匹配新的配置。因为Activity是和用户交互的最主要机制,它包含了处理配置改变的特别支持。

 

除非你专门指定,一个配置的改变(比如屏幕分享,语言,输入设备等等的改变)将引发我们当前activity被销毁,这个销毁的动作是通过一个正常activity生命周期过程(相应的调用onPause(), onStop(), and onDestroy()来处理)。如果activity已经在前台(foreground)或是对用户可见,当这个实例(instance)的onDestroy()被调用完成后就重新创建这个activity的实例,并将onSaveInstanceState(Bundle)保存的前一个实例传递给新的实例。

 

由于任何应用资源(包括layout文件)都有可能因为配置值而改变,所以处理配置改变的唯一安全的方式就是重新获取所有的资源,包括layouts(布局)、drawables(绘图资源)和strings(字符串资源)。由于activities已经知道如何保存它们的状态并能够从哪些状态中重新创建自身,所以用新配置来重启activity自身是一种便利的方式。

 

在一些特殊的情况中,我们希望当一种或是多种配置改变时避免重启activity,可以通过在mainfest文件中设置android:configChanges属性来实现这一点。在这里可以声明activity可以处理的任何改变,当这些配置改变时不会重启activity,而是会调用activity的onConfigurationChanged(Configuration)方法。如果改变的配置中有activity无法配置的配置(在android:configChanges并未声明),我们的activity仍然要被重启,而onConfigurationChanged(Configuration)将不会被调用。

 

6.     启动activity并获取结果(Starting Activities and Getting Results)

startActivity(Intent)用来启动一个新的activity,这activity将位于activitiy栈的栈顶。这个方法只有一个参数Intent,此参数描述将被执行的activity。

 

有时候,我们希望在一个activity结束时得到它返回的结果。比如,我们开启一个activity来让用户从通讯录中选择一个人,当它结束时将会返回被选择的人。为了得到这个返回的信息,我们调用startActivityForResult(Intent, int)方法来启动新的activity,此方法第2个整形参数作为这次调用的标识。然后通过我们的onActivityResult(int, int, Intent)方法返回结果,此方法第1个参数就是之前调用所用到的标识。

 

当一个activity退出,它调用setResult(int)返回数据给它的父进程(parent),此方法必须提供一个结果码,这个结果码可是是标准结果码RESULT_CANCELED,RESULT_OK,也可以是其他任何从RESULT_FIRST_USER开始的值。此外,它还可根据需要返回一个包含了任何额外数据的Intent。所有这些信息都会在父activity的回调函数Activity.onActivityResult()中出现,并连同最初提供的识别标记一起(此处有些拗口,意思其实就是子activity返回的内容、返回码、识别标记都将作为参数,按照不同的返回情况来调用父activity的Activity.onActivityResult()方法,以实现出现各种返回时父activity做出响应的处理)。

 

如果子activity由于某种情况(比如crashing)失败,父activity将会收到RESULT_CANCELED结果码

 public class MyActivity extends Activity {
     ...

     static final int PICK_CONTACT_REQUEST = 0;

     protected boolean onKeyDown(int keyCode, KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
             // When the user center presses, let thempick a contact.
             startActivityForResult(
                 new Intent(Intent.ACTION_PICK,
                 new Uri("content://contacts")),
                 PICK_CONTACT_REQUEST);
            return true;
         }
         return false;
     }

     protected void onActivityResult(int requestCode, int resultCode,
             Intent data) {
         if (requestCode == PICK_CONTACT_REQUEST) {
             if (resultCode == RESULT_OK) {
                 // A contact was picked.  Here wewill just display it
                 // to the user.
                 startActivity(new Intent(Intent.ACTION_VIEW, data));
             }
         }
     }
 }
 

 这里是一个例子,说明了如何启动一个新的activity并处理结果。

 

7.     保存持久数据(Saving Persistent State)

通常有两种持久状态需要activity处理:类似于共享文档(代表性的是使用一个content provider存储在SQLite数据库中)和像用户参数一样的内部状态。

 

对于content provider数据,我们建议activity使用“编辑即生效”(edit in place)的用户模型。就是说,用户的任何编辑都立即生效而不要求进一步的确认。下面两天规则通常使支持这种模型成为一件简单的事情:

(1)  当创建一个新文档,立即为它创建后台数据库条目(backing database entry)或是文件。比如,如果用户写一封新邮件,当他们开始输入数据时马上创建一个新的数据库条目,所以即使他们输入后进入其他任何activity,这封邮件也会出现在草稿箱中。

(2)  当activity的onPause()被调用时,它应该讲用户的所做的任何修改提交给后台content provider或是文件,这样可确在准备运行的acitvity中看到这些修改。我们可能在activity生命周期的关键时刻更积极(more aggressively)提交数据,比如在启动一个新的activity之前,在完成自己的activity之前,在用户输入域切换时,等等。

 

这模型是为了避免用户在activity之间切换时数据丢失而设计的,并允许系统在activity被paused之后的任何时刻安全销毁activity(因为在其他地方可能需要系统资源)。主要这里暗示用户点击activity的BACK键并不意味着cancel,它意味着安全保存当前内容后离开这个activity。Activity中的取消编译通过其他几只来提供,比如一个显示的“revert(还原)”和“undo(撤销)”选项。

 

如果要了解content providers更多的内容请看content package部分,这是不同的activity如何调用和彼此之间传递参数的关键部分。

 

Activity类也提供API来管理与activity相关联的内部持久状态,比如,这可以用来记忆日历的首选初始显示(日期视图或周视图)或web浏览器中用户的默认主页(default home page)。

 

Activity持久状态使用getPreferences(int)方法来管理,允许我们检索和修改一系列与activity相关联的名称/值对。如果要在多个应用组件(activites、receivers、services、providers)之间共享数据,我们可以使用底层方法Context.getSharedPreferences()来检索一个特定名称下的参数对象(请注意application packages之间不能共享设置数据----这时你就需要一个contentprovoders来实现。)

 

这里是从一个日历activity中摘取的代码,用来在永久设置中保存用户首选的视图。

 

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

8.     许可(permission)

我们可以通过在activity所属应用的mainfest文件中对应的<activity>标签声明来显示哪些应用可以启动此activity。如果进行了声明,其他应用需要在他们自己的manifest文件中声明对应的<uses-permission>元素才可以启动这个activity。

 

当启动一个activity,我们能够在Intent设置

Intent.FLAG_GRANT_READ_URI_PERMISSION

和/或Intent.FLAG_GRANT_WRITE_URI_PERMISSION。这样即可授权acitvity来访问Intent中特殊的URIs。此访问被保持直到activity完成(在正在销毁的宿主进程和其他临时破坏之间切换时也会被保持)。自从GINGERBRED(Android2.3.3)开始,如果这个activity已经被创建和一个新的Intent正被传送给onNewIntent(Intent),任何新的被授权的URI权限将被添加到已存在的这个activity中。

 

如果想了解更多关于许可和安全方面的信息,请参考Security and Permissions部分。

 

9.     进程生命周期(process lifecycle)

Android系统尽量久的保留应用程序,但是当内存降低时最终会移除旧的进程。就想activity生命周期描述的一样,移除哪个进程取决于与用户交互的亲密程度。一般来说,进程基于运行的activity来分为4个状态,下面根据状态的重要程度来排列。系统会在kill更重要的进程(第1个)之前会先kill哪些不是很重要的进程(最后一个)

 

(1)  前台(foreground)activity(此activity位于屏幕最上方,正与用户交互)被认为是最重要的,它的进程只能在万不得已的情况下才能kill。如果使用了多于设备的有效内存,一般来说这时候设备处于内存分页(paging)状态,为了使用户界面保持响应才会发出kill请求。

(2)  可见的activity(对用户可见当不在前台,比如处在浮动对话框后)被认为是非常重要的,除非为了保证前台activity运行,否则不会被kill。

(3)  后台activity(对用户不可见,并处于paused状态的activity)就不是很重要了,系统为了保证前台和可见进程的运行会回收内存而安全地kill它们。如果它的进程需要被kill,当用户切回(navigate back to)到此activity(使它在屏幕上再次可见),它的onCreate(Bundle)办法将被以保存了之前状态的savedInstanceState来调用,此参数由onSaveInstanceState(Bundle)办法提供。所以它能够在用户上次离开它的地方重启自身。

(4)  空进程是一个没有运行activity或是其他组件(比如Service或是BroadcastReceiver类)的进程,它们会在系统内存比较低时被快速kill。因此,当你要在activity外运行任何的后台操作时,必须在BroadcastReceiver or Service的上下文运行,这样才能确保系统需要将我们的进程保留。

 

有时候一个activity可能需要运行一个长时间操作,且它不依赖于activity生命周期而存在。例如一个照相应用运行我们上传照片到web站点,上传可能需要很长时间,在上传过程中允许应用离开应用但还保持工作。为了实现这点,我们的activity应该启动一个Service来执行此工作。这使系统能在我们进程上传数据的过程中够恰当的区分它的优先级(认为此进程比其他不可见应用更重要),而不管创建此Service的activity的状态是puased、stopped还是finished。

 

 

 

参考链接:

Android开发者Activity

http://developer.android.com/reference/android/app/Activity.html

Android Activity 详解

http://www.2cto.com/kf/201303/197684.html

 

androidactivity生命周期(转载)

http://www.cnblogs.com/draem0507/archive/2012/11/28/2793332.html

 

http://kb.cnblogs.com/page/70125/

 

posted on 2014-05-28 17:56  王亮1  阅读(153)  评论(0编辑  收藏  举报

导航