Android Activity 生命周期

2019-12-27 作者:新闻   |   浏览(147)

本文内容

  • 概述
  • Activity 和生命周期
  • 演示 Activity 生命周期
  • 参考资料

最近研究 Android ,很多东西都需要仔细体会~期间,本来是想看点网上现成的项目加快学习速度,但导入到 eclipse 后,很多代码行都出现问题提示,虽然代码仍可以运行,但说明代码对新版本来说,Android 已经不推荐使用了,而有更合适的解决方案。Android 在发展过程中,每个版本的变化都比较大,这对学习 Android 的障碍比较大,最好的方法也只能去官网下载最新的开发工具,以及演示代码来学习了。

[toc]

概述


学习任何语言和 IDE,不是会拖放几个控件,就完事了。若想搞好 Android,就要根据之前开发 Web 程序的经验,好好体会它的设计思想。

先从 Activity、View 和 Fragment 开始,它们是很重要的概念,也是 Android 的基类。你可以想想,Java 中的 .jsp 文件和其后台文件,以及 Java 部件;.Net 中的 .aspx 文件及其后台文件,以及 .Net 组件、用户控件等。一个 Web 页面当然可以放很多组件,ASP.NET 所有页面创建时,都会继承 System.Web.UI.Page,而所有控件都继承 System.Web.UI.Control。这类似于 Activity 和 View。你的 Android 页面都可以继承 Activity 类,Android 所有控件都继承 View 类。

如果一个 Web 页面都不在了,页面上的组件也不会存在。也就是说,如果 Activity 不存在了,View 和 Fragment 也不会存在。

HTML 文件,在互联网刚开始时,前端开发人员喜欢用 table 标记为页面布局,但到了现在,人们更喜欢用 DIV 布局。尤其是对现在的 RIA。这有点类似于 Activity 和 Fragment,一个 Activity 可以用在多个 Fragment(片段),一个 Fragment 可以用在多个 Activity 上。

 

Android面试题

Android面试题除了Android基础之外,更多的问的是一些源码级别的、原理这些等。所以想去大公司面试,一定要多看看源码和实现方式,常用框架可以试试自己能不能手写实现一下,锻炼一下自己。

Activity 和生命周期


当用户浏览(无论是使用中,还是退出和返回)你的应用程序时,应用程序的 Activity 实例在其生命周期中会在不同状态之间的过渡。例如,当你的 Activity 第一次启动时,它恢复(Resume)到系统前台,并接受用户焦点(操作)。在此过程中,Android 系统在 Activity 上调用一系列生命周期的方法,这些 Activity 是你建立的用户界面以及其他组件。如果用户执行一个动作,启动另一个 Activity,或切换到另一个应用程序,系统在你 Activity 上会调用另一套生命周期的方法,这样该 Activity 移动到后台(Activity 不再可见,但实例和它的状态保存不变)。

在生命周期回调方法内,你可以声明,当用户离开和重新进入你的 Activity 时,Activity 将如何。例如,如果你构建一个流媒体播放器,当用户切换到另一个应用程序时,你可能要暂停(Pause)视频,终止网络连接。当用户返回时,你要重新连接网络,允许用户从上次看到的地方重新播放。

加拿大28开奖结果 1 

图 1 生命周期回调方法

实际开发中,我们经常重构的三个方法是 onCreate、onPause 和 onResume。例如,onCreate 经常用于初始化页面;onPause 经常用于保存应用状态,当你正在玩游戏时,有人给你打电话,此时应用程序会自动调用该方法,那么,你就需要保存一下游戏状态,这样听完电话后,可以继续游戏。

 

一、Android基础知识点

演示 Activity 生命周期


在 Android 中文社区发现这个不错的演示,很能说明 Activity。了解 Activity 生命周期的意义在于,Android 虽然与 Web 很相似,但差异也蛮大的,毕竟手机屏幕大小有限。生命周期回调方法,每个 Activity 实例接收,以及如何使用它们,这样的话,你的 Activity 完成了用户所期望的,而不会消耗系统资源。

本实例中,有三个 Activity(Activity A、Activity B 和 Activity C),和对话框(Dialog),当操作其中一个时,另几个会处于何种状态。

下面三组图,是连续操作的。

加拿大28开奖结果 2

图 2 左:运行主程序;中:在左图中点击“Start B”;右:在中图中点击“Start C”

Lifecycle Method List 区域表示 Activity 经历的生命周期,Activity Status 区域表示三个 Activity 的当前状态。

注意:当操作其中一个时,另几个会处于何种状态。比如,当运行主程序时,Activity A 先后经历了“创建-开始-恢复”;当在主程序中点击“Start B”时,Activity B 先后经历了“创建-开始-恢复”,而 Activity  A 变成了“暂停-恢复-暂停-停止”,此时,Activity  B 处于活动状态,A在后台;当再点击“Start C”时,Activity C 处于活动状态,Activity A 和 Activity B 处于后台。

加拿大28开奖结果 3 

图 3 左:在图 2 右图的基础上,点击“Finish C”;右:接着点击“Finish B”

最终,Activity C 和 Activity B 结束后,都被销毁了,只剩下 Activity  A。

加拿大28开奖结果 4 

图 4 点击“Dialog”

当只剩下 Activity A 时,点击“Dialog”按钮,弹出一个对话框,此时,Activity A 处于暂停状态,当关闭对话框后,Activity A 又会恢复。

最后,若点击“Finish A”,则该程序将完全退出。

该示例,对理解 Activity 很有帮助,具体代码不多说,文后有源代码,下载后看了估计就会明白。

你可以把 Activity 看成是 Web 页面,View 看成 Web 控件,而 Fragment 有点类似于 DIV,对于现在的 RIA 来说,如果你向界面添加一个控件,并用 Firefox 调试,你会发现会有很多 DIV 嵌套,越是够炫的 RIA,DIV 就越多。

Activity、View 和 Fragment 也具有相似性,比如,列表那样的外观和功能,无论是 Activity、View 和 Fragment 都可以实现(它们的相应子类),也就是页面、控件和片段都可以实现列表功能,如果你刚开始做了一个具有列表功能的 Activity,也就是继承了 Activity 基类,但是后来出于性能考虑,觉得用 Fragment,把这个  Fragment 用到多个 Activity 上更合适(新闻列表与新闻的内容),那直接将继承 Activity 基类改成继承 Fragment 基类就行,Android 这样做看似“重复、多余”,其实主要是因为,Android 跟 Web 毕竟场景不同,手机屏幕大小有限,还要考虑性能。

 

四大组件是什么

  1. Activity
    其中Activity是我们最常使用的组件,也是我们最熟悉的组件,一般用于呈现页面内容,处理用户交互等等

  2. Service
    Service是一个可以运行于后台的组件,我们一般用于处理一些不需要用户知道,但是又必须比较长时间存在的操作,比如下载

  3. ContentProvider
    主要用于进程间通信,比如暴露某个APP的信息内存给予另外一个APP获取使用,比如获取联系人等等

  4. BroadcastReceiver
    广播接受者主要用于接受广播信息,像我们日常使用中可能用于两种情况:

    • APP内:界面间通信,例如退出app,可以发送自杀广播
    • APP间:可以收到第三方发出的广播,进而进行对应的响应操作。

参考资料


 

下载 Demo

下载 Demo 2

四大组件的生命周期和简单用法

Activity
  1. 生命周期

oncreate()->onstart()->onresume()->onpause()->onstop()->ondestory()

  1. 简单用法
        /**创建App*/
        class someClass :Activity() 
        /**跳转app*/
        startActivity(Intent(this@someActivity,targetClass::class.java))
Service
  1. 生命周期
  • startService()的生命周期:
    oncreate()->onstartComment()->onstart()->onDestory()
  • bindService()的生命周期:
    oncreate()->onbind()->onUnbind()->onDestroy()
  1. 简单用法
    • startService()
      通过简单的startService()进行service启动,此后启动该Service的组件无法把控Service的生命周期,理论上此后该Service可以在后台无期限运行(实际根据情况该Service可能会在任意一个时刻被杀死,这里牵连到了另外一个知识点:Service防杀
      我们可以在onStartCommand()里面做我们要做的操作,注意Service跟Activity一样不可以做耗时操作,虽然运行anr时间比Activity多了近一倍。
    • bindService()
      通过绑定的方式启动Service
      绑定后,该Service与启动绑定操作的组件形成绑定,当组件销毁时,该Service也随着销毁。
      其中组件与Service形成一个典型的BC体系,Service相当于服务器,组件可以通过IBinder像Service 发送请求并获取回应。
  public boolean bindService(Intent service, ServiceConnection conn,
                int flags)
BroadcastReceiver(分为2种):
  1. 简单用法
    • 静态注册(常驻广播)
      在AndroidManifest.xml中进行注册,App启动的时候自动注册到系统中,不受任何组件生命周期影响,(即便应用程序已经关闭),但是 耗电占内存
    • 动态注册(非常驻广播)
      在代码中进行注册,通过IntentFilter意图过滤器筛选需要监听的广播,
      记得注销(推荐在onResume()注册,在onPause()注销),使用灵活,生命周期随组件变化
全局广播
  1. 普通广播(最常用的那种)
  2. 系统广播
    系统广播无须开发者进行发送,我们只需做好广播接收器进行接收即可,常用的几个系统广播为:
    android.net.conn.CONNECTIVITY_CHANGE 监听网络变化
    Intent.ACTION_PACKAGE_ADDED 成功安装apk
    ... ...
  3. 有序广播
    有序广播是指广播按照一定的优先级被广播接受者依次接收,代码实例
    发送广播
    定义2个广播接受者
    动态注册2个广播接受者,设置不同的优先级
    高优先级的广播接受者接受信息,篡改信息,塞回篡改后的信息
    低优先级广播接收源信息,接收上一个广播信息,分别打印出来
class MainActivity : AppCompatActivity() {
                            companion object {
                                fun sayLog(message: String) = Log.e("THIS IS THE TAG", message)
                            }

                            override fun onCreate(savedInstanceState: Bundle?) {
                                super.onCreate(savedInstanceState)
                                setContentView(R.layout.activity_main)
                                tvMainSendBroadcast.setOnClickListener {
                                    sendBroadcast()
                                }

                                // register 高等级的广播 等级为100
                                val firstReceiver = BroadcastReceiverOrderFirst()
                                val intentFilter = IntentFilter()
                                intentFilter.addAction("lY_ACTION")
                                intentFilter.priority = 100
                                registerReceiver(firstReceiver, intentFilter)

                                // register 低等级的广播 等级为50
                                val lowReceiver = BroadcastReceiverOrderLow()
                                val intentFilterLow = IntentFilter()
                                intentFilterLow.addAction("lY_ACTION")
                                intentFilterLow.priority = 50
                                registerReceiver(lowReceiver, intentFilterLow)
                            }

                            /**发送普通广播*/
                            private fun sendBroadcast() {
                                val targetIntent = Intent()
                                val bundle = Bundle()
                                bundle.putString("message", "this is the def message")
                                targetIntent.action = "lY_ACTION"
                                targetIntent.putExtra("extra", bundle)
                                sendOrderedBroadcast(targetIntent, null)
                            }

                            /**
                             * 优先级比较高的广播接受者
                             */
                            class BroadcastReceiverOrderFirst : BroadcastReceiver() {
                                override fun onReceive(context: Context?, intent: Intent?) {
                                    val bundle = intent?.getBundleExtra("extra")
                                    sayLog("BroadcastReceiverOrderFirst has the message,the message is                                ${bundle?.getString("message")}")
                        //            修改携带的信息
                                    bundle?.putString("message", " this is not the def message")
                        //            把修改好的信息put进结果集
                                    setResultExtras(bundle)
                                }


                            }

                            /**
                             * 优先级比较低的广播接受者
                             */
                            class BroadcastReceiverOrderLow : BroadcastReceiver() {
                                override fun onReceive(context: Context?, intent: Intent?) {
                                // 获取源数据
                                    val bundle = intent?.getBundleExtra("extra")
                                    sayLog("BroadcastReceiverOrderLow has the message,the message is                                      ${bundle?.getString("message")}")
                                // 获取上一个广播传递过来的数据
                                    val bundleForPre=getResultExtras(true)
                                    sayLog("BroadcastReceiverOrderLow has the message,the modify is                                       ${bundleForPre?.getString("message")}")

                                }

                            }
                        }

                    }
本地广播

上面所讲的广播都是全局广播,全局广播指的是,我在甲App发送了一条广播,其中已App也可以收到这个广播信息,存在问题为:

  1. 其他app可以发送相对应的 intent-filter到我方App,导致我方错误处理
  2. 其他app可以设置对应的 intent-filter接收我方App发出的广播,导致安全性问题
    为解决这个问题,我们可以使用本地广播:
    1.方式一:
    1. 设置exported为false(默认为true),使该广播只在app内传递
    2. 广播传递和接收时,添加响应的permission
    3. 通过intent.setPackage(com.xxx.xxx)
  3. 方式二 LocalBroadcastManager:
 /**发送普通广播*/
    private fun sendBroadcast() {
        val targetIntent = Intent()
        val bundle = Bundle()
        bundle.putString("message", "this is the def message")
        targetIntent.action = "lY_ACTION"
        targetIntent.putExtra("extra", bundle)
        val localBroadcastReceiver =LocalBroadcastManager.getInstance(this@MainActivity)
        localBroadcastReceiver.sendBroadcast(targetIntent)



        // register
        val lowReceiver = BroadcastReceiverOrderLow()
        val intentFilterLow = IntentFilter()
        intentFilterLow.addAction("lY_ACTION")
        intentFilterLow.priority = 50
        localBroadcastReceiver.registerReceiver(lowReceiver,intentFilterLow)
    }
ContentProvider
  1. contentProvider 内容提供者
  2. contentresolver 内容解析器

Activity之间的通信方式

  1. 通过广播的方式进行通信
  2. 通过Intent的方式通信(startActivityForResult)
  3. 借助类的静态成员/方法进行通信
  4. 使用外部工具(sqlite/sharePreference/file/剪切板...)
  5. 全局变量(静态配置类/Application)
  6. Service(IPC模型),也就是上文的bingService()
  7. 通过EventBus/rxBus 进行通信

Activity各种情况下的生命周期

  1. 正常情况下是:onCreate()->onStart()->onResume()->onPause()->onStop()->onDestory()
  2. 被dialog遮罩情况下:
    1. 如果是自身的dialog那么不会走自己的生命周期
    2. 如果是其他组件传递的dialog,那么会走onpause()
  3. 可以看到我们前面有一个onRestart()的生命周期,这个生命周期我们不常用,具体是什么时机会被调用呢:
    1. home键,然后切换回来
    2. 跳转到另外一个activity,然后back键
    3. 从本应用跳转到另外一个应用,然后回来

... 其他

横竖屏切换的时候,Activity 各种情况下的生命周期

2种情况:

  1. 不做任何配置,生命周期重新走一遍:完整流程为:onCreate()->onStart()->onResume()->onPause()->onSaveInstanceState()->onStop()->Ondestory()->onCreate()->onStart()->onRestoreInstanceState()->onResume()。
  2. 做配置:android:configChanges="orientation|screenSize"
    横竖屏不走其他生命周期

其中onSaveInstanceState()可以保存用户数据,对应的onRestoreInstanceState()可以读取之前保存的用户数据

Activity与Fragment之间生命周期比较

activity有7个生命周期,fragment有十一个生命周期

加拿大28开奖结果 5

fragment以及activity生命周期

其中他们的关系是:

  1. 创建的时候:Activity 带动 Fragment 走生命周期,表格关系为:
所属 Activity Fragment
创建操作 onCreate onAttach/onCreate/onCreateView/onActivityCreated
创建操作 onStart onStart()
  1. 销毁的时候:Fragment 带动 Activity 走生命周期,表格关系为:
所属 Fragment Activity
创建操作 onPause() onPause()
创建操作 onStop() onStop()
创建操作 onDestroyView()/onDestroy()/onDetach() onDestroy()

Activity上有Dialog的时候按Home键时的生命周期

两个Activity 之间跳转时必然会执行的是哪几个方法?

假设有2个Activity A,B,在A里面激活B
A:调用onPause()
B:调用onCreate(),onStart(),onResume()
如果B是个正常的Activity,那么B会覆盖A,那么A会走onStop()
如果B不能覆盖A,那么A不会走onStop()

前台切换到后台,然后再回到前台,Activity生命周期回调方法。弹出Dialog,生命值周期回调方法

  1. 前台切换到后台:onPause()->onStop()
    如果正常切换回来,那么会走onRestart()->onStart()->onResume()
    如果很久没切回来,系统内存紧急被回收了,那么回来会重新走一次生命周期
  2. 弹出dialog
    如果dialog是自身Activity弹出来的,则不会走生命周期
    如果不是自身Activity弹出来的,则走onPause(),退出Dialog后走onRestart()->onResume()

Activity的四种启动模式对比

standard

这个是Activity的默认启动方式,我们不需要额外的配置
在该配置下,启动一个Activity就会在该应用的Activity栈中压入一个Activity,返回的时候就直接把该Activity弹出栈。

singleTop

这个是栈顶复用模式
加拿大28开奖结果,在该配置下,如果在Activity栈,栈顶是该Activity,那么会走onNewIntent()->onResume()
如果不是,那么就走正常的生命周期

singleInstance

这个是栈内复用模式
在该配置下,如果在该Activity栈,栈内存在该Activity(没有要求是栈顶),那么会走onNewIntent()->onResume(),并且把位于该Activity上方的Activity全部出栈,使该Activity位于栈顶

singleTask

这个配置下,Activity独享一个Activity栈。

Activity状态保存与恢复

override fun onSaveInstanceState(outState: Bundle?){} 保存状态
override fun onRestoreInstanceState(savedInstanceState: Bundle?){} 恢复状态

Fragment状态保存startActivityForResult是哪个类的方法,在什么情况下使用?

startActivityForResult是FragmentActivity的方法。
其实和Activity的startActivityForResult是一样的,只不过需要注意在Fragment里面调用的话,直接使用:

val targetIntent = Intent(this@MyFragment.context, DialogActivity::class.java)
startActivityForResult(targetIntent, 1)

而不需要使用getActivity()/activity:

val targetIntent = Intent(this@MyFragment.context, DialogActivity::class.java)
activity.startActivityForResult(targetIntent, 1)

本文由加拿大28开奖官网发布于新闻,转载请注明出处:Android Activity 生命周期

关键词: