Woj*_*lik 10 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.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)
Run Code Online (Sandbox Code Playgroud)
CompoundButton是一个基类,Switch我的主页上有两个开关.SwitchRenderer及其基类,我也没有看到任何状态保存代码.在Stack Overflow的许多问题中,声称问题可能是由重复引起的android:id,但正如我上面提到的,我没有自定义布局.
更新
我决定深入调查,开始验证整个州的保全机制.以下是我的发现:
(viewId, state).事实证明,所有视图都将状态保留为AbsSavedState仅CompoundButton存储CompoundButton.SavedState.因此,我的猜测是,某种程度上错误的状态用于恢复CompoundButton.样品状态:{Bundle[{ android:viewHierarchyState=Bundle[{android:views=
{1=android.view.AbsSavedState$1@e738983,2=android.view.AbsSavedState$1@e738983,
3=android.view.AbsSavedState$1@e738983, 4=android.view.AbsSavedState$1@e738983,
5=android.view.AbsSavedState$1@e738983, 6=android.view.AbsSavedState$1@e738983,
7=android.view.AbsSavedState$1@e738983, 8=android.view.AbsSavedState$1@e738983,
9=android.view.AbsSavedState$1@e738983, 10=android.view.AbsSavedState$1@e738983,
11=android.view.AbsSavedState$1@e738983, 12=android.view.AbsSavedState$1@e738983,
13=android.view.AbsSavedState$1@e738983, 14=android.view.AbsSavedState$1@e738983,
15=android.view.AbsSavedState$1@e738983, 16=android.view.AbsSavedState$1@e738983,
17=android.view.AbsSavedState$1@e738983, 18=android.view.AbsSavedState$1@e738983,
19=android.view.AbsSavedState$1@e738983, 20=android.view.AbsSavedState$1@e738983,
21=android.view.AbsSavedState$1@e738983, 22=android.view.AbsSavedState$1@e738983,
23=android.view.AbsSavedState$1@e738983, 24=CompoundButton.SavedState{26e683d checked=false},
25=android.view.AbsSavedState$1@e738983, 26=CompoundButton.SavedState{8f32832 checked=true},
27=android.view.AbsSavedState$1@e738983, 28=android.view.AbsSavedState$1@e738983,
29=android.view.AbsSavedState$1@e738983, 30=android.view.AbsSavedState$1@e738983,
31=android.view.AbsSavedState$1@e738983, 32=android.view.AbsSavedState$1@e738983,
33=android.view.AbsSavedState$1@e738983, 34=android.view.AbsSavedState$1@e738983,
35=android.view.AbsSavedState$1@e738983, 36=android.view.AbsSavedState$1@e738983,
37=android.view.AbsSavedState$1@e738983,
16908290=android.view.AbsSavedState$1@e738983,
2131558525=android.view.AbsSavedState$1@e738983,
2131558526=android.view.AbsSavedState$1@e738983}}],
android:lastAutofillId=1073741825,
android:fragments=android.app.FragmentManagerState@969a700}]}
CompoundButtons基类Switch:MainPage和模态页面.毕竟我认为这可能是这种可能的不匹配,而恢复状态是由重复的ID以某种方式引起的.我决定写一段代码用ids打印整个层次结构.下面你可以看到MainPage和模态页面,总共3个开关.但是,这里没有重复.-- 16908290 - ContentFrameLayout ---- -1 - RelativeLayout ------ -1 - PlatformRenderer -------- 1 - PageRenderer ---------- -1 - DefaultRenderer ------------ -1 - DefaultRenderer -------------- 2 - ImageRenderer ------------ -1 - CustomScrollViewRenderer -------------- -1 - ScrollViewContainer ---------------- -1 - DefaultRenderer ------------------ -1 - DefaultRenderer -------------------- -1 - DefaultRenderer ---------------------- -1 - DefaultRenderer ------------------------ 3 - ImageRenderer ---------------------- 4 - LabelRenderer ---------------------- 5 - LabelRenderer ---------------------- -1 - DefaultRenderer ------------------------ 6 - ImageRenderer ------------------ -1 - DefaultRenderer -------------------- -1 - DefaultRenderer ---------------------- 7 - LabelRenderer ---------------------- 8 - LabelRenderer ---------------------- -1 - DefaultRenderer ------------------------ 9 - ImageRenderer ------------------ -1 - DefaultRenderer -------------------- -1 - DefaultRenderer ---------------------- -1 - DefaultRenderer ------------------------ -1 - GaugeChartRenderer ------------------------ 10 - LabelRenderer ------------------------ 11 - LabelRenderer ------------------------ -1 - GaugeChartRenderer ------------------------ 12 - LabelRenderer ------------------------ 13 - LabelRenderer ------------------ -1 - DefaultRenderer -------------------- 14 - LabelRenderer -------------------- 15 - LabelRenderer ------------------ -1 - LinearChartRenderer -------------------- 16 - LinearChart ------------------ -1 - DefaultRenderer -------------------- -1 - CustomButtonRenderer ---------------------- 17 - Button -------------------- -1 - CustomButtonRenderer ---------------------- 18 - Button -------------------- -1 - CustomButtonRenderer ---------------------- 19 - Button -------------------- -1 - CustomButtonRenderer ---------------------- 20 - Button -------------------- -1 - CustomButtonRenderer ---------------------- 21 - Button -------------------- -1 - CustomButtonRenderer ---------------------- 22 - Button ------------------ -1 - DefaultRenderer ------------------ -1 - DefaultRenderer -------------------- -1 - DefaultRenderer ---------------------- 23 - LabelRenderer ---------------------- 24 - LabelRenderer ---------------------- 25 - LabelRenderer ---------------------- 26 - LabelRenderer ---------------------- 27 - LabelRenderer -------------------- -1 - DefaultRenderer ---------------------- -1 - DefaultRenderer ------------------------ -1 - DefaultRenderer -------------------------- 33 - LabelRenderer -------------------------- 34 - LabelRenderer -------------------------- 35 - LabelRenderer ------------------ -1 - DefaultRenderer -------------------- -1 - CustomSwitchRenderer ---------------------- 28 - Switch -------------------- 29 - LabelRenderer -------------------- -1 - DefaultRenderer ---------------------- 36 - ImageRenderer ------------------ -1 - DefaultRenderer -------------------- -1 - CustomSwitchRenderer ---------------------- 30 - Switch -------------------- 31 - LabelRenderer ------------------ -1 - DefaultRenderer -------------------- 37 - ImageRenderer -------------------- -1 - CustomButtonRenderer ---------------------- 32 - Button -------- 44 - ModalContainer ---------- -1 - View ---------- 38 - PageRenderer ------------ -1 - DefaultRenderer -------------- -1 - DefaultRenderer ---------------- -1 - DefaultRenderer ------------------ 39 - LabelRenderer ------------------ -1 - DefaultRenderer -------------------- 45 - ImageRenderer ---------------- -1 - SearchBarRenderer ------------------ 40 - SearchView -------------------- 16909226 - LinearLayout ---------------------- 16909225 - AppCompatTextView ---------------------- 16909227 - AppCompatImageView ---------------------- 16909229 - LinearLayout ------------------------ 16909231 - AppCompatImageView ------------------------ 16909232 - LinearLayout -------------------------- 16909233 - AutoCompleteTextView -------------------------- 16909228 - AppCompatImageView ------------------------ 16909321 - LinearLayout -------------------------- 16909230 - AppCompatImageView -------------------------- 16909235 - AppCompatImageView -------------- -1 - DefaultRenderer ---------------- -1 - ListViewRenderer ------------------ -1 - SwipeRefreshLayout -------------------- 41 - ListView ---------------------- -1 - Container ---------------------- -1 - Container ------------------------ -1 - DefaultRenderer -------------------- -1 - ImageView -------------- -1 - DefaultRenderer ---------------- -1 - DefaultRenderer ------------------ -1 - CustomSwitchRenderer -------------------- 42 - Switch ------------------ 43 - LabelRenderer
internal static int GenerateViewId()
{
if ((int)Build.VERSION.SdkInt >= 17)
return global::Android.Views.View.GenerateViewId();
if (s_id >= 0x00ffffff)
s_id = 0x00000400;
return s_id++;
}
static int s_id = 0x00000400;
它看起来很好,除非有一些竞争条件.我的想法已经不多了.
更新2
我将Switch控制和覆盖OnRestoreSavedInstance以及奇怪的东西分类,它从未在我的设备上调用过.然而,OnSaveInstanceState被称为.请注意,我正确地模拟了状态恢复(它被调用MainActivity,但不会传播到Switch).
我找到了它以这种方式表现的原因.请看一下Android的实现View.dispatchRestoreState:
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container)
{
if (mID != NO_ID) {
Parcelable state = container.get(mID); // <--- HERE
if (state != null) {
// Log.i("View", "Restoreing #" + Integer.toHexString(mID)
// + ": " + state);
mPrivateFlags &= ~SAVE_STATE_CALLED;
onRestoreInstanceState(state);
if ((mPrivateFlags & SAVE_STATE_CALLED) == 0) {
throw new IllegalStateException(
"Derived class did not call super.onRestoreInstanceState()");
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
Xamarin.Forms通过增加计数器自动设置ID.因此,在创建页面后,它会将ID设置1为n.在另一次娱乐之后(例如在旋转屏幕之后),它将ID设置n+1为2n+1.因此,任何控件都无法恢复其状态,因为在保留状态时它将保存为状态id=x,但是在重新创建后,Activity此控件将具有不同的ID.
因此,不应该发生这种崩溃,因为没有状态恢复......
更新3
我注意到Android的实现中也有一些奇怪的东西.CompoundButton有这个实现:
@Override
public void onRestoreInstanceState(Parcelable state) {
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
setChecked(ss.checked);
requestLayout();
}
Run Code Online (Sandbox Code Playgroud)
然而,TextView(CompoundButton's祖先)有这个实现:
@Override
public void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
// ...
}
Run Code Online (Sandbox Code Playgroud)
如您所见,TextView首先验证此演员表是否成功,CompoundButton不是.也许这是Android的缺陷.但是我仍然没有看到状态如何被错配并且AbsSavedState已经被传递给CompoundButton了CompoundButton.SavedState.
毕竟,看起来必须有重复的 id 处于保留状态,但是我没有看到任何合理的解释。我也无法在我的设备上重现它。正如我上面所描述的:
Xamarin.Forms 通过增加计数器自动设置 ids。因此,在创建页面后,它将 id 设置
1为n。在另一次游戏后(例如旋转屏幕后),它将 id 设置n+1为2n+1。因此,没有控件能够恢复其状态,因为在保留状态时,它将被保存为 id=x 的状态,但是在重新创建 Activity 后,该控件将具有不同的 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;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
它禁用所有控件的状态保存Switch。以防万一我还设置Id = -1覆盖 Xamarin 分配的 id。-1是 Android 中的常量,表示“无 id”。
此解决方法不会破坏 中的状态保存Xamarin.Forms,因为Page重新创建后的状态依赖于您的绑定,而不是 Android 的机制。
但是,如果您想让它在不禁用状态保存的情况下工作。您可以设置一些大 ID,该 ID 在运行之间保持不变。当然,您需要为每个设置不同的 ID Switch,因此您可能需要创建一个自定义Switch并添加一些属性,例如AndroidId. 请注意,id 应小于0x00ffffff且足够大,以避免与 Xamarin 自动生成的 id 发生冲突。
| 归档时间: |
|
| 查看次数: |
596 次 |
| 最近记录: |