Android原理——SavedState

本文介绍如何利用Android的BaseSavedState类来有效保存和恢复Activity中的View状态,避免了传统public static方法的局限性。通过具体的ViewPager和SeekBar示例展示了BaseSavedState的使用方法。

引言

还在用public static T 保存View状态?
Android官方早已想到了:
使用View.BaseSavedState来保存状态。

  1. 在一个activity被销毁前,不一定会调用onSaveInstanceState()这个方法,因为不是所有情况都需要去存储activity的状态(例如当用户按回退键退出你的activity的时候,因为用户指定关掉这个activity)。

  2. 如果这个方法被调用,它一定会在 onStop()方法之前,可能会在onPause()方法之前。

  3. 布局中的每一个View默认实现了onSaveInstanceState()方法,这样的话,这个UI的任何改变都会自动的存储和在activity重新创建的时候自动的恢复。但是这种情况只有在你为这个UI提供了唯一的ID之后才起作用,如果没有提供ID,将不会存储它的状态。

  4. 由于默认的onSaveInstanceState()方法的实现帮助UI存储它的状态,所以如果你需要覆盖这个方法去存储额外的状态信息时,你应该在执行任何代码之前都调用父类的onSaveInstanceState()方法(super.onSaveInstanceState())。

  5. 由于onSaveInstanceState()方法调用的不确定性,你应该只使用这个方法去记录activity的瞬间状态(UI的状态)。不应该用这个方法去存储持久化数据。当用户离开这个activity的时候应该在onPause()方法中存储持久化数据(例如应该被存储到数据库中的数据)。


类概述

BaseSavedState
Base class for derived classes that want to save and restore their own state in onSaveInstanceState().

直接子类

TextView.SavedState
ViewPager.SavedState


How To Use

读取保存状态
onRestoreInstanceState(Parcelable state)
保存状态
Parcelable onSaveInstanceState()

举个ViewPager.SavedState的示例

    /**
     * 读取保存状态
     * */
    @Override
    public void onRestoreInstanceState(Parcelable state) {
        SavedState savedState = (SavedState)state;
        super.onRestoreInstanceState(savedState.getSuperState());
        mCurrentPage = savedState.currentPage;
        mSnapPage = savedState.currentPage;
        requestLayout();
    }
    /**
     * 保存状态
     * */
    @Override
    public Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        SavedState savedState = new SavedState(superState);
        savedState.currentPage = mCurrentPage;
        return savedState;
    }

    /**
     * 保存界面状态
     * */
    static class SavedState extends BaseSavedState {
        //当前页
        int currentPage;

        public SavedState(Parcelable superState) {
            super(superState);
        }

        private SavedState(Parcel in) {
            super(in);
            currentPage = in.readInt();
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            super.writeToParcel(dest, flags);
            dest.writeInt(currentPage);
        }

        public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
            @Override
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            @Override
            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }


BaseSavedState源码

我们看一下它的构造方法

public static class BaseSavedState extends AbsSavedState {
        /**
         * Constructor used when reading from a parcel. Reads the state of the superclass.
         *
         * @param source
         */
        public BaseSavedState(Parcel source) {
            super(source);
        }

        /**
         * Constructor called by derived classes when creating their SavedState objects
         *
         * @param superState The state of the superclass of this view
         */
        public BaseSavedState(Parcelable superState) {
            super(superState);
        }

        public static final Parcelable.Creator<BaseSavedState> CREATOR =
                new Parcelable.Creator<BaseSavedState>() {
            public BaseSavedState createFromParcel(Parcel in) {
                return new BaseSavedState(in);
            }

            public BaseSavedState[] newArray(int size) {
                return new BaseSavedState[size];
            }
        };
    }

而其父类实现了Parcelable借口

/**
 * A {@link Parcelable} implementation that should be used by inheritance
 * hierarchies to ensure the state of all classes along the chain is saved.
 */
public abstract class AbsSavedState implements Parcelable {
    public static final AbsSavedState EMPTY_STATE = new AbsSavedState() {};

    private final Parcelable mSuperState;

    /**
     * Constructor used to make the EMPTY_STATE singleton
     */
    private AbsSavedState() {
        mSuperState = null;
    }

    /**
     * Constructor called by derived classes when creating their SavedState objects
     * 
     * @param superState The state of the superclass of this view
     */
    protected AbsSavedState(Parcelable superState) {
        if (superState == null) {
            throw new IllegalArgumentException("superState must not be null");
        }
        mSuperState = superState != EMPTY_STATE ? superState : null;
    }

    /**
     * Constructor used when reading from a parcel. Reads the state of the superclass.
     * 
     * @param source
     */
    protected AbsSavedState(Parcel source) {
        // FIXME need class loader
        Parcelable superState = source.readParcelable(null);

        mSuperState = superState != null ? superState : EMPTY_STATE;
    }

    final public Parcelable getSuperState() {
        return mSuperState;
    }

    public int describeContents() {
        return 0;
    }

    public void writeToParcel(Parcel dest, int flags) {
         dest.writeParcelable(mSuperState, flags);
    }

    public static final Parcelable.Creator<AbsSavedState> CREATOR 
        = new Parcelable.Creator<AbsSavedState>() {

        public AbsSavedState createFromParcel(Parcel in) {
            Parcelable superState = in.readParcelable(null);
            if (superState != null) {
                throw new IllegalStateException("superState must be null");
            }
            return EMPTY_STATE;
        }

        public AbsSavedState[] newArray(int size) {
            return new AbsSavedState[size];
        }
    };
}

补充

最后再举一个定义SeekBar的

3   import android.content.Context;
4   import android.content.res.TypedArray;
5   import android.os.Parcel;
6   import android.os.Parcelable;
7   import android.util.AttributeSet;
8   import android.view.View;
9   import android.view.ViewGroup;
10  import android.view.ViewParent;
11  
12  import com.WazaBe.HoloEverywhere.LayoutInflater;
13  import com.WazaBe.HoloEverywhere.R;
14  import com.WazaBe.HoloEverywhere.widget.SeekBar;
15  
16  public class SeekBarDialogPreference extends DialogPreference {
17      private static class SavedState extends BaseSavedState {
18          @SuppressWarnings("unused")
19          public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
20              @Override
21              public SavedState createFromParcel(Parcel in) {
22                  return new SavedState(in);
23              }
24  
25              @Override
26              public SavedState[] newArray(int size) {
27                  return new SavedState[size];
28              }
29          };
30  
31          protected int mValue, mMaxValue;
32  
33          public SavedState(Parcel source) {
34              super(source);
35              mValue = source.readInt();
36              mMaxValue = source.readInt();
37          }
38  
39          public SavedState(Parcelable superState) {
40              super(superState);
41          }
42  
43          @Override
44          public void writeToParcel(Parcel dest, int flags) {
45              super.writeToParcel(dest, flags);
46              dest.writeInt(mValue);
47              dest.writeInt(mMaxValue);
48          }
49      }
50  
51      private final SeekBar mSeekBar;
52      private int mValue = Integer.MIN_VALUE, mMaxValue = Integer.MIN_VALUE;
53  
54      public SeekBarDialogPreference(Context context) {
55          this(context, null);
56      }
57  
58      public SeekBarDialogPreference(Context context, AttributeSet attrs) {
59          this(context, attrs, R.attr.seekBarDialogPreferenceStyle);
60      }
61  
62      public SeekBarDialogPreference(Context context, AttributeSet attrs,
63              int defStyle) {
64          super(context, attrs, defStyle);
65          TypedArray a = context.obtainStyledAttributes(attrs,
66                  R.styleable.SeekBarDialogPreference, defStyle,
67                  R.style.Holo_PreferenceDialog_SeekBarDialogPreference);
68          int maxValue = a.getInt(R.styleable.SeekBarDialogPreference_max, 100);
69          a.recycle();
70          mSeekBar = onCreateSeekBar();
71          setMaxValue(maxValue);
72      }
73  
74      public int getMaxValue() {
75          return mMaxValue;
76      }
77  
78      public SeekBar getSeekBar() {
79          return mSeekBar;
80      }
81  
82      public int getValue() {
83          return mValue;
84      }
85  
86      @Override
87      protected void onBindDialogView(View view) {
88          super.onBindDialogView(view);
89          synchronized (mSeekBar) {
90              ViewParent oldParent = mSeekBar.getParent();
91              if (oldParent != view) {
92                  if (oldParent != null) {
93                      ((ViewGroup) oldParent).removeView(mSeekBar);
94                  }
95                  ((ViewGroup) view).addView(mSeekBar);
96              }
97          }
98      }
99  
100     protected SeekBar onCreateSeekBar() {
101         return (SeekBar) LayoutInflater.inflate(getContext(),
102                 R.layout.preference_dialog_seekbar_widget);
103     }
104 
105     @Override
106     protected void onDialogClosed(boolean positiveResult) {
107         super.onDialogClosed(positiveResult);
108         final int value;
109         synchronized (mSeekBar) {
110             value = mSeekBar.getProgress();
111         }
112         if (positiveResult && callChangeListener(value)) {
113             setValue(value);
114         }
115     }
116 
117     @Override
118     protected Integer onGetDefaultValue(TypedArray a, int index) {
119         return a.getInt(index, 0);
120     }
121 
122     @Override
123     protected void onRestoreInstanceState(Parcelable state) {
124         if (state == null || !(state instanceof SavedState)) {
125             super.onRestoreInstanceState(state);
126             return;
127         }
128         SavedState ss = (SavedState) state;
129         super.onRestoreInstanceState(ss.getSuperState());
130         setValue(ss.mValue);
131         setMaxValue(ss.mMaxValue);
132     }
133 
134     @Override
135     protected Parcelable onSaveInstanceState() {
136         final Parcelable superState = super.onSaveInstanceState();
137         if (isPersistent()) {
138             return superState;
139         }
140         final SavedState myState = new SavedState(superState);
141         myState.mValue = mValue;
142         myState.mMaxValue = mMaxValue;
143         return myState;
144     }
145 
146     @Override
147     protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
148         int def = defaultValue instanceof Integer ? (Integer) defaultValue
149                 : defaultValue == null ? 0 : Integer.parseInt(defaultValue
150                         .toString());
151         mSeekBar.setProgress(restoreValue ? getPersistedInt(def) : def);
152     }
153 
154     public void setMaxValue(int maxValue) {
155         if (mMaxValue == maxValue) {
156             return;
157         }
158         final boolean wasBlocking = shouldDisableDependents();
159         mMaxValue = maxValue;
160         mSeekBar.setMax(maxValue);
161         if (shouldDisableDependents() != wasBlocking) {
162             notifyDependencyChange(!wasBlocking);
163         }
164     }
165 
166     public void setValue(int value) {
167         if (mValue == value) {
168             return;
169         }
170         final boolean wasBlocking = shouldDisableDependents();
171         mValue = value;
172         mSeekBar.setProgress(value);
173         persistInt(value);
174         if (shouldDisableDependents() != wasBlocking) {
175             notifyDependencyChange(!wasBlocking);
176         }
177     }
178 }
<think>好的,我现在要解决用户遇到的依赖问题。用户提到Dependency 'androidx.savedstate:savedstate:1.2.1'需要依赖它的库和应用使用Android API版本33或更高版本来编译。首先,我需要确认这个错误的具体原因。 用户可能是在构建Android项目时遇到了编译错误,提示关于API版本的问题。首先,我应该回忆一下AndroidX库的发布情况。AndroidX库通常会随着新版本的API更新,而某些新版本的库可能会要求最低编译版本提升,以使用新的API功能或修复兼容性问题。 接下来,我需要检查androidx.savedstate:savedstate:1.2.1的官方文档或发布说明,看看是否确实要求API 33以上。不过根据经验,Android API 33对应的是Android 13,而某些库可能在某个版本后开始要求更高的编译SDK版本。假设这个版本确实需要API 33,那么用户需要调整项目的配置。 用户的问题可能在于他们的项目的compileSdkVersion或者targetSdkVersion设置过低,比如还停留在31或32。这时候,解决方案是建议用户将项目的编译SDK版本升级到33或更高。此外,可能需要检查其他相关依赖是否也要求更高的API版本,避免冲突。 另外,用户可能不知道如何修改编译版本,需要给出具体的步骤,比如在build.gradle文件中修改相应的配置。同时,需要注意升级后是否需要处理兼容性问题,例如新的API行为变化,但编译版本升级本身不会影响运行时行为,除非targetSdkVersion也被提升。 也有可能用户无法立即升级到API 33,这时候可能需要寻找较旧版本的androidx.savedstate库,比如查看是否有1.2.x版本中不要求API 33的版本。需要指导用户如何查找库的版本历史,或者建议他们使用之前的稳定版本,直到能够升级SDK。 此外,可能涉及到Gradle的依赖管理问题,比如是否强制使用了某个版本的库,导致冲突。这时候需要检查依赖树,使用命令如./gradlew app:dependencies来查看依赖关系,确认是否有其他库引入了更高版本的savedstate,从而引发问题。如果有,可能需要排除或调整版本。 最后,需要提醒用户在升级编译版本后,进行充分的测试,确保应用在新版本上的兼容性,特别是如果同时提升了targetSdkVersion,需要处理相关的行为变更。比如Android 13的权限变更、后台限制等,但这属于后续步骤,当前问题主要是解决编译错误。 总结解决方案步骤:1. 升级compileSdkVersion到33;2. 检查并处理其他依赖的兼容性;3. 如有必要,降低savedstate库版本;4. 测试应用确保稳定性。需要将这些步骤以清晰的方式呈现给用户,并提供具体的代码示例,如修改build.gradle文件的部分。</think>你遇到的错误提示说明项目中所依赖的 `androidx.savedstate:savedstate:1.2.1` 要求项目必须使用 **Android API 33(Android 13)或更高版本**进行编译。以下是逐步解决方案: --- ### **1. 原因分析** - **依赖库的最低编译要求**:该版本的 `androidx.savedstate` 库需要开发者使用 Android SDK 33 或更高版本来编译代码。 - **项目当前配置问题**:你的项目可能配置了较低的 `compileSdkVersion`(例如 32 或更早版本)。 --- ### **2. 解决方案** #### **方法一:升级项目的编译版本(推荐)** 修改项目的 `compileSdkVersion` 和 `targetSdkVersion` 至 **33** 或更高。 1. 打开 `build.gradle` (Module 级文件),找到以下配置项: ```groovy android { compileSdkVersion 31 // 当前可能为 31 或其他低版本 defaultConfig { targetSdkVersion 31 } } ``` 2. 将其升级为: ```groovy android { compileSdkVersion 33 // 至少改为 33 defaultConfig { targetSdkVersion 33 // 建议同步升级(可选但推荐) } } ``` 3. **同步项目**(Sync Project)并重新编译。 #### **方法二:降级依赖库版本(临时方案)** 如果暂时无法升级 SDK,可尝试使用兼容低版本 API 的 `androidx.savedstate`。 1. 在 `build.gradle` 中指定旧版本: ```groovy dependencies { implementation 'androidx.savedstate:savedstate:1.2.0' // 替换为兼容旧 API 的版本 } ``` 2. 通过 [Maven Central](https://mvnrepository.com/) 查找适配你当前 `compileSdkVersion` 的库版本。 --- ### **3. 验证依赖冲突** 如果项目中其他库间接依赖了高版本的 `androidx.savedstate`,需检查依赖树: 1. 运行命令查看依赖关系: ```bash ./gradlew :app:dependencies --configuration releaseRuntimeClasspath ``` 2. 若存在冲突,可强制指定版本: ```groovy configurations.all { resolutionStrategy.force 'androidx.savedstate:savedstate:1.2.0' } ``` --- ### **4. 其他注意事项** - **更新 Android Studio**:确保 IDE 支持 Android SDK 33。 - **测试兼容性**:升级 `targetSdkVersion` 后,需测试应用在新系统的行为(如权限、后台限制等)。 --- 通过以上步骤,可解决依赖库与编译版本的兼容性问题。若仍有问题,请提供完整的 `build.gradle` 文件以便进一步分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值