Android Activity详解

1.什么是Activity

Activity是Android提供的四大组件之一,是进行Android开发必不可少的组件.Activity是一个界面的载体,它提供屏幕进行交互.可以把它与html页面进行类比,html页面由各种各样的标签组成,而Activity则可以由各种控件组成.每个Activity都会获得一个用于绘制其用户界面的窗口,窗口可以充满哦屏幕也可以小于屏幕并浮动在其他窗口之上。

一个应用通常是由多个彼此松散联系的Activity组成,一般会指定应用中的某个Activity为主活动,也就是说首次启动应用时给用户呈现的Activity。将Activity设为主活动的方法,如下面代码所示需要在AndroidManifest文件中添加以下内容

<application>
     ....
    <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
     </activity>
     ....
</application>   

当然Activity之间可以进行互相跳转,以便执行不同的操作。每当新Activity启动时,旧的Activity便会停止,但是系统会在堆栈也就是返回栈中保留该Activity。当新Activity启动时,系统也会将其推送到返回栈上,并取得用户的操作焦点。当用户完成当前Activity并按返回按钮时,系统就会从堆栈将其弹出销毁,然后回复前一Activity.

当一个Activity因某个新Activity启动而停止时,系统会通过该Activity的生命周期回调方法通知其这一状态的变化。Activity因状态变化每个变化可能有若干种,每一种回调都会提供执行与该状态相应的特定操作的机会

2.创建Activity

要创建Activity,必须创建Activity的子类。在子类中实现Activity在生命周期的各种状态之间转变时(例如创建 Activity、停止 Activity、恢复 Activity 或销毁 Activity 时)系统调用的回调方法。Android Studio中新建项目默认创建的代码为

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

onCreate()方法:必须实现的方法,系统在创建Activity时调用此方法。您应该在实现内初始化Activity的必要组件,必须在此方法调用setContentView(),用来定义Activity用户界面布局(XML文件)

3.在清单文件中声明Activity

每次新建的Activity都需要在AndroidManifest文件中添加如下内容,并将元素添加为元素的子项

<manifest ... >
  <application ... >
      <activity android:name=".ExampleActivity" />
      ...
  </application ... >
  ...
</manifest >

4.启动Activity

作为主活动,在应用开启的时候就会系统创建,而用户不仅仅只需要主活动界面,用户需要界面的跳转,而界面的跳转也是其他活动界面(Activity)启动。

4.1 Activity启动方式

在Activity启动中涉及到Intent这个API,在Android中提供了Intent机制来协助应用间的交互与通讯Intent负责对应用中一次操作的动作、动作涉及数据、附加数据进行描述,Android则根据此Intent的描述,负责找到对应的组件,将 Intent传递给调用的组件,并完成组件的调用。
Intent不仅可用于应用程序之间,也可用于应用程序内部的Activity/Service之间的交互。因此,可以将Intent理解为不同组件之间通信的“媒介”,专门提供组件互相调用的相关信息

Intent有七大属性:

  1. component(组件):目的组件
  2. action(动作):用来表现意图的行动
  3. category(类别):用来表现动作的类别
  4. data(数据):表示与动作要操纵的数据
  5. type(数据类型):对于data范例的描写
  6. extras(扩展信息):扩展信息
  7. Flags(标志位):期望这个意图的运行模式

显示启动
这应该用的是最多的一个启动方式了,先在AndroidManifest.xml里配置activity,然后进行如下操作:

Intent intent = new Intent(MainActivity.this,CloneActivity.class); 
startActivity(intent);

隐式启动
它与显示启动最大区别就是不需要在Intent的实例化中传参, 但是需要在AndroidManifest.xml给该activity配置intentfilter属性,启动代码如下

<intent-filter>
    <action android:name="com.mangoer.activityreview"></action>
    <category android:name="android.intent.category.DEFAULT"></category>
</intent-filter>
Intent intent = new Intent();
intent.setAction("com.mangoer.activityreview");
startActivity(intent);

隐式启动的过滤过程如图
Android Activity详解


一般显示启动用于同一APP内隐式启动用于启动APP外部活动,如果有多个组件被匹配成功,就会以对话框列表的方式让用户进行选择。每个Intent中只能指定一个action,但却能指定多个category;类别越多,动作越具体,意图越明确
Intent添加类别可以添加多个类别,那就要求被匹配的组件必须同时满足这多个类别,才 能匹配成功。操作Activity的时候,如果没有类别,须加上默认类别
比如打开百度:

Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
Uri data=Uri.parse("http://www.baidu.com");
intent.setData(data);
startActivity(intent);

有时候我们跳转到别的activity后,希望能够携带一些数据返回到原activity,那就需要用到startActivityForResult


4.2 Activity启动模式

启动模式允许你去定义如何将一个activity的实例和当前任务栈进行关联,有两种方式来定义启动模式

  1. 使用manifest文件:manifest文件中声明一个activity的时候指定launchMode属性
  2. 使用Intent flag参数:当使用Intent启动activity的时候,可以在Intent中加入flag来指定新启动的activity如何与当前任务进行关联

如果一个被启动的activitymanifest里定义了启动模式,然后在使用Intent启动的时候也设置了flag定义启动模式,那么Intent中的定义模式将会覆盖manifest中的定义


在manifest定义启动模式
通过设置launchMode属性来定义,有四种可选参数:

  • standard: 这也是默认启动模式,即标准模式;如果在该文件中声明activity的时候不指定这个参数值,这个activity就模式使用这种模式;它的意思是每启动一个activity都会重新创建一个该activity的实例加入到当前任务中,会走完整的生命周期函数,就算任务中已经存在了这个activity的实例还是会创建。这样就会出现上面说的一个activity在返回栈中存在多个实例,这其实是非常消耗资源的。

  • singleTop: 即栈顶复用模式,这个中文翻译很有意思,给出了两个很重要的点,栈顶和复用;也就是说如果要启动的activity在返回栈内已经存在了一个实例并且还处于栈顶的位置,那么就不会在重新创建一个实例了,而是复用这个activity;复用体现在哪呢,就是调用这个activity的onNewIntent方法,而不会走onCreate-onStart-onResume这个创建逻辑了;如果这个activity不在栈顶,那么还是会重新创建的。这种模式一般运用场景是一个Activity被频繁推到栈顶的情况,比如IM聊天,有很多消息过来了,不可能每点击一个消息就去新建一个Activity;新闻推送,也不可能每次点击推送消息,就去新建一个Activity

  • singleTask: 即栈内复用模式,也是一种单例模式;当启动的activity如果在栈内有实例,不管在不在栈顶都会复用这个实例,将其置于栈顶,调用这个activity的onNewIntent方法,并且将其上面的所有activity进行出栈处理,全部销毁。
    这里有个注意点:android系统会检测要启动的activity的affinity和当前任务的affinity是否相同,如果相同就会把这个activity放入到当前任务中,不同的话就会创建一个新的任务。而同一个程序中所有的activity的affinity默认都是相同的,不同的程序是不同的,这样启动别的应用的这种模式的activity会创建一个新的任务,启动自己应用的activity不会创建新的任务。这种模式运用场景比较少,一般用在比较特殊的页面,比如用户被签退了,需要重新跳转到登陆页面,这时候需要将之前的页面全部销毁,因为跟session相关的数据需要重新初始化

  • singleInstance: 即单例模式,即这个activity自己独享一个任务,所在的返回栈里面只有这个activity。
    比如给activity2设置singleInstance模式,从activity1跳转到activity2,activity2跳转到activity3,这时候按返回键会返回到activity1.因为Activity2独自占用一个任务栈,而activity1和activity2共用一个任务栈
    再举一个例子,Android系统内置的浏览器程序声明自己浏览网页的Activity始终应该在一个独立的任务当中打开,也就是通过在元素中设置"singleInstance"启动模式来实现的。这意味着,当你的程序准备去打开Android内置浏览器的时候,新打开的Activity并不会放入到你当前的任务中,而是会启动一个新的任务。而如果浏览器程序在后台已经存在一个任务了,则会把这个任务切换到前台,不会再重新创建。


使用Intent Flags 定义启动模式
使用方法就是在使用startActivity的时候构建Intent,对Intent加入一个flag来改变Activity与任务的关联模式

  • FLAG_ACTIVITY_NEW_TASK: 当Intent对象包含这个标记时,系统会寻找或创建一个新的task来放置目标Activity,寻找时依据目标Activity的taskAffinity属性进行匹配,如果找到一个task的taskAffinity与之相同,就将目标Activity压入此task中,如果查找无果,则创建一个新的task,并将该task的taskAffinity设置为目标Activity的taskActivity,将目标Activity放置于此task。注意,如果同一个应用中Activity的taskAffinity都使用默认值或都设置相同值时,应用内的Activity之间的跳转使用这个标记是没有意义的,因为当前应用task就是目标Activity最好的宿主

  • FLAG_ACTIVITY_SINGLE_TOP: 设置了这个flag,如果要启动的Activity在当前任务中已经存在了,并且还处于栈顶的位置,那么就不会再次创建这个Activity的实例,而是直接调用它的onNewIntent()方法;否则会再创建这个activity;这种flag和在launchMode中指定"singleTop"模式所实现的效果是一样的。

  • FLAG_ACTIVITY_CLEAR_TOP: 设置了这个flag,如果要启动的Activity在当前任务的栈顶,就会销毁这个实例并重新创建这个activity;如果不在栈顶,还是销毁已存在的实例并清空这个实例上面的所有activity,最后重新创建这个activity。
    如果将Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP两个结合用,如果要启动的Activity已经存在任务栈而且不在栈顶,那么会清空这个activity以上的所有activity,并回调这个activity的onNewIntent,不会重新创建这个activity;如果处于栈顶,就直接回调onNewIntent


taskAffinity参数

affinity可以用于指定一个Activity更加愿意依附于哪一个任务,在默认情况下,同一个应用程序中的所有Activity都具有相同的affinity,所以,这些Activity都更加倾向于运行在相同的任务当中。当然了,你也可以去改变每个Activity的affinity值,通过元素的taskAffinity属性就可以实现了。
taskAffinity属性接收一个字符串参数,你可以指定成任意的值(经我测试字符串中至少要包含一个),但必须不能和应用程序的包名相同,因为系统会使用包名来作为默认的affinity值。
通常可以与allowTaskReparenting属性配合使用,设置为true时,Activity就拥有了一个转移所在任务的能力。具体点来说,就是一个Activity现在是处于某个任务当中的,但是它与另外一个任务具有相同的affinity值,那么当另外这个任务切换到前台的时候,该Activity就可以转移到现在的这个任务当中。


5. 结束Activity

通过调用Activity的**finish()方法来结束Activity还可以通过调用finishActivity()**结束之前启动的活动

关于finishActivity()的理解:
你通过 MainActivity 来启动 ActivityA (使用 startActivityForResult 方法),那么你在 MainActivity 这个类中需要重写 onActivityResult() 这个方法,
然后,你可以在 onActivityResult() 中通过 finishActivity() 方法去结束掉 ActivityA

6. 管理Activity生命周期

周期即活动从开始到结束所经历的各种状态。生命周期即活动从开始到结束所经历的各个状态。从一个状态到另一个状态的转变,从无到有再到无,这样一个过程中所经历的状态就叫做生命周期。

Activity本质上有四种状态:

  1. 运行(Active/Running): Activity处于活动状态,此时Activity处于栈顶,是可见状态,可以与用户进行交互

  2. 暂停(Paused): 当Activity失去焦点时,或被一个新的非全面屏的Activity,或被一个透明的Activity放置在栈顶时,Activity就转化为Paused状态。此刻并不会被销毁,只是失去了与用户交互的能力,其所有的状态信息及其成员变量都还在,只有在系统内存紧张的情况下,才有可能被系统回收掉

  3. 停止(Stopped): 当Activity被系统完全覆盖时,被覆盖的Activity就会进入Stopped状态,此时已不在可见,但是资源还是没有被收回

  4. 系统回收(Killed): 当Activity被系统回收掉,Activity就处于Killed状态,它所保存的信息和成员变量都不在了。

如果一个活动在处于停止或者暂停的状态下,系统内存缺乏时会将其结束(finish)或者杀死(kill)。这种非正常情况下,系统在杀死或者结束之前会调用**onSaveInstance()方法来保存信息,同时,当Activity被移动到前台时,重新启动该Activity并调用onRestoreInstance()**方法加载保留的信息,以保持原有的状态。

Android Activity详解
Android Activity详解


  1. Activity 的整个生命周期发生在 onCreate() 调用与 onDestroy() 调用之间。
    您的 Activity 应在 onCreate()中执行“全局”状态设置(例如定义布局),并释放 onDestroy()中的所有其余资源。例如,如果您的 Activity 有一个在后台运行的线程,用于从网络上下载数据,它可能会在onCreate() 中创建该线程,然后在onDestroy() 中停止该线程。

  2. Activity 的可见生命周期发生在 onStart() 调用与 onStop() 调用之间。
    在这段时间,用户可以在屏幕上看到 Activity 并与其交互。例如,当一个新 Activity 启动,并且此 Activity 不再可见时,系统会调用onStop()。您可以在调用这两个方法之间保留向用户显示 Activity 所需的资源。例如,您可以在onStart() 中注册一个 BroadcastReceiver 以监控影响 UI的变化,并在用户无法再看到您显示的内容时在 onStop()中将其取消注册。在 Activity 的整个生命周期,当 Activity 在对用户可见和隐藏两种状态中交替变化时,系统可能会多次调用 onStart() 和 onStop()。

  3. Activity 的前台生命周期发生在 onResume() 调用与 onPause() 调用之间。
    在这段时间,Activity 位于屏幕上的所有其他 Activity 之前,并具有用户输入焦点。Activity 可频繁转入和转出前台 — 例如,当设备转入休眠状态或出现对话框时,系统会调用 onPause()。由于此状态可能经常发生转变,因此这两个方法中应采用适度轻量级的代码,以避免因转变速度慢而让用户等待。


小结:
当Activity启动时,依次会调用onCreate(),onStart(),onResume(),而当Activity退居后台时(不可见,点击Home或者被新的Activity完全覆盖),onPause()和onStop()会依次被调用。当Activity重新回到前台(从桌面回到原Activity或者被覆盖后又回到原Activity)时,onRestart(),onStart(),onResume()会依次被调用。当Activity退出销毁时(点击back键),**onPause(),onStop(),onDestroy()**会依次被调用,到此Activity的整个生命周期方法回调完成。
Android Activity详解

Android Activity详解


参考:

  1. activity的四种状态说明
  2. Activity开发–掌握Activity隐式显示启动方法 scheme跳转协议 生命周期及启动模式配置
  3. Android Activity详解