还原Android状态时崩溃-无法强制转换AbsSavedState
我收到Crashlytics关于我的Xamarin.Forms项目中以下崩溃的通知:还原Android状态时崩溃-无法强制转换AbsSavedState,android,xamarin,xamarin.forms,Android,Xamarin,Xamarin.forms,我收到Crashlytics关于我的Xamarin.Forms项目中以下崩溃的通知: Fatal Exception: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.xxx.xxx/xxxxx.MainActivity}: java.lang.ClassCastException: android.view.AbsSavedState$1 cannot be cast to android.wi
Fatal Exception: java.lang.RuntimeException: Unable to start activity
ComponentInfo{com.xxx.xxx/xxxxx.MainActivity}:
java.lang.ClassCastException: android.view.AbsSavedState$1 cannot be cast to
android.widget.CompoundButton$SavedState
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2957)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3032)
at android.app.ActivityThread.-wrap11(Unknown Source)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1696)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6944)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
Caused by java.lang.ClassCastException:
android.view.AbsSavedState$1 cannot be cast to android.widget.CompoundButton$SavedState
at android.widget.CompoundButton.onRestoreInstanceState(CompoundButton.java:619)
at android.view.View.dispatchRestoreInstanceState(View.java:18884)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3936)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3936)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3936)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3936)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3936)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3936)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3936)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3936)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3936)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3936)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3936)
at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3936)
at android.view.View.restoreHierarchyState(View.java:18862)
at com.android.internal.policy.PhoneWindow.restoreHierarchyState(PhoneWindow.java:2248)
at android.app.Activity.onRestoreInstanceState(Activity.java:1153)
at android.app.Activity.performRestoreInstanceState(Activity.java:1108)
at android.app.Instrumentation.callActivityOnRestoreInstanceState(Instrumentation.java:1266)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2930)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3032)
at android.app.ActivityThread.-wrap11(Unknown Source)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1696)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6944)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
- 不幸的是,我无法复制它
- 我检查了
是否是CompoundButton
的基类,我的主页上有两个开关Switch
- 我只有一项主要活动
- 我在Xamarin.Android中使用Xamarin.Forms,没有任何自定义布局
- 我没有任何关于国家保护/恢复的习惯行动
- 我检查了Xamarin.Forms源代码中的
及其基类,也没有看到任何状态保存代码SwitchRenderer
android:id
引起的,但是正如我上面提到的,我没有自定义布局
更新 我决定深入调查,并开始验证整个国家保护机制。以下是我的调查结果:
(viewId,state)
。此外,所有视图都将状态保留为AbsSavedState
仅CompoundButton
存储CompoundButton.SavedState
。因此,我的猜测是,在还原CompoundButton
时使用了不正确的状态。样本状态:1
设置为n
。在另一次重新创建之后(例如在旋转屏幕之后),它将ID从n+1
设置为2n+1
。因此,没有一个控件能够恢复其状态,因为在保存状态时,它将被保存为id=x
的状态,但是在重新创建活动之后,此控件将具有不同的id
因此,此崩溃不应发生,因为没有状态恢复
更新3
我还注意到Android的实现中有一些奇怪的东西CompoundButton
具有以下实现:
@Override
public void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
setChecked(ss.checked);
requestLayout();
}
@Override
public void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
// ...
}
但是,TextView
(CompoundButton
的祖先)有以下实现:
@Override
public void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
setChecked(ss.checked);
requestLayout();
}
@Override
public void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
// ...
}
如您所见,TextView
首先验证此强制转换是否成功,CompoundButton
不成功。也许这是安卓系统的一个缺陷。但是我仍然不明白状态不匹配的可能性,并且AbsSavedState
被传递到CompoundButton
而不是CompoundButton。SavedState
这并不能解决您的整体问题,但我相信我可以对您的更新3部分有所帮助
首先,让我重申您的问题:为什么TextView
和CompoundButton
有两种不同的策略来实现onRestoreInstanceState()
TextView根据传递给它的特定包裹执行条件逻辑:
而CompoundButton不:
原因是TextView
和CompoundButton
有两种不同的策略来实现onSaveInstanceState()
,因此每个类都有相应的策略来恢复状态
TextView可以从onSaveInstanceState()返回两种不同的类型:
在super
调用没有保存所需的所有内容的情况下(例如,当要求TextView冻结其文本或有选择时),TextView将仅返回其自己的自定义SavedState
类。在所有其他情况下,它只是委托给super
调用并直接返回该调用
由于onRestoreInstanceState()
将接收返回的任何onSaveInstanceState()
内容,因此当TextView接收到super
返回值或其自身的SavedState
时,它需要能够工作
另一方面,CompoundButton只能从onSaveInstanceState()
返回一种类型:
因为我们知道传入的状态
对象总是类型为SavedState
,所以我们不必执行任何条件逻辑。我们可以把它扔了就走
希望这个答案能为其他回答者提供一个基础,也许最终会回答你的首要问题。 毕竟,在保存状态中看起来必须有重复ID,但是我看不出有什么合理的解释。我都不能在我的设备上复制它。如上所述:
Forms通过增加计数器自动设置ID。因此,在创建页面后,它将ID从1
设置为n
。在另一次重新创建之后(例如在旋转屏幕之后),它将ID从n+1
设置为2n+1
。因此,没有控件能够恢复其状态,因为在保留状态时,它将保存为id=x的状态,但是在重新创建活动后,此控件将具有不同的id
尽管如此,我还是找到了一个解决办法来阻止撞车
using Android.Content;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(Switch), typeof(MyApp.Droid.CustomRenderers.CustomSwitchRenderer))]
namespace MyApp.Droid.CustomRenderers
{
public class CustomSwitchRenderer : SwitchRenderer
{
public CustomSwitchRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Switch> e)
{
base.OnElementChanged(e);
if (this.Control != null)
{
this.Control.Id = -1;
this.Control.SaveEnabled = false;
}
}
}
}
使用Android.Content;
使用Xamarin.Forms;
使用Xamarin.Forms.Platform.Android;
[程序集:ExportRenderer(typeof(Switch)、typeof(MyApp.Droid.CustomRenderers.CustomSwitchRenderer))]
命名空间MyApp.Droid.CustomRenders
{
公共类CustomSwitchRenderer:SwitchRenderer
{
公共CustomSwitchRenderer(上下文):基础(上下文)
{
}
受保护的覆盖无效OnElementChanged(ElementChangedEventArgs e)
{
基础。一个要素发生变化(e);
if(this.Control!=null)
{
this.Control.Id=-
@Override
public void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
// ...
}
@Override
public void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
...
}
@Override
public void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
...
}
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
...
if (freezesText || hasSelection) {
SavedState ss = new SavedState(superState);
...
return ss;
}
return superState;
}
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.checked = isChecked();
return ss;
}
using Android.Content;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(Switch), typeof(MyApp.Droid.CustomRenderers.CustomSwitchRenderer))]
namespace MyApp.Droid.CustomRenderers
{
public class CustomSwitchRenderer : SwitchRenderer
{
public CustomSwitchRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Switch> e)
{
base.OnElementChanged(e);
if (this.Control != null)
{
this.Control.Id = -1;
this.Control.SaveEnabled = false;
}
}
}
}