Android Activity全面解析

从实习到现在,工作了也快一年了,期间过程也碰到和解决了不少的问题,我的为知笔记上也积累了许许多多零零散散的知识点。个人觉得还是有必要将这些零碎的知识点整体梳理总结一遍这样才能真正的转化成真正的技术积累。所以这个专题我主要就是对Android的一些常用知识点以及开发当中遇到的各种问题进行一些梳理与总结。首先,就从Android四大组件Activity开始。

1.Activity生命周期方法完全解析

activity_lifecycle.png

1).onCreate方法

当Activity第一次创建的时候调用。这个方法里主要是提供给我们做一些初始化操作,如:创建view、绑定数据到view。同时这个方法里还带有一个Bundle参数,这个参数的主要的用途会在后面的onSavedInstanceState方法的介绍里再来讲解。

2).onStart方法

紧接着onCreate方法执行的是onStart方法,该方法的执行表示Activity已经显示了但是还无法和用户交互,只有当执行到onResume方法的时候才可以进行交互。另外提一点,google的文档里有写onStart方法可以直接到onStop方法不经过onResume和onPause,我想了一下就在onStart方法里调用了一下finish()方法,果不其然onStart后就直接onStop了,但是感觉并没有什么卵用所以也就不分析了,有兴趣的可以自己实验一下。

3).onResume方法

调用到onResume方法后,Activity就可以与用户开始进行交互了,此时Activity就会位于Activity栈的栈顶了。至此一个Activity就完整的呈现在了我们的眼前并可以与之进行交互了。

4).onPause方法

当系统开始准备停止当前Activity的时候调用,在该方法中google给出的建议是存储一些变化的数据同时停止一些类似于动画等消耗CPU的工作。该方法的调用过程是很快的,否则会影响到后面的Activity的现实,所以在该方法里不宜做过多耗时操作。

5).onStop方法

紧接着onPause方法调用,此时Activity已经不再显示在用户面前了,此时新的Activity可能已经执行到onStart方法或者onResume方法了,所以此时可做一些较为重量级回收操作比方说关于数据库的一些读写操作等。

6)onRestart方法

onStop方法之后可能会调用到onRestart方法,这是因为代表的Activity正在被重新启动,然后紧接着就会继续走到onStart和onResume方法中。

7)onDestroy方法

该方法表示Activity生命周期中的最后一个方法,表示Activity方法将会被销毁,此时我们可以做一些回收操作。这里需要提到的一点是,即使一个Activity被销毁后app内部的static变量是不会被销毁的,因为static变量是全局的,activity销毁但是该app的进程并没有被杀死。所以说这一点尤为需要注意我们的static变量的使用,否则稍有不慎再次启动该activity的时候该static变量就会是一个dirty data!

2典型生命周期分析

通过上面对生命周期中各个方法的分析我们已经对Activity的各个生命周期方法中所做的事情做了一个完整的梳理,下面就针对几种典型的生命周期情况进行分析:

生命周期.png

下面的分析都是针对一个特定的Activity进行分析:

1).启动Activity->返回桌面->再次回到Activity

根据前面对生命周期的分析可以不难知道这三个过程Activity的生命周期方法调用顺序如下:


启动Activity.png

一个Activity,它从启动到展现在用户面前的完成毁掉过程是onCreate()->onStart()->onResume()
按HOME键返回桌面:


按Home键返回.png

此时会调用到onPause()->onStop()方法。
当再次回到Activity的时候会调用onRestart()->onStart()方法->onResume()方法:
按back键退出.png

2).按back退出activity

此时会走onPause()->onStop()->onDestroy()方法:

back键退出Activity.png

再次强调的是该方法即使退出了主Activity但是也没有杀掉进程,所以static变量并没有被销毁,再次进来的时候可能会是脏数据。就以我的经验碰到的最多的一个问题就是:有的时候会将一些名字什么的存在static变量里作为全局变量进行调用,此时测试人员的其中一个case会按back退出activity然后切换系统语言再次启动app。如果没有对static变量做一些销毁操作的话,再次回来就是一个dirty data,语言文字并没有切换导致了bug的存在。

3).在一个Activity中启动另一个Activity

从MainActivity中启动SecondActivity我们可以很清楚的看到MainActivity中的onPause方法执行完了以后然后新的SecondActivity的onCreate、onStart、onResume方法就会依次执行将SecondActivity显示出来,最后MainActivity的onStop方法才会被调用。这同时也验证了之前提到的,google并不建议在onPause方法里进行一些耗时操作。


启动新的Activity.png

这里我们可以做一个暴力一点的实验,在onPause方法里调用Thread.sleep(3000)方法让它睡上三秒,如下图所示明显我们可以看到SecondActivity在等待了3秒以后才被执行到,此时的界面会有一个明显的卡顿住的过程。所以特别强调在onPause方法里不要做任何耗时操作,同时一些如动画这样的消耗CPU性能的操作也需要及时关闭以能最快的启动新的activity。


onPause里执行耗时操作.png

3.与生命周期相关的一些重要方法

1)onSaveInstanceState与onRestoreInstanceState方法

前面提在介绍onCreate的时候提到过onCreate方法中带有一个Bundle参数,我们在正常启动Activity的时候打印这个参数会发先它是为null的。我们看google文档可以发现,这个参数只有当Activity重新初始化的时候才不会为null。那么什么叫重新初始化呢?举一个简单的例子,比方说手机横竖屏切换的时候如果我们没有在manifest文件的configchange属性里指定orientation|screenSize(在android4.0以上必须同时指定这两个属性,如果只写orientation不生效)就会在onPause之后调用到onSaveInstanceState(Bundle)方法,此时我们就可以往Bundle里存储一些数据,随后系统会杀死Activity然后再重启它,此时我们发现onCreate方法中的Bundle参数不为空:

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  Log.i(TAG, "MainActivity onCrate");
  if (savedInstanceState != null) {
    Log.i(TAG, "onCreate:" + savedInstanceState.getString(TAG));
  } else {
    Log.i(TAG, "onCreate:" + savedInstanceState);
  }
}

@Override
protected void onSaveInstanceState(Bundle outState) {
  super.onSaveInstanceState(outState);
  Log.i(TAG, "onSaveInstanceState");
  outState.putString(TAG, "outState");
}
初次启动的时候.png
横竖屏切换.png

我们很容易发现在横竖屏切换的时候会调用到onSaveInstanceState方法,然后在Activity再次启动起来的时候onCreate方法中的Bundle就不会为空并且我们可以读到我们存储的一些值。虽然在onCreate里我们可以读到Bundle的值并取出来使用,但是google更建议的方式还是在onRestoreInstanceState里来读这个值。onRestoreInstanceState在onStart方法执行完成后调用,因为此时试图的初始化工作已经做完了,再取出值来在视图上进行数据绑定更加合理。

2.onConfigurationChanged方法

3.onNewIntent方法

当我们在manifest文件里对Activity的launchmode进行设置为singleTop、singleTask、singleInstance的时候都会有可能调用到此方法。如图所示:

SingleTask模式下MainActivity演示.png

在这里我对MainActivity在manifest里配置了singleTask,首先应用程序起来进入了MainActivity,然我我们通过MainActivity进入到了SecondeActivity,最后通过SecondActivity来再次启动MainActivity,这个时候因为我们对MainActivity指定的是singleTask模式启动,所以MainActivity的onNewIntent被调用到了。其实当我们对MainActivity指定为singleTop,singleInstance的时候也同样会被调用到,究其原因在于该方法主要是用于复用该Activity实例的时候调用的。

4.configChange的各种属性

configChanges的各项属性.png

configchange里面有这么多属性都可以进行配置,下面对其中一些较为常用的进行一番解释说明。
locate:主要是指系统切换语言
keyboard:代表键盘类型发生了变化,比方说由系统的软键盘切换到外置键盘
fontScale:用户在设置里切换了字号
orientation:屏幕方向发生了变化,不过在android 4.0以上不好使需要同scrennSize一起使用。

5.Activity的启动模式

Android应用程序都是由一个或多个Activity组成的,而Android内部则是通过栈来对Activity进行管理的。所谓栈就是一个先进后出的数据结构。正常情况下栈顶的Activity就是当前Task显示的Activity,当我们按back键的时候该Activity便会出栈。然而事实并不是这么简单,google在对Activity任务栈进行设计的时候考虑到了一些特殊需求所以便有了Activity的启动模式之说。
Activity的启动模式包含四种,分别是:standard、singleTop、singleTask、singleInstance,我们可以在manifest里通过Activity的launchmode进行指定。下面我就逐个介绍一下这四中模式。

1)standard模式

这是Activity的标准启动模式,如果我们不对Activity做任何特殊处理的情况下就默认为该模式启动,所以该模式并不需要在manifest或者Intent里进行指定。这个模式的问题在于会导致一个任务栈里会有多个该Activity的实例存在,很简单的一个例子就是我们在AcitivytA里启动ActivityA这样就会有两个ActivityA存在。假如说该Activity非常消耗资源,那么就有必要考虑下更改下Activity的启动模式了。

2)singleTop模式

该模式简单来说,启动的Activity已经在任务栈的栈顶话,那么再启动这个Activity的时候就不会创建该实例,同时会调用该Acitivity的onNewIntent方法(前面有提过该方法)。但是如果该Activity不在栈定的话,那么启动它的行为与standard模式并没有什么区别。

3)singleTask模式

singleTask指的是一个任务栈中只能存在一个这样的Acitivity。但是需要我们注意的是如果任务栈中没有该Activity的话系统就会帮我们创建一个Acitivity压入栈顶,但是如果存在该Activity的话就会销毁压在该Activity上的所有Activity最终让创建出来的Activity实例处于栈顶,同时也会回掉该Activity的onNewIntent方法。

4)singleInstance模式

该模式是四个模式当中最为特殊的一个,设置了该模式启动的Acitivyt会在一个独立的任务栈中开启,同事该任务栈有且只有一个这样的Activity实例,每次再启动这个Activity的时候就会在该任务栈里重用该Activity同时回掉onNewIntent方法。
singleInstace与singleTask的区别在于:singleTask启动的Activity在系统层面上来说是可以有多个实例的。比如说应用程序A想调用singleInstance模式下的ActivityA,而应用程序B也同样调用了,那么在应用程序A和B中就会各有一个ActivityA的实例。但如果该ActivityA是singleInstance模式的话,那么无论有多少个应用程序调用它,它都只可能在系统中存在一个实例同时该实例还是位于它自己的一个单独的任务栈中。

5).通过Intent中setFlags来指定启动模式

1.Intent.FLAG_ACTIVITY_NEW_TASK

使用一个新的任务栈来启动一个Activity,该flag通常用于在Service中启动Activity的场景,因为Service中并不存在有Activity任务栈所以通常通过这种方式来新启动一个Activity任务栈并创建新的Activity实例。

2.Intent.FLAG_ACTIVITY_SINGLE_TOP

与在manifest文件里的launchmode指定"singleTop"作用一样

3.Intent.FLAG_ACTIVITY_CLEAR_TOP

与在manifest文件里的launchmode指定"singleTask"作用一样

4.Intent.FLAG_ACTIVITY_NO_HISTORY

设置了该Flags的Activity在启动其他Activity后该Activity就消失了,不会保留在Activity栈中,此Activity可以作为一个中转Activity来负责启动其他的Activity。

Activity的常用的基础知识就这么多了,关于更加深入的话题如Activity的启动流程等会在framework学习篇开始探讨。

推荐阅读更多精彩内容