Jetpack 之 LifeCycle 组件原理解析

简介: 1. LifeCycle 是如何监听到 Activity/Fragment 生命周期变化的?2. LifeCycle 如何将生命周期变化的事件分发给观察者的?

对于 LifeCycle 组件还不了解的同学,可以先阅读这篇文章:

Jetpack 之 LifeCycle 组件使用详解

学习问题

  1. LifeCycle 是如何监听到 Activity/Fragment 生命周期变化的?

  2. LifeCycle 如何将生命周期变化的事件分发给观察者的?

本文目录

一、四个重要的类

(一)Lifecycle

Lifecycle 是一个抽象类。它内部定义了两个枚举:Event 需要分发的事件的类型,State 宿主的状态。

1.Event

public enum Event {
   
   
    //对应于宿主(实现了LifecycleOwner的类)的 onCreate 方法
    ON_CREATE,

    //对应于宿主(实现了LifecycleOwner的类)的 onStart 方法
    ON_START,

    //对应于宿主(实现了LifecycleOwner的类)的 onResume 方法
    ON_RESUME,

    //对应于宿主(实现了LifecycleOwner的类)的 onPause 方法
    ON_PAUSE,

    //对应于宿主(实现了LifecycleOwner的类)的 onStop 方法
    ON_STOP,

    //对应于宿主(实现了LifecycleOwner的类)的 onDestroy 方法
    ON_DESTROY,

    //可以匹配宿主的所有生命周期事件
    ON_ANY
}

各个生命周期事件分发的时机:

ON_CREATE、ON_START 和 ON_RESUME:这三个生命周期事件是在宿主相应的生命周期方法 执行完成之后 被分发。

ON_PAUSE、ON_STOP 和 ON_DESTROY:这三个生命周期事件是在宿主相应的生命周期方法 被调用之前 分发。

2.State

public enum State {
   
   

    //已销毁状态。以 Activity 为例,在回调 Activity 的 onDestroy 方法之前,宿主会达到此状态。
    DESTROYED,

    //已初始化状态。在回调 onCreate 方法之前的一种状态。
    INITIALIZED,

    //已创建状态。两种情况下回处于这种状态:
    //1.宿主的 onCreate 方法执行之后
    //2.宿主的 onStop 方法调用之前
    CREATED,

    //可见状态。两种情况下回处于这种状态:
    //1.宿主的 onStart 方法执行之后
    //2.宿主的 onPause 方法调用之前
    STARTED,

    //聚焦(可交互状态)。宿主执行了 onResume 方法后处于该状态。
    RESUMED;

    public boolean isAtLeast(@NonNull State state) {
   
   
        return compareTo(state) >= 0;
    }
}

3.Event 和 State 的对应关系

(二)LifecycleRegistry

它是 Lifecycle 的唯一实现类。主要用来注册观察者(LifecycleObserver),以及分发宿主状态给它们(可以处理多个观察者)。

(三)LifecycleOwner

用来声明它是一个能够提供生命周期事件的宿主,Activity/Fragment 都实现了该接口。内部只有一个 getLifecycle 方法。

public interface LifecycleOwner {
   
   
    @NonNull
    Lifecycle getLifecycle();
}

(四)LifecycleObserver

用来定义观察者。

二、四个类之间的关系

LifeCycle 类关系图

说明:

  1. Activity/Fragment 都默认实现了 LifecycleOwner 接口;
  2. LifecycleRegistry 是 Lifecycle 唯一的实现类;
  3. 实现观察者(Observer)有三种方式:
    • LifecycleObserver 配合 @OnLifecycleEvent 注解
    • DefaultLifecycleObserver 拥有宿主所有生命周期事件
    • LifecycleEventObserver 将宿主生命周期事件封装成 Lifecycle.Event
  4. 在 Activity/Fragment 中通过 getLifecycle() 方法获取到一个 LifecycleRegistry 对象;
  5. 通过调用 LifecycleRegistry 对象的 addObserver() 添加一个观察者(该观察者通过三种方式实现都可以)。

三、Fragment 如何实现 LifyCycle

在 Fragment 各个生命周期方法内部会利用 LifecycleRegistry 进行相应的事件分发。

public class Fragment implements LifecycleOwner {
   
   

    LifecycleRegistry mLifecycleRegistry;

    void performCreate(Bundle savedInstanceState) {
   
   
        onCreate(savedInstanceState);
        //ON_CREATE 事件在执行了 onCreate 方法之后分发 
        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
    }

    void performStart() {
   
   
        onStart();
        //ON_START 事件在执行了 onStart 方法之后分发
        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
    }

    void performResume() {
   
   
        onResume();
        //ON_RESUME 事件在执行了 onResume 方法之后分发
        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
    }

    void performPause() {
   
   
        //ON_PAUSE 事件在执行 onPause 方法执行之前分发
        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_PAUSE);
        onPause();
    }

    void performStop() {
   
   
        //ON_STOP 事件在执行 onStop 方法执行之前分发
        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP);
        onStop();
    }

    void performDestroy() {
   
   
        //ON_DESTROY 事件在执行 onDestroy 方法执行之前分发
        mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
        onDestroy();
    }
}

四、Activity 如何实现 LifyCycle

Activity 实现 Lifecycle 需要借助于 ReportFragment 往 Activity 上添加一个 fragment。ReportFragment 没有任何的页面,它只负责在生命周期变化时利用 LifecycleRegistry 进行相应事件的分发。

之所以需要借助 ReportFragment ,目的是为了兼顾不是继承自 AppCompactActivity 的场景, 同时也支持我们自定义 LifecycleOwner 的场景。

(一)ComponentActivity 的源码分析

以下是 ComponentActivity 的源码,在 onCreate 方法中往 Activity 里面添加了一个 fragment。

public class ComponentActivity extends Activity implements LifecycleOwner {
   
   

    private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);

    @SuppressLint("RestrictedApi")
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
   
   
        super.onCreate(savedInstanceState);
        //添加一个 fragment
        ReportFragment.injectIfNeededIn(this);
    }
}

补充一下 Activity 的继承关系:

(二)ReportFragment 的源码分析

下面来看看 ReportFragment 的源码。在 ReportFragment 中最主要的一个方法是 injectIfNeededIn。

public class ReportFragment extends Fragment {
   
   

    ...

    public static void injectIfNeededIn(Activity activity) {
   
   
        //1.如果 API >= 29 ,注册 ActivityLifecycleCallbacks
        if (Build.VERSION.SDK_INT >= 29) {
   
   
            activity.registerActivityLifecycleCallbacks(
                    new LifecycleCallbacks());
        }

        //2.添加一个 ReportFragment 到 Activity 中
        android.app.FragmentManager manager = activity.getFragmentManager();
        if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
   
   
            manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit();
            manager.executePendingTransactions();
        }
    }

    ...

injectIfNeededIn 方法会做两件事情:

1.判断当前的 API Level,根据 API Level 选择监听 Activity 生命周期的时机和方式

(1)API Level >= 29

如果大于等于 29 的话,会注册一个 ActivityLifecycleCallbacks,

if (Build.VERSION.SDK_INT >= 29) {
   
   
    activity.registerActivityLifecycleCallbacks(new LifecycleCallbacks());
}

ActivityLifecycleCallbacks 是 Application 的一个内部接口。

public interface ActivityLifecycleCallbacks {
   
   
        void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState);
        void onActivityStarted(@NonNull Activity activity);
        void onActivityResumed(@NonNull Activity activity);
        void onActivityPaused(@NonNull Activity activity);
        void onActivityStopped(@NonNull Activity activity);
        void onActivityDestroyed(@NonNull Activity activity);
    }

以 Activity 的 onResume() 生命周期为例,如果我们注册了 ActivityLifecycleCallbacks,Android 系统会先回调 ActivityLifecycleCallbacks 的 onActivityResumed 方法,然后才执行 Android 本身的 onResume() 方法。

利用这个特点,我们可以注册一个自定义的 ActivityLifecycleCallbacks,在自定义的 ActivityLifecycleCallbacks 添加分发生命周期事件的逻辑来通知观察者:

public class ReportFragment extends Fragment {
   
   

    public static void injectIfNeededIn(Activity activity) {
   
   
        if (Build.VERSION.SDK_INT >= 29) {
   
   
            activity.registerActivityLifecycleCallbacks(
                    new LifecycleCallbacks());
        }

        android.app.FragmentManager manager = activity.getFragmentManager();
        if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
   
   
            manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit();
            manager.executePendingTransactions();
        }
    }

    @SuppressWarnings("deprecation")
    static void dispatch(@NonNull Activity activity, @NonNull Lifecycle.Event event) {
   
   
        ...

        if (activity instanceof LifecycleOwner) {
   
   
            Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle();
            if (lifecycle instanceof LifecycleRegistry) {
   
   
                //最终还是通过 LifecycleRegistry 来处理事件的分发
                ((LifecycleRegistry) lifecycle).handleLifecycleEvent(event);
            }
        }
    }

    //自定义 ActivityLifecycleCallbacks 来处理生命周期事件的分发,通知观察者
    static class LifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
   
   

        ...

        @Override
        public void onActivityPostCreated(@NonNull Activity activity,
                @Nullable Bundle savedInstanceState) {
   
   
            //分发 ON_CREATE 事件
            dispatch(activity, Lifecycle.Event.ON_CREATE);
        }

        @Override
        public void onActivityPostStarted(@NonNull Activity activity) {
   
   
            //分发 ON_START 事件
            dispatch(activity, Lifecycle.Event.ON_START);
        }

        @Override
        public void onActivityPostResumed(@NonNull Activity activity) {
   
   
            //分发 ON_RESUME 事件
            dispatch(activity, Lifecycle.Event.ON_RESUME);
        }

        @Override
        public void onActivityPrePaused(@NonNull Activity activity) {
   
   
            //分发 ON_PAUSE 事件
            dispatch(activity, Lifecycle.Event.ON_PAUSE);
        }

        @Override
        public void onActivityPreStopped(@NonNull Activity activity) {
   
   
            //分发 ON_STOP 事件
            dispatch(activity, Lifecycle.Event.ON_STOP);
        }

        @Override
        public void onActivityPreDestroyed(@NonNull Activity activity) {
   
   
            //分发 ON_DESTROY 事件
            dispatch(activity, Lifecycle.Event.ON_DESTROY);
        }

        ...

    }
}

(2)API Level < 29

对于 API 小于 29 的情况,它的实现方式和 Fragment 的实现是一样的,在各个生命周期方法内利用 LifecycleRegistry 分发相应的 Lifecycle.Event 事件给每个观察者:

public class ReportFragment extends Fragment {
   
   

    public static void injectIfNeededIn(Activity activity) {
   
   
        if (Build.VERSION.SDK_INT >= 29) {
   
   
            activity.registerActivityLifecycleCallbacks(
                    new LifecycleCallbacks());
        }

        android.app.FragmentManager manager = activity.getFragmentManager();
        if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
   
   
            manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit();
            manager.executePendingTransactions();
        }
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
   
   
        super.onActivityCreated(savedInstanceState);
        dispatch(Lifecycle.Event.ON_CREATE);
    }

    @Override
    public void onStart() {
   
   
        super.onStart();
        dispatch(Lifecycle.Event.ON_START);
    }

    @Override
    public void onResume() {
   
   
        super.onResume();
        dispatch(Lifecycle.Event.ON_RESUME);
    }

    @Override
    public void onPause() {
   
   
        super.onPause();
        dispatch(Lifecycle.Event.ON_PAUSE);
    }

    @Override
    public void onStop() {
   
   
        super.onStop();
        dispatch(Lifecycle.Event.ON_STOP);
    }

    @Override
    public void onDestroy() {
   
   
        super.onDestroy();
        dispatch(Lifecycle.Event.ON_DESTROY);
    }

    private void dispatch(@NonNull Lifecycle.Event event) {
   
   
        //在此处判断一下 API ,避免重复分发事件
        if (Build.VERSION.SDK_INT < 29) {
   
   
            dispatch(getActivity(), event);
        }
    }
}

2.在 injectIfNeededIn 中会创建一个 ReportFragment 添加到 Activity 中

public class ReportFragment extends Fragment {
   
   

    public static void injectIfNeededIn(Activity activity) {
   
   

        ...

        //往 Activity 中添加一个 ReportFragment
        android.app.FragmentManager manager = activity.getFragmentManager();
        if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
   
   
            manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit();
            manager.executePendingTransactions();
        }
    }
}

五、LifecycleRegistry 源码分析

从前文的分析中,我们知道了 Activity/Fragment 是如何去实现 LifeCycle 的,对于生命周期事件的分发,它们最终都是交给了 LifecycleRegistry 去处理,因此,我们有比较去了解一下 LifecycleRegistry 内部的工作机制。

(一)addObserver() 源码分析

LifeCycle 是通过观察者模式去实现的,添加一个观察者的方式是调用 addObserver() 方法:

getLifecycle().addObserver(observer);

先来看一下完整的 addObserver() 方法:

public void addObserver(@NonNull LifecycleObserver observer) {
   
   
        State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
        ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
        ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);

        if (previous != null) {
   
   
            return;
        }
        LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
        if (lifecycleOwner == null) {
   
   
            // it is null we should be destroyed. Fallback quickly
            return;
        }

        boolean isReentrance = mAddingObserverCounter != 0 || mHandlingEvent;
        State targetState = calculateTargetState(observer);
        mAddingObserverCounter++;
        while ((statefulObserver.mState.compareTo(targetState) < 0
                && mObserverMap.contains(observer))) {
   
   
            pushParentState(statefulObserver.mState);
            statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState));
            popParentState();
            // mState / subling may have been changed recalculate
            targetState = calculateTargetState(observer);
        }

        if (!isReentrance) {
   
   
            // we do sync only on the top level.
            sync();
        }
        mAddingObserverCounter--;
    }

下面,我们逐行代码来分析:

1.首先确定新添加的 Observer 的初始化状态

State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;

只要当前宿主的状态不是 DESTROYED,那么它的初始状态都是 INITIALIZED

2.将 Observer 包装成 ObserverWithState

ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);

ObserverWithState 表示带有状态的 Observer,这个类后文会详细分析。

3.将 Observer 添加到集合中

ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);

if (previous != null) {
   
   
    return;
}

通过 putIfAbsent() 方法将 ObserverWithState 添加到集合中,如果之前已经添加过了,putIfAbsent() 方法会直接返回之前添加过的 ObserverWithState,此时,程序会直接 return。

4.利用一个 while 循环将观察者和宿主的状态进行对齐

//首先计算出观察者应该达到的状态
State targetState = calculateTargetState(observer);
//通过 compareTo 方法,将观察者的状态和宿主当前状态做比较,如果小于0,说明两者状态还没有对齐
while ((statefulObserver.mState.compareTo(targetState) < 0
        && mObserverMap.contains(observer))) {
   
   
    pushParentState(statefulObserver.mState);
    //执行一次事件分发
    statefulObserver.dispatchEvent(lifecycleOwner, upEvent(statefulObserver.mState));
    popParentState();
    //计算观察者下一个应该到达的状态,在下次循环中观察者状态还会与之对比,直到状态对齐,退出循环
    targetState = calculateTargetState(observer);
}

根据 while 循环的逻辑,我们可以得出一个结论:

在 Activity/Fragment 的任意生命周期方法内注册观察者都能接收到完整的生命周期事件。

比如,我们在 onResume() 方法内注册观察者:

  • while 第一次循环:分发 on_Create 事件,观察者状态 INITIALIZED -> CREATED
  • while 第二次循环:分发 on_Start 事件,观察者状态 CREATED -> STARTED
  • while 第三次循环:分发 on_Resume 事件,观察者状态 STARTED -> RESUMED

不过,建议最好是在 onCreate 方法中进行注册。

关于使用 compareTo 比较两个枚举值,请参考文章:

Java 枚举比较

(二)handleLifecycleEvent() 源码分析

handleLifecycleEvent() 方法主要是负责宿主生命周期变化后相应的事件分发。

public void handleLifecycleEvent(@NonNull Lifecycle.Event event) {
   
   
        State next = getStateAfter(event);
        moveToState(next);
    }

1.首先根据需要分发的事件,获取宿主当前处于什么状态

假设分发的是 ON_START 事件,那么根据 getStateAfter() 方法我们可以知道,宿主当前是处于 STARTED 状态。

static State getStateAfter(Event event) {
        switch (event) {
            case ON_CREATE:
            case ON_STOP:
                return CREATED;
            case ON_START:
            case ON_PAUSE:
                return STARTED;
            case ON_RESUME:
                return RESUMED;
            case ON_DESTROY:
                return DESTROYED;
            case ON_ANY:
                break;
        }

2.根据第一步获取的宿主状态设置当前的状态并通知观察者

private void moveToState(State next) {
   
   
        if (mState == next) {
   
   
            return;
        }
        //设置当前状态
        mState = next;

        ...

        //通知观察者
        sync();
    }

下面来看一下 sync 方法:

private void sync() {
   
   

    while (!isSynced()) {
   
   

        //如果宿主当前的状态 小于 mObserverMap 集合中最先添加的那个观察者的状态
        //则说明宿主可能发生了状态回退,比如当前是 RESUMED 状态,执行了onPause 则回退到STARTED 状态
        //此时调用 backwardPass 给集合中的每个一观察者分发一个 on_pause 事件,并同步它的状态。
        if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) {
   
   
            backwardPass(lifecycleOwner);
        }

        //如果宿主当前状态 大于 mObserverMap 集合中最先添加的那个观察者的状态
        //则说明宿主可能发生了状态前进,比如当前是 STARTED 状态,执行了onResume 则前进到RESUMED 状态
        //此时调用 forwardPass 给集合中的每个一观察者分发一个 on_resume 事件,并同步它的状态。
        Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest();
        if (!mNewEventOccurred && newest != null
                && mState.compareTo(newest.getValue().mState) > 0) {
   
   
            forwardPass(lifecycleOwner);
        }
    }
    mNewEventOccurred = false;
}

(三)ObserverWithState 源码分析

ObserverWithState 是 LifecycleRegistry 中持有观察者及其状态的内部类。

static class ObserverWithState {
   
   
    State mState;
    LifecycleEventObserver mLifecycleObserver;

    //把传入的 LifecycleObserver 适配成 LifecycleEventObserver,目的是为了统一事件的分发形式。
    //在之前的文章里介绍过实现观察者有三种形式,每一种接收的事件类型都不一样,如果在分发的时候不统一事件分发的形式,将会变得很麻烦
    ObserverWithState(LifecycleObserver observer, State initialState) {
   
   
        mLifecycleObserver = Lifecycling.lifecycleEventObserver(observer);
        mState = initialState;
    }

    void dispatchEvent(LifecycleOwner owner, Event event) {
   
   
        State newState = getStateAfter(event);
        mState = min(mState, newState);
        //执行事件分发
        mLifecycleObserver.onStateChanged(owner, event);
        mState = newState;
    }
}
相关文章
|
10月前
|
安全 算法 网络协议
解析:HTTPS通过SSL/TLS证书加密的原理与逻辑
HTTPS通过SSL/TLS证书加密,结合对称与非对称加密及数字证书验证实现安全通信。首先,服务器发送含公钥的数字证书,客户端验证其合法性后生成随机数并用公钥加密发送给服务器,双方据此生成相同的对称密钥。后续通信使用对称加密确保高效性和安全性。同时,数字证书验证服务器身份,防止中间人攻击;哈希算法和数字签名确保数据完整性,防止篡改。整个流程保障了身份认证、数据加密和完整性保护。
|
6月前
|
安全 Java Android开发
为什么大厂要求安卓开发者掌握Kotlin和Jetpack?深度解析现代Android开发生态优雅草卓伊凡
为什么大厂要求安卓开发者掌握Kotlin和Jetpack?深度解析现代Android开发生态优雅草卓伊凡
317 0
为什么大厂要求安卓开发者掌握Kotlin和Jetpack?深度解析现代Android开发生态优雅草卓伊凡
|
9月前
|
机器学习/深度学习 数据可视化 PyTorch
深入解析图神经网络注意力机制:数学原理与可视化实现
本文深入解析了图神经网络(GNNs)中自注意力机制的内部运作原理,通过可视化和数学推导揭示其工作机制。文章采用“位置-转移图”概念框架,并使用NumPy实现代码示例,逐步拆解自注意力层的计算过程。文中详细展示了从节点特征矩阵、邻接矩阵到生成注意力权重的具体步骤,并通过四个类(GAL1至GAL4)模拟了整个计算流程。最终,结合实际PyTorch Geometric库中的代码,对比分析了核心逻辑,为理解GNN自注意力机制提供了清晰的学习路径。
680 7
深入解析图神经网络注意力机制:数学原理与可视化实现
|
10月前
|
机器学习/深度学习 算法 数据挖掘
解析静态代理IP改善游戏体验的原理
静态代理IP通过提高网络稳定性和降低延迟,优化游戏体验。具体表现在加快游戏网络速度、实时玩家数据分析、优化游戏设计、简化更新流程、维护网络稳定性、提高连接可靠性、支持地区特性及提升访问速度等方面,确保更流畅、高效的游戏体验。
263 22
解析静态代理IP改善游戏体验的原理
|
9月前
|
机器学习/深度学习 缓存 自然语言处理
深入解析Tiktokenizer:大语言模型中核心分词技术的原理与架构
Tiktokenizer 是一款现代分词工具,旨在高效、智能地将文本转换为机器可处理的离散单元(token)。它不仅超越了传统的空格分割和正则表达式匹配方法,还结合了上下文感知能力,适应复杂语言结构。Tiktokenizer 的核心特性包括自适应 token 分割、高效编码能力和出色的可扩展性,使其适用于从聊天机器人到大规模文本分析等多种应用场景。通过模块化设计,Tiktokenizer 确保了代码的可重用性和维护性,并在分词精度、处理效率和灵活性方面表现出色。此外,它支持多语言处理、表情符号识别和领域特定文本处理,能够应对各种复杂的文本输入需求。
1220 6
深入解析Tiktokenizer:大语言模型中核心分词技术的原理与架构
|
10月前
|
编解码 缓存 Prometheus
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
本期内容为「ximagine」频道《显示器测试流程》的规范及标准,我们主要使用Calman、DisplayCAL、i1Profiler等软件及CA410、Spyder X、i1Pro 2等设备,是我们目前制作内容数据的重要来源,我们深知所做的仍是比较表面的活儿,和工程师、科研人员相比有着不小的差距,测试并不复杂,但是相当繁琐,收集整理测试无不花费大量时间精力,内容不完善或者有错误的地方,希望大佬指出我们好改进!
723 16
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
|
9月前
|
传感器 人工智能 监控
反向寻车系统怎么做?基本原理与系统组成解析
本文通过反向寻车系统的核心组成部分与技术分析,阐述反向寻车系统的工作原理,适用于适用于商场停车场、医院停车场及火车站停车场等。如需获取智慧停车场反向寻车技术方案前往文章最下方获取,如有项目合作及技术交流欢迎私信作者。
777 2
|
9月前
|
索引
【Flutter 开发必备】AzListView 组件全解析,打造丝滑索引列表!
在 Flutter 开发中,AzListView 是实现字母索引分类列表的理想选择。它支持 A-Z 快速跳转、悬浮分组标题、自定义 UI 和高效性能,适用于通讯录、城市选择等场景。本文将详细解析 AzListView 的核心参数和实战示例,助你轻松实现流畅的索引列表。
472 7
|
10月前
|
Java 数据库 开发者
详细介绍SpringBoot启动流程及配置类解析原理
通过对 Spring Boot 启动流程及配置类解析原理的深入分析,我们可以看到 Spring Boot 在启动时的灵活性和可扩展性。理解这些机制不仅有助于开发者更好地使用 Spring Boot 进行应用开发,还能够在面对问题时,迅速定位和解决问题。希望本文能为您在 Spring Boot 开发过程中提供有效的指导和帮助。
1300 12
|
10月前
|
开发框架 监控 JavaScript
解锁鸿蒙装饰器:应用、原理与优势全解析
ArkTS提供了多维度的状态管理机制。在UI开发框架中,与UI相关联的数据可以在组件内使用,也可以在不同组件层级间传递,比如父子组件之间、爷孙组件之间,还可以在应用全局范围内传递或跨设备传递。
329 2

推荐镜像

更多
  • DNS