大厂Android面试秘籍: Android Activity 状态保存与恢复机制(六)

深度剖析 Android Activity 状态保存与恢复机制:从源码层面解锁底层逻辑

本人掘金号,欢迎点击关注:掘金号地址

本人公众号,欢迎点击关注:公众号地址

一、引言

在 Android 开发的广袤天地中,Activity 作为与用户交互的核心组件,其状态保存与恢复机制宛如精密齿轮,默默保障应用在复杂多变的使用场景下稳定运行。无论是因系统资源紧张导致 Activity 被销毁重建,还是用户主动切换设备方向触发的配置变更,状态保存与恢复机制都肩负着保留用户操作进度、维持应用连贯性体验的重任。深入理解这一机制的底层实现,不仅是提升应用质量的关键,更是开发者进阶路上不可或缺的一环。本文将深入 Android 源码,逐行拆解 Activity 状态保存与恢复模块的工作原理,揭开其神秘面纱。

二、Activity 状态保存与恢复的基本概念

2.1 状态保存与恢复的定义

在 Android 系统中,状态保存指的是当 Activity 即将面临被销毁的风险时,系统自动或开发者手动将 Activity 当前的重要状态信息(如界面上用户输入的数据、滚动位置等)存储起来的过程。而状态恢复则是在 Activity 重新创建时,系统利用之前保存的状态信息,将 Activity 还原到之前状态的操作。这一机制确保了用户在使用应用过程中,即使遇到意外情况(如内存不足导致 Activity 被系统回收),也不会丢失关键数据和操作进度,极大提升了应用的用户体验。

2.2 触发状态保存与恢复的常见场景

  1. 系统内存不足:当系统内存紧张时,为了释放资源,可能会销毁后台或当前不可见的 Activity。一旦内存压力缓解,这些 Activity 可能需要重新创建并恢复到之前的状态。
  2. 配置变更:例如用户旋转设备屏幕,此时 Activity 的配置(如屏幕方向、语言等)发生改变,系统会销毁当前 Activity 并重新创建一个新的 Activity 以适配新的配置。在这个过程中,状态保存与恢复机制会介入,确保用户在旋转屏幕前的操作状态得以保留。
  3. 用户切换应用:当用户从当前应用切换到其他应用,然后再切回时,如果当前 Activity 在后台期间被系统销毁,就需要恢复其状态。

三、状态保存流程的源码分析

3.1 状态保存的触发点

当系统决定需要保存 Activity 的状态时,这一过程通常由 ActivityManagerService(AMS)发起。AMS 作为 Android 系统中管理 Activity 生命周期的核心服务,时刻监控着系统中各个 Activity 的状态。当满足特定条件(如内存不足、配置变更等)时,AMS 会向对应的 Activity 所在的进程发送指令,触发状态保存流程。

3.2 ActivityThread 中的状态保存处理

在应用进程中,ActivityThread 类起着至关重要的作用,它负责管理应用的主线程和 Activity 的生命周期。当 AMS 发出状态保存指令后,ActivityThread 会接收到相关通知并进行处理。以下是 ActivityThread 中与状态保存相关的关键方法和源码分析:

3.2.1 handleStopActivity 方法

java

java 复制代码
// ActivityThread.java
public void handleStopActivity(IBinder token, boolean show, int configChanges,
        int seq, boolean isChild, boolean keepActive) {
    // 获取ActivityClientRecord对象,该对象保存了Activity的相关信息
    ActivityClientRecord r = mActivities.get(token);
    if (r != null) {
        // 设置Activity已停止的标志
        r.activity.mFinished = true;
        r.stopped = true;
        r.paused = true;
        r.visible = false;
        r.configChangeFlags |= configChanges;
        r.hideForNow =!show;
        r.forceNewConfig = false;
        r.setState(ON_STOP);
        if (DEBUG_LIFECYCLE) Slog.v(TAG, "Finishing stop of " + r + " with flags "
                + r.configChangeFlags);
        // 调用performStopActivityInner方法进一步处理
        performStopActivityInner(r, "handleStopActivity", keepActive);
    }
}

handleStopActivity方法中,首先通过mActivities这个 Map 获取与当前 Activity 对应的ActivityClientRecord对象。ActivityClientRecord类保存了 Activity 的诸多重要信息,包括 Activity 的实例、生命周期状态等。获取到ActivityClientRecord后,设置一系列与 Activity 状态相关的标志位,如mFinished表示 Activity 已完成,stoppedpausedvisible等标志位也相应更新,同时记录配置变更的标志configChangeFlags。最后调用performStopActivityInner方法进行更深入的状态保存处理。

3.2.2 performStopActivityInner 方法

java

java 复制代码
// ActivityThread.java
private void performStopActivityInner(ActivityClientRecord r, String reason,
        boolean keepActive) {
    // 判断是否需要保存Activity的状态
    boolean shouldSaveState = r.isPreHoneycomb()
            ||!r.activity.mFinished
            || r.activity.mCalled;
    if (shouldSaveState) {
        // 调用callCallActivityOnSaveInstanceState方法保存状态
        callCallActivityOnSaveInstanceState(r);
    }
    if (r.activity.mVisibleFromClient) {
        // 处理Activity的可见性相关逻辑
        if (DEBUG_VISIBILITY) Slog.v(TAG, "Updating visibility to "
                + (r.activity.mVisibleFromClient? "visible" : "invisible")
                + " for " + r);
        try {
            ActivityManager.getService().activityStopped(r.token,
                    r.activity.mVisibleFromClient);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
    // 标记Activity已停止
    r.activity.mStopped = true;
    if (r.activity.mParent == null &&!keepActive) {
        r.activity.mFinished = true;
    }
}

performStopActivityInner方法中,首先判断是否需要保存 Activity 的状态。判断条件包括 Activity 的版本是否为 Android 3.0(Honeycomb)之前、Activity 是否完成以及mCalled标志位等。如果需要保存状态,则调用callCallActivityOnSaveInstanceState方法。接着处理 Activity 的可见性相关逻辑,通过 AMS 通知系统 Activity 已停止。最后更新 Activity 的停止状态标志。

3.2.3 callCallActivityOnSaveInstanceState 方法

java

java 复制代码
// ActivityThread.java
private void callCallActivityOnSaveInstanceState(ActivityClientRecord r) {
    // 创建一个Bundle对象用于保存状态
    Bundle icicle = new Bundle();
    if (r.isPersistable()) {
        // 如果Activity是可持久化的,创建一个PersistableBundle对象
        PersistableBundle persistentState = new PersistableBundle();
        r.persistentState = persistentState;
        // 调用Instrumentation的callActivityOnSaveInstanceState方法
        mInstrumentation.callActivityOnSaveInstanceState(r.activity, icicle, persistentState);
    } else {
        // 调用Instrumentation的callActivityOnSaveInstanceState方法
        mInstrumentation.callActivityOnSaveInstanceState(r.activity, icicle);
    }
    r.state = icicle;
    r.persistentState = null;
}

callCallActivityOnSaveInstanceState方法主要负责创建用于保存状态的Bundle对象。如果 Activity 是可持久化的(Android 5.0 及以上版本引入的概念,用于处理设备配置变更等情况),还会创建PersistableBundle对象。然后通过mInstrumentation调用callActivityOnSaveInstanceState方法,将 Activity 实例以及创建好的Bundle对象传递进去,实际执行状态保存操作。最后将保存好状态的Bundle对象赋值给ActivityClientRecordstate字段。

3.3 Instrumentation 在状态保存中的作用

Instrumentation 类在 Android 系统中扮演着重要的角色,它为应用程序和系统之间提供了一个桥梁。在 Activity 状态保存过程中,Instrumentation 负责调用 Activity 的具体状态保存方法。以下是 Instrumentation 中callActivityOnSaveInstanceState方法的源码分析:

java

java 复制代码
// Instrumentation.java
public void callActivityOnSaveInstanceState(Activity activity, Bundle outState) {
    // 调用Activity的performSaveInstanceState方法
    activity.performSaveInstanceState(outState);
}

public void callActivityOnSaveInstanceState(Activity activity, Bundle outState,
        PersistableBundle outPersistentState) {
    // 调用Activity的performSaveInstanceState方法
    activity.performSaveInstanceState(outState, outPersistentState);
}

这两个重载的callActivityOnSaveInstanceState方法,最终都是调用 Activity 的performSaveInstanceState方法,将用于保存状态的Bundle对象传递给 Activity,让 Activity 进行具体的状态保存操作。

3.4 Activity 的状态保存实现

在 Activity 类中,performSaveInstanceState方法是状态保存的核心实现。以下是该方法的源码分析:

java

java 复制代码
// Activity.java
final void performSaveInstanceState(Bundle outState) {
    // 调用onSaveInstanceState方法
    onSaveInstanceState(outState);
    // 保存Dialog的状态
    outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
    // 保存转场动画相关状态
    Parcelable transitionState = mWindow.getAttributes().windowAnimations;
    if (transitionState != null) {
        outState.putParcelable(WINDOW_TRANSITION_TAG, transitionState);
    }
    if (mActionBar != null) {
        // 保存ActionBar的状态
        mActionBar.performSaveInstanceState(outState);
    }
    if (mUiThread != null) {
        // 保存用户界面线程相关状态
        mUiThread.saveState(outState);
    }
    if (mFragments != null) {
        // 保存Fragment的状态
        mFragments.saveAllState();
    }
    if (mVoiceInteractor != null) {
        // 保存语音交互相关状态
        mVoiceInteractor.saveInstanceState(outState);
    }
    if (mAssistManager != null) {
        // 保存辅助功能相关状态
        mAssistManager.saveInstanceState(outState);
    }
    if (mActivityTransitionState!= null) {
        // 保存Activity转场动画状态
        mActivityTransitionState.saveState(outState);
    }
}

final void performSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
    // 调用onSaveInstanceState方法
    onSaveInstanceState(outState, outPersistentState);
    // 保存Dialog的状态
    outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
    // 保存转场动画相关状态
    Parcelable transitionState = mWindow.getAttributes().windowAnimations;
    if (transitionState != null) {
        outState.putParcelable(WINDOW_TRANSITION_TAG, transitionState);
    }
    if (mActionBar!= null) {
        // 保存ActionBar的状态
        mActionBar.performSaveInstanceState(outState);
    }
    if (mUiThread!= null) {
        // 保存用户界面线程相关状态
        mUiThread.saveState(outState);
    }
    if (mFragments!= null) {
        // 保存Fragment的状态
        mFragments.saveAllState();
    }
    if (mVoiceInteractor!= null) {
        // 保存语音交互相关状态
        mVoiceInteractor.saveInstanceState(outState);
    }
    if (mAssistManager!= null) {
        // 保存辅助功能相关状态
        mAssistManager.saveInstanceState(outState);
    }
    if (mActivityTransitionState!= null) {
        // 保存Activity转场动画状态
        mActivityTransitionState.saveState(outState);
    }
    if (outPersistentState!= null) {
        // 保存可持久化的状态
        mFragments.saveAllState(outPersistentState);
    }
}

performSaveInstanceState方法中,首先调用onSaveInstanceState方法,这是开发者可以重写的方法,用于保存自定义的状态信息。然后依次保存 Dialog 的状态、转场动画相关状态、ActionBar 的状态、用户界面线程相关状态、Fragment 的状态、语音交互相关状态、辅助功能相关状态以及 Activity 转场动画状态等。如果存在可持久化的Bundle对象(outPersistentState),还会保存可持久化的 Fragment 状态。

3.5 onSaveInstanceState 方法详解

java

java 复制代码
// Activity.java
protected void onSaveInstanceState(Bundle outState) {
    outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState());
    if (mActionBar!= null) {
        mActionBar.performSaveInstanceState(outState);
    }
    if (mUiThread!= null) {
        mUiThread.saveState(outState);
    }
    if (mFragments!= null) {
        mFragments.saveAllState();
    }
    if (mVoiceInteractor!= null) {
        mVoiceInteractor.saveInstanceState(outState);
    }
    if (mAssistManager!= null) {
        mAssistManager.saveInstanceState(outState);
    }
    if (mActivityTransitionState!= null) {
        mActivityTransitionState.saveState(outState);
    }
}

protected void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
    if (outPersistentState!= null) {
        mFragments.saveAllState(outPersistentState);
    }
    onSaveInstanceState(outState);
}

onSaveInstanceState方法有两个重载版本。默认实现中,它会保存一系列与 Activity 相关的状态信息。首先保存Window层次结构的状态,通过调用mWindow.saveHierarchyState()方法。如果 ActionBar 存在,保存 ActionBar 的状态。接着保存用户界面线程、Fragment、语音交互、辅助功能以及 Activity 转场动画等相关状态。在第二个重载版本中,如果存在可持久化的Bundle对象(outPersistentState),会先保存可持久化的 Fragment 状态,然后调用第一个重载版本的onSaveInstanceState方法保存其他状态。

3.6 Window 层次结构的状态保存

在 Activity 的状态保存过程中,Window层次结构的状态保存至关重要,它负责保存 Activity 界面上所有 View 的状态。以下是PhoneWindow类中saveHierarchyState方法的源码分析:

java

java 复制代码
// PhoneWindow.java
public Bundle saveHierarchyState() {
    // 创建一个Bundle对象用于保存状态
    Bundle state = new Bundle();
    if (mDecor == null) {
        return state;
    }
    // 保存View树的状态
    SparseArray<Parcelable> views = new SparseArray<>();
    mDecor.saveHierarchyState(views);
    if (views.size() > 0) {
        state.putSparseParcelableArray(VIEWS_TAG, views);
    }
    // 保存焦点View的ID
    View focused = findFocus();
    if (focused!= null) {
        state.putInt(FOCUS_TAG, focused.getId());
    }
    // 保存Panel的状态
    savePanelState(state);
    return state;
}

saveHierarchyState方法中,首先创建一个Bundle对象state用于保存状态。如果mDecor(即 Activity 的顶级 View)为空,则直接返回空的Bundle。接着创建一个SparseArray<Parcelable>对象views,用于保存 View 树的状态。通过调用mDecor.saveHierarchyState(views)方法,将 View 树的状态保存到views中。如果views中保存了状态信息,则将其放入state中。然后查找当前获得焦点的 View,并将其 ID 保存到state中。最后调用savePanelState(state)方法保存 Panel(如 PopupWindow 等)的状态,最终返回保存好状态的Bundle对象。

3.7 View 的状态保存

View 类提供了saveHierarchyState方法用于保存自身及其子 View 的状态。以下是该方法的源码分析:

java

java 复制代码
// View.java
public void saveHierarchyState(SparseArray<Parcelable> container) {
    // 保存自身的状态
    Parcelable state = onSaveInstanceState();
    if (mID!= NO_ID && state!= null) {
        container.put(mID, state);
    }
    if (this instanceof ViewGroup) {
        // 如果是ViewGroup,递归保存子View的状态
        ViewGroup group = (ViewGroup) this;
        final int count = group.getChildCount();
        for (int i = 0; i < count; i++) {
            group.getChildAt(i).saveHierarchyState(container);
        }
    }
}

protected Parcelable onSaveInstanceState() {
    // 创建一个BaseSavedState对象,包含View的ID
    Parcelable superState = super.onSaveInstanceState();
    if (mID == NO_ID) {
        return superState;
    }
    return new BaseSavedState(superState);
}

saveHierarchyState方法中,首先调用onSaveInstanceState方法保存自身的状态。如果 View 有设置 ID 且保存的状态不为空,则将状态保存到container中。如果当前 View 是ViewGroup,则递归调用子 View 的saveHierarchyState方法,保存所有子 View 的状态。

onSaveInstanceState方法默认创建一个BaseSavedState对象,该对象包含了 View 的 ID 信息。如果 View 没有设置 ID,则直接返回父类保存的状态。

3.8 Fragment 的状态保存

在 Activity 中,如果包含 Fragment,Fragment 的状态也需要保存。FragmentManager 负责管理 Fragment 的状态保存操作。以下是FragmentController类中saveAllState方法的源码分析:

java

java 复制代码
// FragmentController.java
public void saveAllState(Bundle outState, PersistableBundle outPersistentState) {
    if (mActive == null) {
        return;
    }
    // 创建一个ArrayList用于保存Fragment的状态
    ArrayList

java

java 复制代码
    // FragmentController.java
    public void saveAllState(Bundle outState, PersistableBundle outPersistentState) {
        if (mActive == null) {
            return;
        }
        // 创建一个ArrayList用于保存Fragment的状态
        ArrayList<FragmentState> active = new ArrayList<>();
        int N = mActive.size();
        Fragment[] fragments = new Fragment[N];
        mActive.values().toArray(fragments);
        for (int i = 0; i < N; i++) {
            Fragment f = fragments[i];
            if (f != null) {
                if (f.mIndex < 0) {
                    // 如果Fragment的索引小于0,说明该Fragment还未被添加到FragmentManager中,跳过
                    continue;
                }
                FragmentState fs = new FragmentState(f);
                active.add(fs);
                if (f.mState > Fragment.INITIALIZING && fs.mSavedFragmentState == null) {
                    // 如果Fragment的状态大于INITIALIZING且还没有保存过状态
                    fs.mSavedFragmentState = saveFragmentBasicState(f);
                    if (f.mTargetIndex >= 0) {
                        // 如果Fragment有目标Fragment
                        if (fs.mSavedFragmentState == null) {
                            fs.mSavedFragmentState = new Bundle();
                        }
                        // 保存目标Fragment的索引
                        fs.mSavedFragmentState.putInt(
                                FragmentManagerImpl.ARG_TARGET_INDEX, f.mTargetIndex);
                        if (f.mTargetRequestCode != 0) {
                            // 保存目标Fragment的请求码
                            fs.mSavedFragmentState.putInt(
                                    FragmentManagerImpl.ARG_TARGET_REQUEST_CODE, f.mTargetRequestCode);
                        }
                    }
                }
                if (outPersistentState != null && f.mRetainInstance) {
                    // 如果存在可持久化的Bundle对象且Fragment设置了保留实例
                    saveFragmentInstanceState(f, outPersistentState);
                }
            }
        }
        if (active.size() > 0) {
            // 如果有需要保存状态的Fragment,将其保存到outState中
            outState.putParcelableArrayList(
                    FragmentManagerImpl.KEY_ACTIVE_FRAGMENTS, active);
        }

        // 保存Fragment的事务状态
        ArrayList<BackStackRecordState> backStack = null;
        if (mBackStack != null) {
            N = mBackStack.size();
            if (N > 0) {
                backStack = new ArrayList<>();
                for (int i = 0; i < N; i++) {
                    BackStackRecord bs = mBackStack.get(i);
                    backStack.add(new BackStackRecordState(bs));
                }
            }
        }
        if (backStack != null) {
            // 如果有需要保存的事务状态,将其保存到outState中
            outState.putParcelableArrayList(
                    FragmentManagerImpl.KEY_BACK_STACK_ENTRIES, backStack);
        }

        // 保存当前的Fragment索引
        outState.putInt(FragmentManagerImpl.KEY_LAST_SEQUENCE_NUMBER, mNextFragmentIndex);
        outState.putInt(FragmentManagerImpl.KEY_LAST_BACK_STACK_ID, mMaxBackStackId);
    }

saveAllState 方法中,首先检查 mActive 是否为空,如果为空则直接返回。接着创建一个 ArrayList<FragmentState> 类型的 active 列表,用于存储需要保存状态的 Fragment。遍历 mActive 中的所有 Fragment,对于每个 Fragment,如果其索引小于 0,说明该 Fragment 还未被添加到 FragmentManager 中,跳过该 Fragment。否则,创建一个 FragmentState 对象 fs,并将其添加到 active 列表中。

如果 Fragment 的状态大于 INITIALIZING 且还没有保存过状态,则调用 saveFragmentBasicState 方法保存 Fragment 的基本状态。如果 Fragment 有目标 Fragment,则将目标 Fragment 的索引和请求码保存到 fs.mSavedFragmentState 中。

如果存在可持久化的 Bundle 对象 outPersistentStateFragment 设置了保留实例(mRetainInstancetrue),则调用 saveFragmentInstanceState 方法保存 Fragment 的可持久化状态。

如果 active 列表中有元素,则将其保存到 outState 中。接着处理 Fragment 的事务状态,将 mBackStack 中的每个 BackStackRecord 转换为 BackStackRecordState 对象,并保存到 backStack 列表中。如果 backStack 列表不为空,则将其保存到 outState 中。

最后,将当前的 Fragment 索引和最大的 BackStack ID 保存到 outState 中。

3.8.1 saveFragmentBasicState 方法

java

java 复制代码
    // FragmentController.java
    Bundle saveFragmentBasicState(Fragment f) {
        // 创建一个Bundle对象用于保存状态
        Bundle result = new Bundle();
        // 保存Fragment的类名
        result.putString(FragmentManagerImpl.ARG_CLASS_NAME, f.getClass().getName());
        if (f.mArguments != null) {
            // 保存Fragment的参数
            result.putBundle(FragmentManagerImpl.ARG_ARGS, f.mArguments);
        }
        if (f.mSavedViewState != null) {
            // 保存Fragment的视图状态
            result.putSparseParcelableArray(FragmentManagerImpl.ARG_VIEW_STATE, f.mSavedViewState);
        }
        if (f.mUserVisibleHint) {
            // 保存Fragment的用户可见性提示
            result.putBoolean(FragmentManagerImpl.ARG_USER_VISIBLE_HINT, true);
        }
        if (f.mTargetIndex >= 0) {
            // 保存目标Fragment的索引
            result.putInt(FragmentManagerImpl.ARG_TARGET_INDEX, f.mTargetIndex);
            if (f.mTargetRequestCode != 0) {
                // 保存目标Fragment的请求码
                result.putInt(FragmentManagerImpl.ARG_TARGET_REQUEST_CODE, f.mTargetRequestCode);
            }
        }
        if (f.mBackStackNesting > 0) {
            // 保存Fragment的BackStack嵌套层数
            result.putInt(FragmentManagerImpl.ARG_BACK_STACK_NESTING, f.mBackStackNesting);
        }
        // 调用Fragment的onSaveInstanceState方法保存自定义状态
        f.performSaveInstanceState(result);
        return result;
    }

saveFragmentBasicState 方法用于保存 Fragment 的基本状态。首先创建一个 Bundle 对象 result,然后依次保存 Fragment 的类名、参数、视图状态、用户可见性提示、目标 Fragment 的索引和请求码、BackStack 嵌套层数等信息。最后调用 FragmentperformSaveInstanceState 方法,让 Fragment 保存自己的自定义状态。

3.8.2 saveFragmentInstanceState 方法

java

java 复制代码
    // FragmentController.java
    private void saveFragmentInstanceState(Fragment f, PersistableBundle outPersistentState) {
        // 创建一个PersistableBundle对象用于保存可持久化状态
        PersistableBundle state = new PersistableBundle();
        // 调用Fragment的onSaveInstanceState方法保存可持久化状态
        f.performSaveInstanceState(state);
        if (!state.isEmpty()) {
            // 如果保存的状态不为空,将其保存到outPersistentState中
            outPersistentState.putPersistableBundle(
                    FragmentManagerImpl.KEY_RETAINED_FRAGMENT_STATE_PREFIX + f.mWho, state);
        }
    }

saveFragmentInstanceState 方法用于保存 Fragment 的可持久化状态。首先创建一个 PersistableBundle 对象 state,然后调用 FragmentperformSaveInstanceState 方法,让 Fragment 保存自己的可持久化状态。如果保存的状态不为空,则将其保存到 outPersistentState 中,使用 FragmentmWho 作为键的前缀。

3.9 总结状态保存流程

整个 Activity 状态保存流程从 AMS 发起通知开始,经过 ActivityThread、Instrumentation 的处理,最终由 Activity 及其包含的各个组件(如 View、Fragment 等)完成状态保存。具体步骤如下:

  1. AMS 通知:当系统需要保存 Activity 状态时,AMS 向对应的 Activity 所在进程发送通知。
  2. ActivityThread 处理 :ActivityThread 接收到通知后,调用 handleStopActivity 方法,在该方法中进一步调用 performStopActivityInner 方法判断是否需要保存状态,如果需要则调用 callCallActivityOnSaveInstanceState 方法。
  3. Instrumentation 调用callCallActivityOnSaveInstanceState 方法通过 mInstrumentation 调用 callActivityOnSaveInstanceState 方法,进而调用 Activity 的 performSaveInstanceState 方法。
  4. Activity 保存状态 :在 performSaveInstanceState 方法中,调用 onSaveInstanceState 方法保存自定义状态,同时保存 Dialog、ActionBar、View、Fragment 等组件的状态。
  5. View 状态保存 :View 通过 saveHierarchyState 方法保存自身及其子 View 的状态。
  6. Fragment 状态保存 :FragmentManager 通过 saveAllState 方法保存 Fragment 的状态,包括基本状态和可持久化状态。

四、状态恢复流程的源码分析

4.1 状态恢复的触发点

当 Activity 由于某些原因(如配置变更、内存不足被销毁后重新创建)需要恢复状态时,系统会在 Activity 的创建过程中触发状态恢复流程。通常在 Activity 的 onCreateonRestoreInstanceState 方法中进行状态恢复操作。

4.2 ActivityThread 中的状态恢复处理

在 Activity 重新创建时,ActivityThread 会负责接收并处理保存的状态信息,将其传递给 Activity 进行恢复。以下是 ActivityThread 中与状态恢复相关的关键方法和源码分析:

4.2.1 handleLaunchActivity 方法

java

java 复制代码
    // ActivityThread.java
    public Activity handleLaunchActivity(ActivityClientRecord r,
            PendingTransactionActions pendingActions, Intent customIntent) {
        // 调用performLaunchActivity方法创建并启动Activity
        Activity a = performLaunchActivity(r, customIntent);
        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            reportSizeConfigurations(r);
            if (!r.activity.mFinished && pendingActions != null) {
                // 处理挂起的事务操作
                pendingActions.setOldState(r.state);
                pendingActions.setRestoreInstanceState(true);
                pendingActions.setCallOnPostCreate(true);
            }
            handleResumeActivity(r.token, false, r.isForward,
                    "LAUNCH_ACTIVITY", pendingActions, false);
        }
        return a;
    }

handleLaunchActivity 方法中,首先调用 performLaunchActivity 方法创建并启动 Activity。如果 Activity 创建成功,记录当前的配置信息,并处理挂起的事务操作。最后调用 handleResumeActivity 方法将 Activity 置于恢复状态。

4.2.2 performLaunchActivity 方法

java

java 复制代码
    // ActivityThread.java
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        // 获取Activity的信息
        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }
        // 获取Activity的类名
        ComponentName component = r.intent.getComponent();
        if (component == null) {
            component = r.intent.resolveActivity(
                    mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }
        if (r.activityInfo.targetActivity != null) {
            component = new ComponentName(r.activityInfo.packageName,
                    r.activityInfo.targetActivity);
        }
        // 创建Activity的实例
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                // 如果存在保存的状态信息,调用Activity的onCreate方法并传入状态信息
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                        "Unable to instantiate activity " + component
                        + ": " + e.toString(), e);
            }
        }
        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            if (activity != null) {
                Context appContext = createBaseContextForActivity(r, activity);
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances,
                        r.config, r.referrer, r.voiceInteractor, window,
                        r.configCallback, r.assistToken);
                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }
                activity.mCalled = false;
                if (r.state != null) {
                    // 如果存在保存的状态信息,调用Activity的onCreate方法并传入状态信息
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    // 调用Activity的onCreate方法
                    mInstrumentation.callActivityOnCreate(activity, null, r.persistentState);
                }
                if (!activity.mCalled) {
                    throw new SuperNotCalledException(
                            "Activity " + r.intent.getComponent().toShortString() +
                            " did not call through to super.onCreate()");
                }
                r.activity = activity;
                r.stopped = true;
                if (!r.activity.mFinished) {
                    activity.performStart();
                    r.stopped = false;
                }
                if (!r.activity.mFinished) {
                    if (r.state != null) {
                        // 如果存在保存的状态信息,调用Activity的onRestoreInstanceState方法
                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state, r.persistentState);
                    }
                }
                if (!r.activity.mFinished) {
                    activity.mCalled = false;
                    mInstrumentation.callActivityOnPostCreate(activity, r.state, r.persistentState);
                    if (!activity.mCalled) {
                        throw new SuperNotCalledException(
                                "Activity " + r.intent.getComponent().toShortString() +
                                " did not call through to super.onPostCreate()");
                    }
                }
            }
            r.paused = true;
            mActivities.put(r.token, r);
        } catch (Exception e) {
            if (!mInstrumentation.onException(activity, e)) {
                throw new RuntimeException(
                        "Unable to start activity " + component
                        + ": " + e.toString(), e);
            }
        }
        return activity;
    }

performLaunchActivity 方法中,首先获取 Activity 的信息,包括类名、包信息等。然后使用 mInstrumentation.newActivity 方法创建 Activity 的实例。如果存在保存的状态信息(r.state 不为空),则将其类加载器设置为当前 Activity 的类加载器。

接着创建 Activity 的上下文,并调用 activity.attach 方法将 Activity 与上下文、ActivityThread 等关联起来。根据是否存在保存的状态信息,调用 mInstrumentation.callActivityOnCreate 方法传入相应的状态信息。

如果 Activity 创建成功且未被销毁,调用 activity.performStart 方法启动 Activity。如果存在保存的状态信息,调用 mInstrumentation.callActivityOnRestoreInstanceState 方法进行状态恢复。最后调用 mInstrumentation.callActivityOnPostCreate 方法完成 Activity 的创建后操作。

4.3 Instrumentation 在状态恢复中的作用

Instrumentation 在状态恢复过程中同样起着桥梁的作用,负责调用 Activity 的具体状态恢复方法。以下是 Instrumentation 中与状态恢复相关的方法的源码分析:

4.3.1 callActivityOnCreate 方法

java

java 复制代码
    // Instrumentation.java
    public void callActivityOnCreate(Activity activity, Bundle icicle) {
        // 调用Activity的performCreate方法
        activity.performCreate(icicle);
    }

    public void callActivityOnCreate(Activity activity, Bundle icicle,
            PersistableBundle persistentState) {
        // 调用Activity的performCreate方法
        activity.performCreate(icicle, persistentState);
    }

这两个重载的 callActivityOnCreate 方法,最终都是调用 Activity 的 performCreate 方法,将保存的状态信息(BundlePersistableBundle)传递给 Activity,让 Activity 进行创建操作。

4.3.2 callActivityOnRestoreInstanceState 方法

java

java 复制代码
    // Instrumentation.java
    public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState) {
        // 调用Activity的performRestoreInstanceState方法
        activity.performRestoreInstanceState(savedInstanceState);
    }

    public void callActivityOnRestoreInstanceState(Activity activity, Bundle savedInstanceState,
            PersistableBundle persistentState) {
        // 调用Activity的performRestoreInstanceState方法
        activity.performRestoreInstanceState(savedInstanceState, persistentState);
    }

这两个重载的 callActivityOnRestoreInstanceState 方法,最终都是调用 Activity 的 performRestoreInstanceState 方法,将保存的状态信息传递给 Activity,让 Activity 进行状态恢复操作。

4.4 Activity 的状态恢复实现

在 Activity 类中,performCreateperformRestoreInstanceState 方法是状态恢复的核心实现。以下是这两个方法的源码分析:

4.4.1 performCreate 方法

java

java 复制代码
    // Activity.java
    final void performCreate(Bundle icicle) {
        // 调用onCreate方法
        onCreate(icicle);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();
    }

    final void performCreate(Bundle icicle, PersistableBundle persistentState) {
        // 调用onCreate方法
        onCreate(icicle, persistentState);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();
    }

performCreate 方法有两个重载版本,分别处理不同类型的状态信息。在这两个方法中,首先调用 onCreate 方法,这是开发者可以重写的方法,用于在 Activity 创建时进行初始化操作,并传入保存的状态信息。然后调用 mActivityTransitionState.readState 方法读取 Activity 转场动画的状态。最后调用 performCreateCommon 方法完成一些通用的创建操作。

4.4.2 performRestoreInstanceState 方法

java

java 复制代码
    // Activity.java
    final void performRestoreInstanceState(Bundle savedInstanceState) {
        if (savedInstanceState != null) {
            // 调用onRestoreInstanceState方法
            onRestoreInstanceState(savedInstanceState);
            // 恢复Window层次结构的状态
            mWindow.restoreHierarchyState(savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG));
            // 恢复ActionBar的状态
            if (mActionBar != null) {
                mActionBar.performRestoreInstanceState(savedInstanceState);
            }
            // 恢复Fragment的状态
            if (mFragments != null) {
                mFragments.restoreAllState(savedInstanceState,
                        getNonConfigInstance());
            }
        }
    }

    final void performRestoreInstanceState(Bundle savedInstanceState,
            PersistableBundle persistentState) {
        if (savedInstanceState != null) {
            // 调用onRestoreInstanceState方法
            onRestoreInstanceState(savedInstanceState, persistentState);
            // 恢复Window层次结构的状态
            mWindow.restoreHierarchyState(savedInstanceState.getBundle(WINDOW_HIERARCHY_TAG));
            // 恢复ActionBar的状态
            if (mActionBar != null) {
                mActionBar.performRestoreInstanceState(savedInstanceState);
            }
            // 恢复Fragment的状态
            if (mFragments != null) {
                mFragments.restoreAllState(savedInstanceState,
                        getNonConfigInstance());
                if (persistentState != null) {
                    mFragments.restoreAllState(persistentState);
                }
            }
        }
    }

performRestoreInstanceState 方法有两个重载版本,分别处理不同类型的状态信息。在这两个方法中,首先检查保存的状态信息是否为空。如果不为空,调用 onRestoreInstanceState 方法,这是开发者可以重写的方法,用于在 Activity 恢复状态时进行自定义的恢复操作。然后依次恢复 Window 层次结构的状态、ActionBar 的状态和 Fragment 的状态。如果存在可持久化的状态信息(persistentState 不为空),则调用 mFragments.restoreAllState 方法恢复可持久化的 Fragment 状态。

4.5 Window 层次结构的状态恢复

在 Activity 的状态恢复过程中,Window 层次结构的状态恢复同样重要,它负责恢复 Activity 界面上所有 View 的状态。以下是 PhoneWindow 类中 restoreHierarchyState 方法的源码分析:

java

java 复制代码
    // PhoneWindow.java
    public void restoreHierarchyState(Bundle savedInstanceState) {
        if (savedInstanceState != null) {
            // 恢复View树的状态
            SparseArray<Parcelable> views = savedInstanceState.getSparseParcelableArray(VIEWS_TAG);
            if (views != null) {
                mDecor.restoreHierarchyState(views);
            }
            // 恢复焦点View的状态
            int focusedViewId = savedInstanceState.getInt(FOCUS_TAG, View.NO_ID);
            if (focusedViewId != View.NO_ID) {
                View focusedView = mDecor.findViewById(focusedViewId);
                if (focusedView != null) {
                    focusedView.requestFocus();
                }
            }
            // 恢复Panel的状态
            restorePanelState(savedInstanceState);
        }
    }

restoreHierarchyState 方法中,首先检查保存的状态信息是否为空。如果不为空,从 savedInstanceState 中获取保存的 View 树状态信息(SparseArray<Parcelable> 类型),并调用 mDecor.restoreHierarchyState 方法恢复 View 树的状态。然后获取保存的焦点 View 的 ID,查找对应的 View 并请求焦点。最后调用 restorePanelState 方法恢复 Panel 的状态。

4.6 View 的状态恢复

View 类提供了 restoreHierarchyState 方法用于恢复自身及其子 View 的状态。以下是该方法的源码分析:

java

java 复制代码
    // View.java
    public void restoreHierarchyState(SparseArray<Parcelable> container) {
        // 恢复自身的状态
        if (mID != NO_ID) {
            Parcelable state = container.get(mID);
            if (state != null) {
                onRestoreInstanceState(state);
            }
        }
        if (this instanceof ViewGroup) {
            // 如果是ViewGroup,递归恢复子View的状态
            ViewGroup group = (ViewGroup) this;
            final int count = group.getChildCount();
            for (int i = 0; i < count; i++) {
                group.getChildAt(i).restoreHierarchyState(container);
            }
        }
    }

    protected void onRestoreInstanceState(Parcelable state) {
        // 恢复父类的状态
        super.onRestoreInstanceState(state);
    }

restoreHierarchyState 方法中,首先检查 View 是否有设置 ID。如果有设置 ID,从 container 中获取对应的状态信息,并调用 onRestoreInstanceState 方法恢复自身的状态。如果当前 View 是 ViewGroup,则递归调用子 View 的 restoreHierarchyState 方法,恢复所有子 View 的状态。

onRestoreInstanceState 方法默认调用父类的 onRestoreInstanceState 方法,恢复父类的状态。

4.7 Fragment 的状态恢复

在 Activity 中,如果包含 Fragment,Fragment 的状态也需要恢复。FragmentManager 负责管理 Fragment 的状态恢复操作。以下是 FragmentController 类中 restoreAllState 方法的源码分析:

java

java 复制代码
    // FragmentController.java
    public void restoreAllState(Bundle savedInstanceState, FragmentManagerNonConfig nonConfig) {
        if (savedInstanceState == null) {
            return;
        }
        // 恢复Fragment的活跃状态
        ArrayList<FragmentState> activeFragments = savedInstanceState.getParcelableArrayList(
                FragmentManagerImpl.KEY_ACTIVE_FRAGMENTS);
        if (activeFragments != null) {
            mActive = new ArrayMap<>();
            int N = activeFragments.size();
            for (int i = 0; i < N; i++) {
                FragmentState fs = activeFragments.get(i);
                if (fs != null) {
                    Fragment f = fs.instantiate(mHost, mParent, nonConfig);
                    if (DEBUG) Log.v(TAG, "restoreAllState: active #" + i + ": " + f);
                    mActive.put(f.mIndex, f);
                }
            }
        }
        // 恢复Fragment的BackStack状态
        ArrayList<BackStackRecordState> backStack = savedInstanceState.getParcelableArrayList(
                FragmentManagerImpl.KEY_BACK_STACK_ENTRIES);
        if (backStack != null) {
            mBackStack = new ArrayList<>();
            int N = backStack.size();
            for (int i = 0; i < N; i++) {
                BackStackRecordState bss = backStack.get(i);
                if (bss != null) {
                    BackStackRecord bs = new BackStackRecord(mHost, bss);
                    if (DEBUG) Log.v(TAG, "restoreAllState: back stack #" + i + ": " + bs);
                    mBackStack.add(bs);
                }
            }
        }
        // 恢复当前的Fragment索引
        mNextFragmentIndex = savedInstanceState.getInt(FragmentManagerImpl.KEY_LAST_SEQUENCE_NUMBER);
        mMaxBackStackId = savedInstanceState.getInt(FragmentManagerImpl.KEY_LAST_BACK_STACK_ID);
    }

    public void restoreAllState(PersistableBundle savedInstanceState) {
        if (savedInstanceState == null) {
            return;
        }
        // 恢复可持久化的Fragment状态
        for (String key : savedInstanceState.keySet()) {
            if (key.startsWith(FragmentManagerImpl.KEY_RETAINED_FRAGMENT_STATE_PREFIX)) {
                String who = key.substring(FragmentManagerImpl.KEY_RETAINED_FRAGMENT_STATE_PREFIX.length());
                Fragment f = findFragmentByWho(who);
                if (f != null) {
                    PersistableBundle state = savedInstanceState.getPersistableBundle(key);
                    f.performRestoreInstanceState(state);
                }
            }
        }
    }

restoreAllState 方法有两个重载版本,分别处理不同类型的状态信息。在第一个重载版本中,首先检查保存的状态信息是否为空。如果不为空,从 savedInstanceState 中获取保存的活跃 Fragment 状态信息(ArrayList<FragmentState> 类型),并将其恢复到 mActive 中。然后获取保存的 BackStack 状态信息(ArrayList<BackStackRecordState> 类型),并将其恢复到 mBackStack 中。最后恢复当前的 Fragment 索引和最大的 BackStack ID。

在第二个重载版本中,检查保存的可持久化状态信息是否为空。如果不为空,遍历 savedInstanceState 中的所有键,找到以 FragmentManagerImpl.KEY_RETAINED_FRAGMENT_STATE_PREFIX 开头的键,获取对应的可持久化状态信息,并调用 FragmentperformRestoreInstanceState 方法恢复 Fragment 的可持久化状态。

4.8 总结状态恢复流程

整个 Activity 状态恢复流程从 ActivityThread 创建 Activity 开始,经过 Instrumentation 的调用,最终由 Activity 及其包含的各个组件(如 View、Fragment 等)完成状态恢复。具体步骤如下:

  1. ActivityThread 创建 Activity :在 performLaunchActivity 方法中,创建 Activity 的实例,并根据是否存在保存的状态信息,调用 mInstrumentation.callActivityOnCreate 方法传入相应的状态信息。
  2. Instrumentation 调用 Activity 方法callActivityOnCreate 方法调用 Activity 的 performCreate 方法,callActivityOnRestoreInstanceState 方法调用 Activity 的 performRestoreInstanceState 方法。
  3. Activity 恢复状态 :在 performRestoreInstanceState 方法中,调用 onRestoreInstanceState 方法进行自定义状态恢复,同时恢复 Window 层次结构、ActionBar、Fragment 等组件的状态。
  4. View 状态恢复 :View 通过 restoreHierarchyState 方法恢复自身及其子 View 的状态。
  5. Fragment 状态恢复 :FragmentManager 通过 restoreAllState 方法恢复 Fragment 的状态,包括活跃状态、BackStack 状态和可持久化状态。

五、特殊情况与注意事项

5.1 配置变更时的状态保存与恢复

在 Android 开发中,配置变更(如屏幕旋转、语言切换等)是常见的情况。当发生配置变更时,默认情况下,Activity 会被销毁并重新创建,这就需要进行状态保存与恢复操作。

5.1.1 系统默认处理

当配置变更发生时,系统会自动调用 Activity 的 onSaveInstanceState 方法保存状态,然后销毁 Activity。在重新创建 Activity 时,会将保存的状态信息传递给 onCreateonRestoreInstanceState 方法进行恢复。

5.1.2 手动处理配置变更

开发者可以通过在 AndroidManifest.xml 中为 Activity 设置 android:configChanges 属性来手动处理配置变更。例如:

xml

java 复制代码
<activity
    android:name=".MainActivity"
    android:configChanges="orientation|screenSize">
    ...
</activity>

当设置了 android:configChanges 属性后,发生指定的配置变更时,Activity 不会被销毁重建,而是会调用 onConfigurationChanged 方法。开发者可以在该方法中进行自定义的配置变更处理,而不需要依赖状态保存与恢复机制。

5.2 内存不足时的状态保存与恢复

当系统内存不足时,为了释放资源,可能会销毁一些处于后台或不可见状态的 Activity。在这种情况下,Activity 的状态保存与恢复机制同样会发挥作用。

5.2.1 系统自动销毁 Activity

当系统检测到内存不足时,会根据一定的策略(如 LRU 算法)选择一些 Activity 进行销毁。在销毁 Activity 之前,系统会调用 onSaveInstanceState 方法保存状态。

5.2.2 Activity 重新创建与状态恢复

当用户再次回到该 Activity 时,系统会重新创建 Activity,并将之前保存的状态信息传递给 onCreateonRestoreInstanceState 方法进行恢复。

5.3 手动销毁 Activity 时的状态处理

当用户手动按下返回键或调用 finish 方法销毁 Activity 时,系统不会调用 onSaveInstanceState 方法保存状态。因为在这种情况下,用户明确表示要结束当前 Activity,不需要保存状态。

5.4 自定义状态保存与恢复

在实际开发中,开发者可能需要保存和恢复一些自定义的状态信息。可以通过重写 Activity 的 onSaveInstanceStateonRestoreInstanceState 方法来实现。

5.4.1 重写 onSaveInstanceState 方法

java

java 复制代码
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    // 保存自定义状态信息
    outState.putString("custom_key", "custom_value");
}
5.4.2 重写 onRestoreInstanceState 方法

java

java 复制代码
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    // 恢复自定义状态信息
    String customValue = savedInstanceState.getString("custom_key");
    if (customValue != null) {
        // 使用恢复的状态信息
    }
}

5.5 状态保存与恢复的性能考虑

状态保存与恢复机制虽然方便,但在某些情况下可能会影响应用的性能。例如,当保存和恢复的状态信息较多时,会增加 Activity 的创建和销毁时间,导致界面响应变慢。

5.5.1 优化策略
  • 减少不必要的状态保存:只保存必要的状态信息,避免保存大量无用的数据。
  • 使用 ViewModel:ViewModel 可以在配置变更时保留数据,减少状态保存与恢复的工作量。
  • 异步处理:对于一些耗时的状态保存与恢复操作,可以考虑使用异步线程进行处理,避免阻塞主线程。

六、总结与展望

6.1 总结

通过对 Android Activity 状态保存与恢复机制的源码分析,我们深入了解了这一机制的实现原理和工作流程。状态保存与恢复机制在 Android 系统中起着至关重要的作用,它确保了 Activity 在各种情况下(如配置变更、内存不足等)能够正确保存和恢复状态,为用户提供了流畅、连贯的使用体验。

整个状态保存流程从 AMS 发起通知开始,经过 ActivityThread、Instrumentation 的处理,最终由 Activity 及其包含的各个组件(如 View、Fragment 等)完成状态保存。状态恢复流程则从 ActivityThread 创建 Activity 开始,同样经过 Instrumentation 的调用,由 Activity 及其组件完成状态恢复。

在实际开发中,开发者可以根据具体需求重写 onSaveInstanceStateonRestoreInstanceState 方法,实现自定义的状态保存与恢复。同时,需要注意一些特殊情况(如配置变更、内存不足等)的处理,以及状态保存与恢复的性能优化。

6.2 展望

随着 Android 系统的不断发展,Activity 状态保存与恢复机制也可能会得到进一步的优化和改进。未来可能会出现更加高效、灵活的状态保存与恢复方式,例如支持更多类型的数据保存、更好地处理大数据量的状态信息等。

同时,随着 Android Jetpack 组件的不断完善,如 ViewModel、SavedStateHandle 等,开发者可以更加方便地进行状态管理,减少对传统状态保存与恢复机制的依赖。这将使得开发者能够更加专注于业务逻辑的实现,提高开发效率和应用的质量。

总之,深入理解 Activity 状态保存与恢复机制对于 Android 开发者来说是非常重要的,它不仅能够帮助我们解决实际开发中的问题,还能为我们未来的开发工作打下坚实的基础。

相关推荐
百锦再11 分钟前
Android游戏辅助工具开发详解
android·游戏·模拟·识别·辅助·外挂
QING61831 分钟前
Kotlin 类型转换与超类 Any 详解
android·kotlin·app
QING6181 小时前
一文带你了解 Kotlin infix 函数的基本用法和使用场景
android·kotlin·app
张风捷特烈1 小时前
平面上的三维空间#04 | 万物之母 - 三角形
android·flutter·canvas
恋猫de小郭2 小时前
Android Studio Cloud 正式上线,不只是 Android,随时随地改 bug
android·前端·flutter
匹马夕阳8 小时前
(十八)安卓开发中的后端接口调用详讲解
android
Pigwantofly9 小时前
鸿蒙ArkTS实战:从零打造智能表达式计算器(附状态管理+路由传参核心实现)
android·华为·harmonyos
拉不动的猪10 小时前
设计模式之------单例模式
前端·javascript·面试
xiegwei10 小时前
Kotlin 和 spring-cloud-function 兼容问题
开发语言·kotlin·springcloud
Gracker11 小时前
Android Weekly #202514
android