Android四大组件之Activity

440 阅读10分钟

Android四大组件之Activity

Activity是Android四大组件中唯一的展示页面的组件,唯一的与用户可直接交互的组件。本篇文章从Activity的生命周期讲起,带你重拾Activity知识。

从本篇文章中你将学习到以下几点,若有不足之处请指正。

  • 第一次启动App至前台开始接受焦点时,经历着怎样的生命周期状态
  • 启动另一个页面或切换至另一个App时,经历着怎样的生命周期状态
  • 在各生命周期方法中需做哪些操作
    • 例:当播放网络视频时,切到另一个App,需暂停播放并终止网络连接,返回该App时恢复网络连接并允许用户从同样位置恢复播放
  • 如何使你的App在不使用时,不占用系统资源

什么是Activity

  • Android四大组件之一,提供屏幕进行交互
  • 会获得一个用于绘制其用户界面窗口,窗口可充满屏幕也可是小窗口并浮在其他窗口之上
  • 一个应用通常由一个或多个Activity组成,主Activity只有一个,为应用首次启动展示的页面
  • 不同Activity可跳转,新Activity启动时,旧Activity停止,并入返回栈中(与启动模式有关),新Activity也会入栈,并获取焦点。返回时退出返回栈,并销毁,回至上个Activity
  • 存在生命周期,发生变化,执行相关生命周期方法

管理你的Activity生命周期

Activity生命周期

  • Activity从开始至结束所经历的各种状态
    • 运行(Running):处于活跃状态,位于栈顶,状态可见,可与用户交互
    • 暂停(Paused):失去焦点,非全屏Activity或透明Activity位于栈顶,不可与用户交互
    • 停止(Stop):Activity被覆盖,其他Activity入栈,不可见
    • 回收(Kill):Activity被回收
    • 异常被杀执行onSaveInstance(),可保存信息,恢复执行onRestoreInstance()加载保存信息

当用户打开、退出App时,Activity同时经历着不同的生命周期状态。Activity完整的生命周期如下图所示 activity生命周期1.png

Activity的生命周期就像登机排队一样,由系统创建后,每回调一个生命周期函数就会往前一步,直到排到最前端就意味着轮到你了(可以和用户进行交互了)。此后会有两种情况:

  • 1.来了vip用户,需要插队排在了你的前面,你就会暂居后位继续等待排队,等vip用户登机后回至最前位置或一直等待所有vip用户完成登机
  • 2.登机直接走人,完成登机历程

Activity的生命周期方法并不需要我们都来实现,你只需根据具体的业务实现对应的方法即可。此时就需要我们了解每个方法的功能以确保你的App是按预期想象的那样执行

  • onCreate():首次创建时调用,创建视图,绑定列表数据等
  • onStart():可见,无焦点不可交互
  • onResume():可见,可交互
  • onPause():可见,失去焦点不可交互,弹出Dialog、退出或启动另一个Activity时回调
    • 不可做耗时操作,会影响下一个页面的显示时间
  • onStop():不可见,被后入栈Activity覆盖
  • onDestroy():Activity销毁前回调
  • onRestart():Activity已停止并即将再次启动重建时回调
  • 注意事项
    • 你的App使用时不会因启动其他App发生crash
    • 未使用某项功能时不应消耗系统资源(例未点击获取位置时不应使用定位API获取位置信息)
    • 离开你的App时要做好数据保存工作,以便用户返回时,还是当时状态
    • 设备发生语言切换、横竖屏切换时,不会丢失用户数据或发生crash

Activity的启动与销毁

本章节主要学习如何启动你的App以及如何执行Activity的创建与销毁,主要介绍onCreate()和onDestroy()的使用。

  • 和其他编程范式一样,Android App也有自己的main方法,在ActivityThread类中,但不是本章重点,就不展开讲述了,本小节的重点是App启动时涉及的Activity生命周期中最重要的回调函数。

主Activity设置

当用户点击App图标时,需要一个响应的Activity来启动你的App页面。系统首先会去查找你的App中声明为“launcher”或"main"的Activity,并启动它。

  • AndroidManifest.xml文件中将你的主Activity声明如下
    • 若未声明action->MAINcategory->LAUNCHER,设备Launcher页面就不会展示你的App图标
<activity android:name=".MainActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
    </intent-filter>
</activity>

Activity简单使用

  • onCreate()执行完毕会会迅速执行onStart()和onResume(),不会在onCreate()和onStart()中停留太久,最终停留在onResume()状态,直到其他干扰因素影响该状态(例弹框或启动其他Activity)。
  • onDestroy()执行后会将你的Activity从系统中移除(非卸载),一般无需实现该方法,资源清除操作应放在onPause()或onStop()中处理,但你在onCreate()中创建的一些资源需在该方法中释放。
    • 该方法在onPause()和onStop()之后执行,除非在onCreate()时就执行了finish()方法(例跳板Activity,用来启动系统浮窗App)
    • 该方法中需做好导致内存泄漏的资源释放
class MainActivity: AppCompatActivity() {
    private val binding: ActivityMaininding by lazy { ActivityMaininding.inflate(layoutInflater) }

    // Activity创建
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 创建视图
        setContentView(binding.root)
        doSomething()
    }
    
    private fun doSomething() {
    }

    // Activity销毁
    override fun onDestroy() {
        super.onDestroy()
        
        // 若存在需释放onCreate()中创建资源
        // 释放导致内存泄漏的资源
    }
}

Activity的暂停与恢复

本章节主要学习Activity暂停与恢复时,你应该做什么?主要介绍onResume()和onPause()的使用。

  • 在使用App时,当前展示的Activity页面有可能被Dialog弹框遮挡或被新的Activity页面遮挡(下节再讲),从而阻塞当前页面的交互,此时的Activity会进入Pause状态,调用Activity的onPause()方法
    • 此时Activity无焦点,不可与用户进行交互,此时可以做一些释放资源的操作,避免CPU资源浪费
      • 暂停视频播放
      • 停止动画执行
      • 广播、传感器等的解注册
      • 移除GPS定位监听
      • 若使用WebView需调用onPause()方法
      • 若使用Camera需调用release()释放资源
      • 不要在onPause()中执行耗时的释放资源操作,可放在onStop()中,onPause会影响进入下一页面的时间(下节再讲)
  • 当Dialog弹框关闭时,Activity从新获得焦点,从Pause状态恢复到Resume状态,会调用Activity的onResume()方法
    • 此时可以恢复一些操作
      • 恢复onPause()中的操作
class MainActivity: AppCompatActivity() {
    private val binding: ActivityMaininding by lazy { ActivityMaininding.inflate(layoutInflater) }
    
    // 传感器相关
    private val sensorManager = getSystemService(Context.SENSOR_SERVICE) as? SensorManager
    private var sensorListener: MySensorListener? = null

    // Activity恢复
    override fun onResume() {
        super.onResume()
        
        // 播放视频
        // 初始化动画执行
        // 广播注册
        // 若使用WebView需调用onResume()方法
        // 若使用Camera需初始化
        
        // GPS定位监听
        locationManager?.requestLocationUpdates(
            LocationManager.GPS_PROVIDER, 0, 0, locationListener
        )

        // 注册加速传感器
        val sensor = sensorManager?.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
        sensor?.let { 
            // 支持加速传感器,注册
            sensorListener = MySensorListener()
            sensorManager?.registerListener(sensorListener, it, SensorManager.SENSOR_DELAY_NORMAL)
        }
    }
    
    // Activity暂停
    override fun onPause() {
        super.onPause()
        
        // 暂停视频播放
        // 停止动画执行
        // 广播解注册
        // 若使用WebView需调用onPause()方法
        // 若使用Camera需调用release()释放资源
        
        // 移除GPS定位监听
        locationManager?.removeUpdates(locationListener)
        
        // 传感器解注册
        sensorListener?.let { 
            sensorManager?.unregisterListener(it)
        }
    }
    
    private inner class MySensorListener: SensorEventListener {
        override fun onSensorChanged(event: SensorEvent?) {
            // 处理传感器数据变化,x、y、z轴加速度,检测手机的晃动、倾斜等
            event?.let {
                val x = event.values[0]
                val y = event.values[1]
                val z = event.values[2]
            }
        }

        override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
            // 处理传感器精度变化
        }
    }
}

Activity的停止与重启

本章节主要学习离开你的App与重启时会发生什么,你需要做什么?主要讲述与暂停、恢复的区别点onStop()、onRestart()、onStart()的使用。

  • 首先介绍下Activity停止与重启的几种场景
    • 跳转至新Activity页面时停止,返回该页面时会重启
    • 点击通知或最近任务列表切换至另一个App时停止,点击桌面App图标和最近任务列表切回时会重启
    • Home键回至主页时停止,点击桌面App图标和最近任务列表切回时会重启
  • onStop()、onRestart()、onStart()是该场景下的主要生命周期方法
    • onStop()区别于onPause()的点在于,此时Activity已不可见并且焦点被切换至新的页面
      • 系统正常场景下会在Activity进入Stop状态时保留该Activity实例,简单业务场景下一般无需实现该方法,实现onPause()即可
        • 系统资源不够时可能会释放资源杀掉该应用(异常场景暂不考虑,留至下一章节)
        • 涉及耗时资源释放时,可重写onStop()释放资源
    • onRestart()方法中一般不做处理,只有从onStop()状态切回时才会回调该方法
    • onStart()区别于onResume()的点在于,此时Activity可见,但还无法获取焦点
      • 该方法在正常创建Activity和重启时都会回调
      • 可在该方法中做onStop()的恢复工作
  • 停止至重启经历的生命周期方法是onPause() -> onStop() -> onRestart() -> onStart() -> onResume()
class MainActivity: AppCompatActivity() {
    private val binding: ActivityMaininding by lazy { ActivityMaininding.inflate(layoutInflater) }

    // 该方法中一般不做处理,只有从onStop()状态切回时才会回调该方法
    override fun onRestart() {
        super.onRestart()
    }

    // 初始化onStop()中释放的资源
    override fun onStart() {
        super.onStart()
    }
    
    // 耗时的操作可在此处处理,避免阻塞下一个页面的开启
    override fun onStop() {
        super.onStop()
        val values = ContentValues()
        values.put("key", "value")
        contentResolver.update(uri, values, null, null)
    }
}

Activity的重建

本章主要学习哪些场景会导致Activity被销毁并重建,此时我们应该做什么

  • 正常销毁场景(此处只列举不展开讲述)

    • 按后退键退出当前页面,用户主动退出,无需做任何处理
    • 业务逻辑需要退出页面的finish()方法调用
  • 异常场景销毁(此时需要做好数据恢复准备)

    • 系统因资源不足时关闭后台进程,再次进入会进入重建过程
    • 长时间Stop状态的Activity也可能被系统关闭,再次进入会进入重建过程
    • 横竖屏切换时,会经历销毁重建过程,以加载一些资源(例横竖屏采用不同的布局资源)
  • 数据保存

    • 默认情况下,一般都使用Bundle对象的key-value保存页面数据(例有编辑框时,保存编辑框内容以便恢复;还有其他状态数据等等)
    • onSaveInstanceState(Bundle)和onRestoreInstanceState(Bundle)是Android系统提供了两个用于保存和恢复数据的方法
    • 离开Activity时可在onSaveInstanceState(Bundle)中保存数据,以便异常场景恢复重建时在onRestoreInstanceState中取出数据恢复原状态
    • onRestoreInstanceState在onStart之后执行,恢复数据最好在该方法中,无需判空
class MainActivity: AppCompatActivity() {
    private val binding: ActivityMaininding by lazy { ActivityMaininding.inflate(layoutInflater) }

    // 在此方法中恢复数据,无需判空;尽量不要在onCreate中恢复
    override fun onRestoreInstanceState(savedInstanceState: Bundle) {
        super.onRestoreInstanceState(savedInstanceState)
        val state = savedInstanceState.getInt("")
        val editText = savedInstanceState.getString("")
    }
    
    // 需要调用父类的onSaveInstanceState数据才会保存
    override fun onSaveInstanceState(outState: Bundle) {
        outState.putInt("", status)
        outState.putString("", editText)
        super.onSaveInstanceState(outState)
    }
}

小结

正常场景生命周期回调

  • 启动app:onCreate -> onStart -> onResume

    • Home键回至主页面:onPause -> onStop
    • Home键回至主页面再次打开app:onRestart -> onStart -> onResume
    • 返回键:onPause -> onStop -> onDestroy
  • 跳转至其他页面:onPause(A) -> onCreate(B) -> onStart(B) -> onResume(B) -> onStop(A)

    • 新页面返回键:onPause(B) -> onRestart(A) -> onStart(A) -> onResume(A) -> onStop(B) -> onDestroy(B)

异常场景生命周期回调

  • onCreate时调用finish,做中转Activity
  • 横竖屏切换时未作任何处理,重走生命周期方法,从onCreate开始
  • 栈顶Activity或singleTask的Activity再次启动时直接执行onNewIntent方法,不会执行onCreate