彻底掌握Android中的Lifecycle

news2025/1/18 19:07:50

彻底掌握Android中的Lifecycle

Lifecycle 是一个生命周期感知型组件,属于 Jetpack 组件库中的一部分,其核心功能是将组件(如Activity 和 Fragment)的生命周期状态通知给观察者(LifecycleObserver)。观察者可以根据这些状态变化来执行相应的操作,如初始化资源、释放资源、更新数据等。通过使用 Lifecycle,开发者可以避免将业务代码和组件强关联,从而提高代码的可读性和可复用性。

Lifecycle使用方式

Lifecycle 的使用方式非常简单,通过 LifecycleOwner 获取 Lifecycle 对象,并添加生命周期观察者就可以了。ComponentActivity 和 Fragment 都默认实现了 LifecycleOwner ,以 Activity 为例,使用方式如下:

class LifecycleActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_lifecycle)

        //方式一:添加观察者(推荐)
        lifecycle.addObserver(object : DefaultLifecycleObserver {
            override fun onCreate(owner: LifecycleOwner) {
                //doAction...
            }
        })
        //方式二:添加观察者
        lifecycle.addObserver(object : LifecycleObserver {
            @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
            fun onCreate() {
                //doAction...
            }
        })
        //方式三:添加观察者
        lifecycle.addObserver(object : LifecycleEventObserver {
            override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
                when (event) {
                    Lifecycle.Event.ON_CREATE -> {
                        //doAction...
                    }
                }
            }
        })
    }
}

推荐使用方式一添加观察者,也就是 DefaultLifecycleObserver 类型,使用更方便;方式二已被标为废弃;方式三同样不推荐,是在源码内部使用的。

举个例子,项目中需要埋点页面的停留时间,在页面可见的时候开始算起,页面不可见的时候(退出页面,切换到后台等)上报,通过 Lifecycle 就可以很优雅的实现:

/**
 * 定义观察者,实现onResume和onStop方法,进行页面停留时间埋点逻辑
 */
class ExposureTimeObserver(val pageName: String): DefaultLifecycleObserver {
    private var start = 0L
    override fun onResume(owner: LifecycleOwner) {
        start = System.currentTimeMillis()
    }

    override fun onStop(owner: LifecycleOwner) {
        val time = System.currentTimeMillis() - start
        if (time > 0) {
            ReportUtil.report("exposureTime", mapOf("pageName" to pageName, "time" to time))
        }
        start = 0L
    }
}

//使用时:
页面A:lifecycle.addObserver(ExposureTimeObserver("PageA"))
页面B:lifecycle.addObserver(ExposureTimeObserver("PageB"))

具体业务实现代码都封装在观察者中,Activity 只需注册观察者即可,将业务代码和组件间解耦,提高了代码复用性和可读性。

Lifecycle 的作用:

  1. 简化生命周期回调:避免在应用组件中手动实现繁琐的生命周期回调。
  2. 灵活性:可以与其他 Jetpack 组件(如ViewModel、LiveData等)结合使用,提供更灵活的生命周期管理方案。
  3. 生命周期安全性:确保在正确的生命周期状态下执行相应的操作,避免崩溃或内存泄漏。

Lifecycle 可以说是生命周期的抽象层,统一了各个组件如 Activity、Fragment 的生命周期,让业务代码可以和组件进行解耦,在编写代码时,只需考虑 Lifecycle 抽象出的生命周期方法,而不必关注组件具体的生命周期实现,从而写出更有条理且更精简的代码,更易于维护。通过理解和使用 Lifecycle,可以编写出更加健壮、可维护的代码,并提升应用的性能和用户体验。

Activity 和 Fragment 生命周期:
在这里插入图片描述

Lifecycle 提供的抽象层生命周期:

  • onCreate
  • onStart
  • onResume
  • onPause
  • onStop
  • onDestroy

对应 Lifecycle 状态机的七个事件,Lifecycle 更侧重状态机的事件,而非状态(下面也会有所体现)。

Lifecyle源码解析

Lifecycle 整体实现就是状态机 + 观察者模式,通过空白 Fragment 触发生命周期事件,驱动状态的转移,并通知对应的观察者,这就是 Lifecycle 的工作流程。下面是 Lifecycle 工作时,状态流转的流程图:
在这里插入图片描述

状态机状态流转表如下,可以结合上面的流程图来看。

状态/事件ON_CREATEON_STARTON_RESUMEON_PAUSEON_STOPON_DESTROY
DESTROYED
INITIALIZED
CREATED
STARTED
RESUMED

蓝色是状态,定义了组件在其生命周期中的不同阶段,共五种:

  • INITIALIZED:初始化状态,onCreate被调用之前。
  • CREATED:创建状态,onCreate 或 onStop 调用后。
  • STARTED:启动状态,onStart 或 onPause 调用后。
  • RESUMED:可见状态,onResume调用后。
  • DESTROYED:销毁状态,onDestroy 调用后。

红色是事件,共七种(ANY先忽略):

  • ON_CREATE:对应 onCreate 方法。
  • ON_START:对应 onStart 方法。
  • ON_RESUME:对应 onResume 方法。
  • ON_PAUSE:对应 onPause 方法。
  • ON_STOP:对应 onStop 方法。
  • ON_DESTROY:对应 onDestroy 方法。
  • ON_ANY:匹配任何事件。

下面看一下 Lifecycle 的源码的具体实现,类间关系图如下:
在这里插入图片描述
Lifecycle 发挥作用的类主要有以下几个:

  • LifecycleOwner:表示一个具有生命周期的组件,只有 getLifecycle 方法,可以调用该方法拿到 Lifecycle 来添加生命周期观察者,Activity 和 Fragment 都实现了 LifecycleOwner 接口。

    //接口定义
    public interface LifecycleOwner {
        public val lifecycle: Lifecycle
    }
    
    //Activity中:
    public class ComponentActivity extends ComponentActivity implements LifecycleOwner,... {
        //可见真正干活的是 LifecycleRegistry
        private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
        
        public Lifecycle getLifecycle() {
            return mLifecycleRegistry;
        }
    }
    
  • ReportFragment:负责监听生命周期的变动,并产生对应的事件来驱动 Lifecycle 的状态改变。这里的实现在不同 Android SDK 版本上略有区别:API 大于等于 29,是通过 Activity.registerActivityLifecycleCallbacks 方法监听生命周期;API 小于 29,则是通过 ReportFragment 自身生命周期来驱动的。

    在 ComponentActivity 的 onCreate 方法中会进行 ReportFragment 的注入,源码:

    class ComponentActivity {
      ...
      protected void onCreate(Bundle savedInstanceState) {
        ...
        ReportFragment.injectIfNeededIn(this);
      }
      ...
    }
    
    open class ReportFragment() : android.app.Fragment() {
        companion object {
            private const val REPORT_FRAGMENT_TAG =
                "androidx.lifecycle.LifecycleDispatcher.report_fragment_tag"
    
            @JvmStatic
            fun injectIfNeededIn(activity: Activity) {
                //1.注入Fragment,区分SDK版本做具体实现
                if (Build.VERSION.SDK_INT >= 29) {
                    // On API 29+, we can register for the correct Lifecycle callbacks directly
                    LifecycleCallbacks.registerIn(activity)
                }
                val manager = activity.fragmentManager
                if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
                    manager.beginTransaction().add(ReportFragment(), REPORT_FRAGMENT_TAG).commit()
                    // Hopefully, we are the first to make a transaction.
                    manager.executePendingTransactions()
                }
            }
        }
        
        override fun onActivityCreated(savedInstanceState: Bundle?) {
            super.onActivityCreated(savedInstanceState)
            dispatchCreate(processListener)
            dispatch(Lifecycle.Event.ON_CREATE)
        }
    
        override fun onStart() {
            super.onStart()
            dispatchStart(processListener)
            dispatch(Lifecycle.Event.ON_START)
        }
    	...
    
        private fun dispatch(event: Lifecycle.Event) {
            //2.API小于29走这里
            if (Build.VERSION.SDK_INT < 29) {
                dispatch(activity, event)
            }
        }
    
        @RequiresApi(29)
        internal class LifecycleCallbacks : Application.ActivityLifecycleCallbacks {
            override fun onActivityPostCreated(
                activity: Activity,
                savedInstanceState: Bundle?
            ) {
                //3.API大于等于29走这里
                dispatch(activity, Lifecycle.Event.ON_CREATE)
            }
    
            override fun onActivityPostStarted(activity: Activity) {
                dispatch(activity, Lifecycle.Event.ON_START)
            }
    		...
            companion object {
                @JvmStatic
                fun registerIn(activity: Activity) {
                    activity.registerActivityLifecycleCallbacks(LifecycleCallbacks())
                }
            }
        }
    }
    
  • Lifecycle:是一个抽象类, 定义了添加和移除观察者的抽象方法,实现了协程作用域的管理。还定义了有框架中用到的状态(State)和事件(Event),也有通过状态推导出事件等方法。

    public abstract class Lifecycle {
        //1.协程作用域的存储
        @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
        public var internalScopeRef: AtomicReference<Any> = AtomicReference<Any>()
    	
        //2.观察者的添加和移除
        public abstract fun addObserver(observer: LifecycleObserver)
        public abstract fun removeObserver(observer: LifecycleObserver)
    
        //3.记录当前状态
        public abstract val currentState: State
    
        //4.事件
        public enum class Event {
            ON_CREATE,
            ON_START,
            ON_RESUME,
            ON_PAUSE,
            ON_STOP,
            ON_DESTROY,
            ON_ANY;
            //5.根据事件推导出下一状态
            public val targetState: State
                get() {
                    when (this) {
                        ON_CREATE, ON_STOP -> return State.CREATED
                        ON_START, ON_PAUSE -> return State.STARTED
                        ON_RESUME -> return State.RESUMED
                        ON_DESTROY -> return State.DESTROYED
                        ON_ANY -> {}
                    }
                    throw IllegalArgumentException("$this has no target state")
                }
    
            //6.根据状态推导出驱动的事件(down和up代表不同的方向,可参考上面的流程图)
            public companion object {
                @JvmStatic
                public fun downFrom(state: State): Event? {
                    return when (state) {
                        State.CREATED -> ON_DESTROY
                        State.STARTED -> ON_STOP
                        State.RESUMED -> ON_PAUSE
                        else -> null
                    }
                }
    
                @JvmStatic
                public fun downTo(state: State): Event? {
                    return when (state) {
                        State.DESTROYED -> ON_DESTROY
                        State.CREATED -> ON_STOP
                        State.STARTED -> ON_PAUSE
                        else -> null
                    }
                }
    
                @JvmStatic
                public fun upFrom(state: State): Event? {
                    return when (state) {
                        State.INITIALIZED -> ON_CREATE
                        State.CREATED -> ON_START
                        State.STARTED -> ON_RESUME
                        else -> null
                    }
                }
    
                @JvmStatic
                public fun upTo(state: State): Event? {
                    return when (state) {
                        State.CREATED -> ON_CREATE
                        State.STARTED -> ON_START
                        State.RESUMED -> ON_RESUME
                        else -> null
                    }
                }
            }
        }
    
        public enum class State {
            DESTROYED,
            INITIALIZED,
            CREATED,
            STARTED,
            RESUMED;
        }
    }
    
    //...协程作用域相关的方法...
    

    1.存储协程作用域对象,获取时才会创建。创建时通过 while 循环 + CAS 机制保证线程安全。

    2.只定义了添加和移除观察者抽象方法,具体的观察者管理由 LifecycleRegistry 负责。

    3.当前状态,与组件生命周期同步

    5.根据生命周期触发的事件,得到事件触发后的目标状态

    6.downFrom、downTo、upFrom、upTo 四个方法,xxFrom 系列返回当前状态转换到下一状态的响应事件;xxTo 系列返回流转到当前状态的驱动事件。down 和 up 表示不同方向,up 表示从前向后的流转,down 则相反。

  • LifecycleRegistry,该类是 Lifecycle 抽象类的唯一实现,同时也是框架的核心,其内不仅维护着所有观察者和对应状态,同时也会收到驱动事件,同步状态变更,并调用对应观察者的回调方法。

    open class LifecycleRegistry private constructor(
        provider: LifecycleOwner,
        private val enforceMainThread: Boolean
    ) : Lifecycle() {
        //1.存储所有观察者和对应状态
        private FastSafeIterableMap<LifecycleObserver, ObserverWithState> mObserverMap =
                new FastSafeIterableMap<>();
    
        //2.组件当前状态,默认是INITIALIZED
        private var state: State = State.INITIALIZED
    
        //3.处理生命周期变动事件
        open fun handleLifecycleEvent(event: Event) {
            ...
        }
    	
        //...4.具体的同步代码,包括moveToState、sync、backwardPass、forwardPass等方法...
        
        //5.添加和移除观察者具体实现
        override fun addObserver(observer: LifecycleObserver) {
            ...
        }
    
        override fun removeObserver(observer: LifecycleObserver) {
            ...
        }
    }
    

    1.FastSafeIterableMap 相当于把 SafeIterableMap 再封装了一层,SafeIterableMap存储的是<K,V>,则它是存储的<K,<K,V>。该类型是用链表实现的,模拟成 Map 接口,特性是支持在遍历的时候删除元素,非线程安全。

    2.handleLifecycleEvent是状态同步的入口,ReportFragment 发出生命周期变更的事件后,最终会调用到该方法,由此开始,进行观察者状态同步和触发方法回调。

    open fun handleLifecycleEvent(event: Event) {
        enforceMainThreadIfNeeded("handleLifecycleEvent")
        //1.获取事件触发后的目标状态,可以结合上图来看,源码在Lifecycle的targetState。
        //2.调用moveToState开始同步观察者状态
        moveToState(event.targetState)
    }
    
    private fun moveToState(next: State) {
        if (state == next) {
            return
        }
        check(!(state == State.INITIALIZED && next == State.DESTROYED)) {
            "no event down from $state in component ${lifecycleOwner.get()}"
        }
        //1.将Lifecycle当前状态修改为目标状态
        state = next
        if (handlingEvent || addingObserverCounter != 0) {
            newEventOccurred = true
            // we will figure out what to do on upper level.
            return
        }
        handlingEvent = true
        //2.同步观察者的状态,并触发对应回调
        sync()
        handlingEvent = false
        //3.如果目标状态为Destroy,清空观察者
        if (state == State.DESTROYED) {
            observerMap = FastSafeIterableMap()
        }
    }
    

    moveToState方法主要做了三件事:

    1. 将 LifecycleRegistry 当前状态更改为目标状态,该状态是页面的真实状态,每个观察者也包含自己的状态
    2. 调用 sync() 方法,主要是让观察者自己的状态和 LifecycleRegistry 的状态进行对齐
    3. 如果目标状态是Destory,则清空所有观察者

    下面继续看 sync() 方法:

    private fun sync() {
        val lifecycleOwner = lifecycleOwner.get()
            ?: throw IllegalStateException(
                "LifecycleOwner of this LifecycleRegistry is already " +
                    "garbage collected. It is too late to change lifecycle state."
            )
        //1.状态不同步则进行状态对齐操作
        while (!isSynced) {
            newEventOccurred = false
            //2.具体的同步方法
            if (state < observerMap.eldest()!!.value.state) {
                backwardPass(lifecycleOwner)
            }
            val newest = observerMap.newest()
            if (!newEventOccurred && newest != null && state > newest.value.state) {
                forwardPass(lifecycleOwner)
            }
        }
        newEventOccurred = false
    }
    
    //新添加的观察、最早添加的观察者和Lifecycle的状态相同才会返回false,保证map中所有观察者状态同步
    private val isSynced: Boolean
        get() {
            if (observerMap.size() == 0) {
                return true
            }
            val eldestObserverState = observerMap.eldest()!!.value.state  //返回链表第一个,最早添加的元素
            val newestObserverState = observerMap.newest()!!.value.state  //返回链表最后一个,最新添加的元素
            return eldestObserverState == newestObserverState && state == newestObserverState
        }
    

    该方法主要做的事就是状态对齐,让 LifecycleRegistry 状态和每个观察者自身的状态同步到一致,并在同步过程中调用观察者回调方法。

    具体做法是先**从后往前(backwardPass)同步,然后再从前往后(forwardPass)**同步,可以对照开头的 Lifecycle 时序图,状态由 INITIALIZED -> RESUMED 是从前向后,反之就是从后向前。

    两个方向的代码实现差不多,这里就从后向前以为例:

    private fun backwardPass(lifecycleOwner: LifecycleOwner) {
        val descendingIterator = observerMap.descendingIterator()
        //1.遍历所有观察者
        while (descendingIterator.hasNext() && !newEventOccurred) {
            val (key, observer) = descendingIterator.next()
            //2.判断观察者状态和当前状态,逐步进行同步
            while (observer.state > state && !newEventOccurred && observerMap.contains(key)
            ) {
                //3.具体的同步操作
                val event = Event.downFrom(observer.state)
                    ?: throw IllegalStateException("no event down from ${observer.state}")
                pushParentState(event.targetState)
                observer.dispatchEvent(lifecycleOwner, event)
                popParentState()
            }
        }
    }
    

    可以看到,该方法会遍历所有观察者,同步每个观察者的状态,并调用观察者对应的生命周期方法。

    1.遍历所有观察者,同步每个观察者的状态

    2.判断观察者状态是否大于最新状态,符合条件则进入 while 循环进行同步操作。(大于判断是因为这里只关注从后向前的同步)

    3.找到观察者状态切换的下一个事件,根据事件调用观察者回调方法,并更新观察者状态。

    举个例子,当进入第 2 步的时候,观察者 A 的状态是 RESUMED,而最新状态是 CREATED,此时进入第 3 步。首先通过 downFrom 找出流转到下一状态的事件(downFrom 是从后向前),也就是 ON_PAUSE,然后通过观察者的 dispatchEvent 方法分发:

    fun dispatchEvent(owner: LifecycleOwner?, event: Event) {
        val newState = event.targetState
        state = min(state, newState)
        lifecycleObserver.onStateChanged(owner!!, event)
        state = newState
    }
    

    dispatchEvent 方法中会回调 onPause 方法,并将状态更新为 STARTED。再回到第 2 步,STATED 仍然大于 CREATED,再进入第三步:找出 ON_STOP 事件,回调 onStop 方法,观察者状态同步为 CREATED,此时观察者 A 状态和最新状态保持一致了,这就是状态同步的流程。

    注意:枚举如果没定义值,则默认是根据定义的位置来决定的

    通过上面流程可以发现,状态的同步是按次序的,不能跨状态同步,可以参考上面的时序图,并不能将状态直接一步对齐成最新的状态,因为 Lifecycle 侧重事件的回调,从一个状态到另一个状态,需要找出所有导致状态变更的事件,并执行对应观察者方法。

    添加新的观察者时也有相同的对齐逻辑:

    override fun addObserver(observer: LifecycleObserver) {
        enforceMainThreadIfNeeded("addObserver")
        val initialState = if (state == State.DESTROYED) State.DESTROYED else State.INITIALIZED
        val statefulObserver = ObserverWithState(observer, initialState)
        val previous = observerMap.putIfAbsent(observer, statefulObserver)
        if (previous != null) {
            return
        }
        val lifecycleOwner = lifecycleOwner.get()
            ?: // it is null we should be destroyed. Fallback quickly
            return
        val isReentrance = addingObserverCounter != 0 || handlingEvent
        var targetState = calculateTargetState(observer)
        addingObserverCounter++
        while (statefulObserver.state < targetState && observerMap.contains(observer)
        ) {
            pushParentState(statefulObserver.state)
            val event = Event.upFrom(statefulObserver.state)
                ?: throw IllegalStateException("no event up from ${statefulObserver.state}")
            statefulObserver.dispatchEvent(lifecycleOwner, event)
            popParentState()
            // mState / subling may have been changed recalculate
            targetState = calculateTargetState(observer)
        }
        if (!isReentrance) {
            // we do sync only on the top level.
            sync()
        }
        addingObserverCounter--
    }
    

    添加新的观察者一般情况默认是 INITIALIZED 状态,所以同样需要同步到最新状态。举个例子,在页面 onResume 时添加观察者,此时将观察者的回调方法都打印一下:
    在这里插入图片描述
    新添加的观察者依次调用了 onCreate、onStart 和 onResume 回调。说明 Lifecycle 侧重状态机的事件,从一个状态到另一个状态,需要找出所有导致状态变更的事件,并执行对应观察者方法,不能将某个事件漏掉,以免调用时代码出错。

    状态观察者:外部添加的观察者内部会包装一层,目的是保存观察者状态,方便和最新状态做对比。

    internal class ObserverWithState(observer: LifecycleObserver?, initialState: State) {
        var state: State
        var lifecycleObserver: LifecycleEventObserver
    
        init {
            lifecycleObserver = Lifecycling.lifecycleEventObserver(observer!!)
            state = initialState
        }
    
        fun dispatchEvent(owner: LifecycleOwner?, event: Event) {
            val newState = event.targetState
            state = min(state, newState)
            lifecycleObserver.onStateChanged(owner!!, event)
            state = newState
        }
    }
    
  • LifecycleObserver:表示一个可以观察 LifecycleOwner 生命周期状态的组件。开发人员可以通过重写 DefaultLifecycleObserver 接口来监听 LifecycleOwner 的生命周期状态变化,并实现对应逻辑。

    public interface DefaultLifecycleObserver : LifecycleObserver {
        public fun onCreate(owner: LifecycleOwner) {}
        public fun onStart(owner: LifecycleOwner) {}
        public fun onResume(owner: LifecycleOwner) {}
        public fun onPause(owner: LifecycleOwner) {}
        public fun onStop(owner: LifecycleOwner) {}
        public fun onDestroy(owner: LifecycleOwner) {}
    }
    

    之所以可以直接使用 DefaultLifecycleObserver,是因为源码内部还通过适配器模式进行了版本兼容,这也是适配器模式的经典用法。源码如下:

    internal class DefaultLifecycleObserverAdapter(
        private val defaultLifecycleObserver: DefaultLifecycleObserver,
        private val lifecycleEventObserver: LifecycleEventObserver?
    ) : LifecycleEventObserver {
        override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
            when (event) {
                Lifecycle.Event.ON_CREATE -> defaultLifecycleObserver.onCreate(source)
                Lifecycle.Event.ON_START -> defaultLifecycleObserver.onStart(source)
                Lifecycle.Event.ON_RESUME -> defaultLifecycleObserver.onResume(source)
                Lifecycle.Event.ON_PAUSE -> defaultLifecycleObserver.onPause(source)
                Lifecycle.Event.ON_STOP -> defaultLifecycleObserver.onStop(source)
                Lifecycle.Event.ON_DESTROY -> defaultLifecycleObserver.onDestroy(source)
                Lifecycle.Event.ON_ANY ->
                    throw IllegalArgumentException("ON_ANY must not been send by anybody")
            }
            lifecycleEventObserver?.onStateChanged(source, event)
        }
    }
    

为什么State不是设置为7种呢

Lifecycle 中有两个很奇怪的状态,分别是 CREATED 和 STARTED,先说 CREATED 状态,通过 ON_CREATE 事件转换为 CREATED 状态可以理解,但通过 CREATED 事件也转换为 CREATED 似乎有些不合常理;STARTED 状态也是一样,为什么 Google 工程师没有再新建两个 STOPED 和 PAUSED 状态呢?个人认为 Lifecycle 侧重的是事件而非状态,CREATED 或 STOPED 状态对于上层来说区别不大,Lifecycle 比较关注的状态可能只有已经定义的五个了。其次复用这两种状态,少了两个枚举,更节省内存。缺点就是代码复杂些,就上述流程图来说,状态的流转并不是单向的了,而是双向的,这就需要根据旧状态和新状态判断方向,从而推断出驱动状态转换的事件,继而调用对应的生命周期方法回调。如此非常巧妙的设计,让人叹为观止。

总结

Lifecycle 是状态机 + 观察者模式的结合,通过 ReportFragment 触发事件,驱动 LifecycleRegistry 中所有观察者状态同步,并根据同步结果触发观察者对应的回调方法,这就是它的整体原理。

Lifecycle 虽然只是提供了抽象层的组件生命周期监听,但这一改变可以让软件编码灵活性大大加强,LiveData 就是基于 Lifecycle 来实现的。通过使用 Lifecycle,可以编写出更加健壮、可维护的代码,提升应用的性能和用户体验。

空白 Fragment 监听生命周期的方式也有很多优秀的三方库在用,比如 Glide,通过这种方式来监听生命周期,处理图片的加载和回收。一些动态权限处理三方库,通过添加空白 Fragment 来监听权限请求结果,从而简化调用处代码,优秀的思路都是相通的。

参考

官方文档:Lifecycle
状态机:重修设计模式-行为型-状态模式
观察者模式:重修设计模式-行为型-观察者模式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2214193.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

指针 + 数组 较为复杂凌乱的 【笔试题】

2024 - 10 - 10 - 笔记 - 25 作者(Author): 郑龙浩 / 仟濹(CSDN 账号名) 【指针 数组】的 各种题型(笔试题) 来自于鹏哥的网课&#xff0c;我做一下笔记 119. 【C语言进阶】笔试题详解&#xff08;4&#xff09;_哔哩哔哩_bilibili ① 题 #include <stdio.h> int m…

VUE 开发——Vue学习(三)—— 智慧商城项目

目录 解释各个模块 api接口模块&#xff1a;发送ajax请求的接口模块utils工具模块&#xff1a;自己封装的一些工具方法模块components组件模块&#xff1a;全局通用的组件router路由模块&#xff1a;封装要所有路由views&#xff1a;各个页面assets&#xff1a;各种资源 van…

JAVA软开-面试经典题(7)-字符串常量池

字符串常量池 1.定义&#xff1a;字符串常量池&#xff08;String Constant Pool&#xff09;&#xff0c;用于存放字符串常量的运行时内存结构&#xff0c;其底层的实现为Hashtable。 【注意】 在JDK1.6之前&#xff0c;字符串常量池中只会存放具体的String实例&#xff0c;在…

MySQL基础探秘(3)

前面那篇文章是简单介绍了往数据库中插入数据&#xff0c;以及对数据进行有些改动。 但是&#xff0c;细想下&#xff0c;数据能够无限制&#xff0c;无约束进行插入吗&#xff1f; emm……显然是不行的&#xff0c;不然数据就乱套了&#xff0c;看起来不美观。 所以要对数据…

Axure详细介绍及功能对比,常用版本选择和替代软件分享

Axure是一款专门用于原型设计和交互设计的专业软件&#xff0c;广泛应用于用户界面&#xff08;UI&#xff09;和用户体验&#xff08;UX&#xff09;设计领域。它的主要功能是帮助产品经理、设计师以及开发人员创建具有互动性的原型&#xff0c;以便展示和测试各种应用、网站或…

CST学习笔记(二)Floquet模式激励设置

CST学习笔记&#xff08;二&#xff09;Floquet模式激励设置 在CST中我们常常使用Floquet模式来仿真频率选择表面(FSS)或者超材料等&#xff0c;但是我们设置好Zmax的floquet模式数量后&#xff0c;启动仿真&#xff0c;会发现S参数一栏中有很多我们不想要看的S参数&#xff0…

海南聚广众达电子商务咨询有限公司解锁流量密码

在这个瞬息万变的数字时代&#xff0c;电商行业如同一股不可阻挡的洪流&#xff0c;正以前所未有的速度重塑着商业版图。而在这股浪潮中&#xff0c;抖音电商以其独特的魅力&#xff0c;迅速崛起为一颗璀璨的新星&#xff0c;吸引了无数商家与创业者的目光。海南聚广众达电子商…

【题解】【动态规划01背包问题】—— [NOIP2012 普及组] 摆花

【题解】【动态规划01背包问题】—— [NOIP2012 普及组] 摆花 [NOIP2012 普及组] 摆花题目描述输入格式输出格式输入输出样例输入 #1输出 #1 提示 解法1.二维 d p dp dp1.1.思路解析1.2.AC代码 解法2.一维 d p dp dp2.1.思路解析2.2.AC代码 3.扩展:前缀和优化 [NOIP2012 普及组…

python基础知识(十一)面向过程,面向对象,对象属性,魔法方法,继承,私有权限

目录 面向过程是什么 什么是面向对象&#xff1f; 面向对象的三大特性&#xff1a; 继承 多态 类 对象 self关键字 对象属性 类外面访问属性 类内部获取属性 魔法方法 无参init()方法 有参init()方法 str()方法 del()方法 继承基础 什么是继承 单继承 多继…

Javascript笔试题目(六)

1.如何使用JS实现Promise 对象?请写出具体代码 Promise其实也不难-CSDN博客 Javascript 手写一个Promise_javascript中手写promise ?-CSDN博客 Promise其实也不难-CSDN博客 题目要求我们使用JavaScript实现一个Promise对象。对此我们可以基于Promise/A规范的要求进行实现Prom…

面试-2024年7月16号

面试-2024年7月16号 自我介绍Mysql主从复制是做了一个什么样的集群&#xff1f;在Mysql的使用过程中遇到过哪些问题&#xff1f;mysql迁移具体步骤mysql漏洞修复是怎么做的。mysql的容灾方案&#xff08;灾备恢复机制&#xff09;。redis多节点怎么部署的redis的备份与恢复、迁…

电源中的“冷地”和“热地”

最近硬件同事在弄开关电源相关项目&#xff0c;由于其第一次做开关电源&#xff0c;并不懂冷地和热地的区别&#xff0c;出现示波器探头接地夹夹“热地”导致实验室多次跳闸&#xff0c;最严重时把板子给炸了。为了了解冷地和热地的如何辨别以及为什么热地带电这些知识&#xf…

【从零开发Mybatis】引入XNode和XPathParser

引言 在上文&#xff0c;我们发现直接使用 DOM库去解析XML 配置文件&#xff0c;非常复杂&#xff0c;也很不方便&#xff0c;需要编写大量的重复代码来处理 XML 文件的读取和解析&#xff0c;代码可读性以及可维护性相当差&#xff0c;使用起来非常不灵活。 因此&#xff0c…

JavaEE 多线程第二节 (多线程的简单实现Thread/Runable)

1. 创建线程&#xff08;继承 Thread 类&#xff09;步骤&#xff1a; 继承 Thread 类&#xff1a; 创建一个类并继承 Thread 类&#xff0c;然后重写 run() 方法&#xff0c;在该方法中写入线程执行的代码 class MyThread extends Thread {Overridepublic void run()…

数据恢复超简单!9 个方法任你选!小白也能轻易恢复数据!

在当今数字化的世界中&#xff0c;数据恢复的重要性日益凸显。无论是工作中的重要文档&#xff0c;还是生活中的珍贵照片和视频&#xff0c;一旦丢失&#xff0c;都可能给我们带来极大的困扰。别担心&#xff0c;下面为大家介绍 9 个超简单的数据恢复方法&#xff0c;让小白也能…

C++基础面试题 | 什么是C++中的运算符重载?

文章目录 回答重点&#xff1a;示例&#xff1a; 运算符重载的基本规则和注意事项&#xff1a; 回答重点&#xff1a; C的运算符重载是指可以为自定义类型&#xff08;如类或结构体&#xff09;定义运算符的行为&#xff0c;使其像内置类型一样使用运算符。通过重载运算符&…

它思科技CTO聂玮奇:消除“AI幻觉”,搭建高可靠对话云平台丨数据猿专访

大数据产业创新服务媒体 ——聚焦数据 改变商业 近年来&#xff0c;大模型技术在全球范围内引起了广泛关注和应用热潮。 提到人工智能&#xff0c;很多人会想到它强大的运算能力和广泛的应用场景。如今&#xff0c;语言模型的发展如火如荼&#xff0c;但其中的“幻觉”问题却带…

基于SSM社区医院预约转诊管理系统JAVA|VUE|Springboot计算机毕业设计源代码+数据库+LW文档+开题报告+答辩稿+部署教+代码讲解

源代码数据库LW文档&#xff08;1万字以上&#xff09;开题报告答辩稿 部署教程代码讲解代码时间修改教程 一、开发工具、运行环境、开发技术 开发工具 1、操作系统&#xff1a;Window操作系统 2、开发工具&#xff1a;IntelliJ IDEA或者Eclipse 3、数据库存储&#xff1a…

SwiftUI 6.0(iOS 18)自定义容器值(Container Values)让容器布局渐入佳境(上)

概述 我们在之前多篇博文中已经介绍过 SwiftUI 6.0&#xff08;iOS 18&#xff09;新增的自定义容器布局机制。现在&#xff0c;如何利用它们对容器内容进行“探囊取物”和“聚沙成塔”&#xff0c;我们已然胸有成竹了。 然而&#xff0c;除了上述鬼工雷斧般的新技巧之外&…

STM32_实验1_建立新工程

1、使用STM32CubeIDE建立一个新工程 1.1选择时钟源为外部晶振时钟。 1.2选择调试方式为 serial wire&#xff08;串行线&#xff09;。 1.3配置时钟树. 1.4选择以 c 和 h 文件型式管理工程文件。 1.5生成 hex 可执行文件。&#xff08;完成后点击锤子&#xff09; 2.串口输出调…