Android 中级圣经系列之Activity

该系列文章主要介绍android里基本组件的了解和使用

圣经?该系列文章将以标签的形式添加,在以后的工作中也会不断完善,也希望有其他朋友补充,最终希望能让阅读的人通过一篇文章就能解决了解与这组件相关的大部分问题,比如说第一篇Activity,除了常见的生命周期外,还会介绍activity的创建过程,启动模式等等

中级?如果要把android工程师分初中高三个等级的话,个人觉得应该达到以下要求


初级:能熟练使用android的view控件和activity,灵活得在前台后台线程进行切换,根据产品需求,完成ui层面的任务


中级:熟练使用android四大组件,初步理解背后实现的机制和原理,理解事件分发三大关键函数dispatchTouchEven,onInterceptTouchEvent,onTouchEvent,能够解决大部分父子节点事件传递的问题,理解NestedScroll(嵌套滑动接口),熟悉view的绘制流程(onMeasure-》onLayout-》onDraw)及这三个函数在view和viewgroup之间的不同点,能熟练得自定义view。


高级:看过并理解view的源码,对于自定义view之类的不再觉得是问题,研究理解优秀的android框架,比如Fresco(图片加载),Volley(网络请求),需要时可以自己开发简化版,游刃有余地处理内存的使用和回收,处理不同线程的并发情况,能在项目中运用合适的开发模式。


希望通过这系列教程和自己的实践,能让大家达到中级的开发水平

Activity生命周期(地球人都知道),但你知不知道onPause和onStop的区别?为什么要存在两个不同的回调?onDestory实际上做了什么?

当当前activity失去焦点时,会马上触发onPause(比如跳转到新的activitiy,切到后台),之后过一段时间后假如检测改activity依然在后台,此时才会触发onStop。

当新打开的Activity是Dialog风格时或者是透明时;这两种情况只会调用onPause方法,但不会调用onStop方法。

销毁activity时如果内存紧张,系统会直接结束这个Activity,而不会触发 onStop 方法。所以保存临时数据的方法必须放在onPause做,而不是onStop。

onDestroy实际上并没有马上把activity销毁,而是把该activity从当前的activity栈中移除,大多数 app并不需要实现这个方法,因为局部类的references会随着activity的销毁而销毁,但是加入这个时候你的activity被其他地方强引用(例如有个动画不断在执行),这时候你会发现在即使onDestroy被调用,但是改activity依然存在系统的内存中,所以单单依靠这个函数来回收资源是不够的。

顺便说一下Fragment的生命周期

Fragment是和activity绑定的,但创建时首先会触发onAttach,此时可以获取到context对象,
onAttach方法:Fragment和Activity建立关联的时候调用。
onCreateView方法:为Fragment加载布局时调用。
onActivityCreated方法:当Activity中的onCreate方法执行完后调用。
onDestroyView方法:Fragment中的布局被移除时调用。
onDetach方法:Fragment和Activity解除关联的时候调用。

activity为什么要细化出onCreate、onStart、onResume、onPause、onStop、onDesdroy?相信很多人为这个问题困扰很久,先来看一个图,从Activity A跳转到Activity B的回调顺序

可以发现跳转发生时,先调用了A的onPause,之后等到B的onStart和onResume完成后才调用A的onStop,这样设计的目的在于让开发者可以在不同阶段进行不同资源的释放或者创建,举个例子,先看下onResume的官方定义
void android.app.Activity.onResume()
Called after onRestoreInstanceState, onRestart, or onPause, for your activity to start interacting with the user. This is a good place to begin animations, open exclusive-access devices (such as the camera), etc.

Keep in mind that onResume is not the best indicator that your activity is visible to the user; a system window such as the keyguard may be in front. Use onWindowFocusChanged to know for certain that your activity is visible to the user (for example, to resume a game).

“open exclusive-access devices (such as the camera)",比如你的应用有打开camera的操作,假如camera的初始化你放到了onCreate里面,释放放到了onStop或者onDestroy里面,假设A和B都需要camera的资源,那么当A跳转到B时,B在oncreate创建camera引用,就会出现A里面的camera还没有释放的问题,所以google建议是在onResume()中打开独占设备(比如相机),在onpause里把这些资源释放出去。

所有的初始化都在onCreate()中实现?
onCreate()代表activity被创建,onStart代表该Activity变成可见状态,Activity的onCreate()被调用时,Activity还不可见,如果要做一些动画,既然视图还不存在,在onCreate中来启动动画,明显有问题;

其次,A Activity 切换到 B Activity,再切换到 A Activity,由于实例已经存在,所以onCreate不会再被调用,那AActivity从后台切换至前台时,有可能需要一些初始化,那就没法再被调用到了;

所有的初始化都在onStart()中实现?
onStart() 被调用时,Activity可能是可见了,但还不是可交互的,onResume()的注释中都明确地说了这不是Activity对用户是可见的最好的指示器,onStart() 在这之前被调用,那有一些特殊的初始化相关的逻辑在这里被调用也会有问题。

这只是很简单的例子,实际中要根据实际情况处理,总的来说可以把一些view的初始化工作放到onCreate里面,在onstart时可以开启一些动画事件,最后在onresume时才打开独占设备,在onpause时把动画停止,独占设备的资源释放出去,停止网络轮询,而不是onStop,最后看一下官方解释:
void android.app.Activity.onStop()
Called when you are no longer visible to the user. You will next receive either onRestart, onDestroy, or nothing, depending on later user activity.
Note that this method may never be called, in low memory situations where the system does not have enough memory to keep your activity's process running after its onPause method is called.
onStop() 的注释中明确地写了,在内存不足而导致系统无法保留此进程的情况下,onStop() 可能都不会被执行。

Acitity启动模式:
涉及关键词“LaunchMode",”Task stack“,“intent”,由于intent我们之后还有一篇文章专门来讲,所以主要看前面两个。

LauchMode有四种,分别为standard,singleTop,singleTask,singleInstance,可以在xml里面配置好,也可以在startactivity启动时设定,后者优先级高于前者。在这里先记录一下官方的定义

"standard":Default. The system always creates a new instance of the activity in the target task and routes the intent to it.

"singleTop":If an instance of the activity already exists at the top of the target task, the system routes the intent to that instance through a call to itsonNewIntent()method, rather than creating a new instance of the activity.

"singleTask":The system creates the activity at the root of a new task and routes the intent to it. However, if an instance of the activity already exists, the system routes the intent to existing instance through a call to itsonNewIntent()method, rather than creating a new one.

"singleInstance":Same as "singleTask", except that the system doesn't launch any other activities into the task holding the instance. The activity is always the single and only member of its task.

这里注意一点,就是singleTop其实是并不一定保证唯一性的,比如一个视频播放的activity,当点击home健退到后台后,通过点击notification到splash页面再回来这个activity,其实已经不是之前那一个了,已有的信息会被删除掉。这种情况可以考虑使用singletask

接着看一下startactivity时可以设置的flag有哪些:

FLAG_ACTIVITY_BROUGHT_TO_FRONT
这个标志一般不是由程序代码设置的,如在launchMode中设置singleTask模式时系统帮你设定。

FLAG_ACTIVITY_SINGLE_TOP
如果设置,当这个Activity位于历史stack的顶端运行时,不再启动一个新的。

FLAG_ACTIVITY_NEW_TASK
如果设置,这个Activity会成为历史stack中一个新Task的开始。一个Task(从启动它的Activity到下一个Task中的 Activity)定义了用户可以迁移的Activity原子组。Task可以移动到前台和后台;在某个特定Task中的所有Activity总是保持相同的次序。

这个标志一般用于呈现“启动”类型的行为:它们提供用户一系列可以单独完成的事情,与启动它们的Activity完全无关。

使用这个标志,如果正在启动的Activity的Task已经在运行的话,那么,新的Activity将不会启动;代替的,当前Task会简单的移入前台。参考FLAG_ACTIVITY_MULTIPLE_TASK标志,可以禁用这一行为

这个标志不能用于调用方对已经启动的Activity请求结果。

FLAG_ACTIVITY_CLEAR_TOP(top的意思是指盖在该activity上面,比他迟创建启动的)
如果设置,并且这个Activity已经在当前的Task中运行,因此,不再是重新启动一个这个Activity的实例,而是在这个Activity上方的所有Activity都将关闭,然后这个Intent会作为一个新的Intent投递到老的Activity(现在位于顶端)中。

例如,假设一个Task中包含这些Activity:A,B,C,D。如果D调用了startActivity(),并且包含一个指向Activity B的Intent,那么,C和D都将结束,然后B接收到这个Intent,因此,目前stack的状况是:A,B。

上例中正在运行的Activity B既可以在onNewIntent()中接收到这个新的Intent,也可以把自己关闭然后重新启动来接收这个Intent。如果它的启动模式声明为 “multiple”(默认值),并且你没有在这个Intent中设置FLAG_ACTIVITY_SINGLE_TOP标志,那么它将关闭然后重新创建;对于其它的启动模式,或者在这个Intent中设置FLAG_ACTIVITY_SINGLE_TOP标志,都将把这个Intent投递到当前这个实例的onNewIntent()中。

这个启动模式还可以与FLAG_ACTIVITY_NEW_TASK结合起来使用:用于启动一个Task中的根Activity,它会把那个Task中任何运行的实例带入前台,然后清除它直到根Activity。这非常有用,例如,当从Notification Manager处启动一个Activity。

FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
如果设置,这将在Task的Activity stack中设置一个还原点,当Task恢复时,需要清理Activity。也就是说,下一次Task带着 FLAG_ACTIVITY_RESET_TASK_IF_NEEDED标记进入前台时(典型的操作是用户在主画面重启它),这个Activity和它之上的都将关闭,以至于用户不能再返回到它们,但是可以回到之前的Activity。

这在你的程序有分割点的时候很有用。例如,一个e-mail应用程序可能有一个操作是查看一个附件,需要启动图片浏览Activity来显示。这个 Activity应该作为e-mail应用程序Task的一部分,因为这是用户在这个Task中触发的操作。然而,当用户离开这个Task,然后从主画面选择e-mail app,我们可能希望回到查看的会话中,但不是查看图片附件,因为这让人困惑。通过在启动图片浏览时设定这个标志,浏览及其它启动的Activity在下次用户返回到mail程序时都将全部清除。

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
如果设置,新的Activity不会在最近启动的Activity的列表中保存。

FLAG_ACTIVITY_FORWARD_RESULT
如果设置,并且这个Intent用于从一个存在的Activity启动一个新的Activity,那么,这个作为答复目标的Activity将会传到这个新的Activity中。这种方式下,新的Activity可以调用setResult(int),并且这个结果值将发送给那个作为答复目标的 Activity。

FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
这个标志一般不由应用程序代码设置,如果这个Activity是从历史记录里启动的(常按HOME键),那么,系统会帮你设定。

FLAG_ACTIVITY_MULTIPLE_TASK
不要使用这个标志,除非你自己实现了应用程序启动器。与FLAG_ACTIVITY_NEW_TASK结合起来使用,可以禁用把已存的Task送入前台的行为。当设置时,新的Task总是会启动来处理Intent,而不管这是是否已经有一个Task可以处理相同的事情。

由于默认的系统不包含图形Task管理功能,因此,你不应该使用这个标志,除非你提供给用户一种方式可以返回到已经启动的Task。

如果FLAG_ACTIVITY_NEW_TASK标志没有设置,这个标志被忽略。

FLAG_ACTIVITY_NO_ANIMATION
如果在Intent中设置,并传递给Context.startActivity()的话,这个标志将阻止系统进入下一个Activity时应用 Acitivity迁移动画。这并不意味着动画将永不运行——如果另一个Activity在启动显示之前,没有指定这个标志,那么,动画将被应用。这个标志可以很好的用于执行一连串的操作,而动画被看作是更高一级的事件的驱动。

FLAG_ACTIVITY_NO_HISTORY
如果设置,新的Activity将不再历史stack中保留。用户一离开它,这个Activity就关闭了。这也可以通过设置noHistory特性。

FLAG_ACTIVITY_NO_USER_ACTION

如果设置,作为新启动的Activity进入前台时,这个标志将在Activity暂停之前阻止从最前方的Activity回调的onUserLeaveHint()。

典型的,一个Activity可以依赖这个回调指明显式的用户动作引起的Activity移出后台。这个回调在Activity的生命周期中标记一个合适的点,并关闭一些Notification。

如果一个Activity通过非用户驱动的事件,如来电或闹钟,启动的,这个标志也应该传递给Context.startActivity,保证暂停的Activity不认为用户已经知晓其Notification。

FLAG_ACTIVITY_REORDER_TO_FRONT
如果在Intent中设置,并传递给Context.startActivity(),这个标志将引发已经运行的Activity移动到历史stack的顶端。

例如,假设一个Task由四个Activity组成:A,B,C,D。如果D调用startActivity()来启动Activity B,那么,B会移动到历史stack的顶端,现在的次序变成A,C,D,B。如果FLAG_ACTIVITY_CLEAR_TOP标志也设置的话,那么这个标志将被忽略。

Activity是运行在『所需的任务栈』,什么是Activity所需的任务栈?这里就要提一个参数:TaskAffinity。这个参数标识了一个Activity所需的任务栈的名字,默认情况下所有的Activity所需的任务栈都是当前包名,当然我们也可以为每个Activity单独指定TaskAffinity,注意这个属性值必须不能和包名相同,TaskAffinity主要和singleTask配合使用,否则的话没什么意义。当启动一个被TaskAffinity标识了的Activity,那么该Activity就会运行在和TaskAffinity相同的任务栈中



最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容