android - tutorial - mvxasynccommand
AplicaciĆ³n de Android MVVMCross Binding Crashes (2)
Tengo una aplicación para Android basada en Xamarin y MvvmCross. En esa aplicación hay una vista con una ExpandableListView que creé por mi cuenta. Ahora esta lista muestra varios elementos, que están vinculados a su DataContext utilizando MvvmCross. Sin embargo, como las vistas de ListItemViews individuales difieren mucho, parte de las ListItemViews se generan mediante programación en ExpandedListViewAdapter. Esto funciona así:
public override View GetChildView(int groupPosition, int childPosition, bool isLastChild, View convertView, ViewGroup parent)
{
object child = GetRawChild(groupPosition, childPosition);
if (child == null)
{
MvxBindingTrace.Trace(MvxTraceLevel.Error, "GetView called for group that seems to have no itemssource: it is null");
return null;
}
var view = (MvxListItemView)GetBindableView(convertView, child, ChildItemTemplateId);
var placeholder = view.FindViewById<BindableFrameLayout>(Resource.Id.placeholder);
var questionVm = (QuestionViewModel)child;
if(questionVm.ViewType == "TextBox")
{
placeholder.RemoveAllViews();
var text = new BindableEditText(context);
text.InputType = InputType;
text.SetRawInputType(InputType);
placeholder.RemoveAllViews();
placeholder.AddView(text);
var answer = questionVm.Children.First();
text.DataContext = answer;
var binding = text.CreateInlineBindingTarget<AnswerViewModel>();
text.Bind(binding, et => et.Text, vm => vm.Model.Value, (string)null, null, null,
MvxBindingMode.TwoWay);
}
else if(questionVm.ViewType == "Spinner")
{
placeholder.RemoveAllViews();
MvxSpinner spinner = new MvxSpinner(context, null);
spinner.ItemsSource = questionVm.Children;
spinner.ItemSelected += (sender, args) =>
{
for (int i = 0; i < questionVm.Children.Count; i++)
{
var answer = (IAnswerViewModel)questionVm.Children[i];
if (i == spinner.SelectedItemPosition)
answer.IsSelected = true;
else
answer.IsSelected = false;
}
};
spinner.Bind(bindings, ctrl => ctrl.ItemsSource, vm => vm.Children, (string)null, null, null, MvxBindingMode.OneWay);
var chosenAnswer = questionVm.Children.Cast<IAnswerViewModel>().FirstOrDefault(@a => @a.IsSelected == true);
if (chosenAnswer != null)
spinner.SetSelection(questionVm.Children.Cast<IAnswerViewModel>().ToList().IndexOf(chosenAnswer));
placeholder.AddView(spinner);
}
... y "BindableEditText" tiene el siguiente aspecto: using System; usando Android.Content; usando Android.Runtime; usando Android.Views; usando Android.Widget; usando Android.Util; utilizando Cirrious.MvvmCross.Binding.Droid.BindingContext; utilizando Cirrious.MvvmCross.Binding.BindingContext;
namespace iCL.Filler.Droid.Controls
{
public class BindableEditText : EditText, IMvxBindingContextOwner
{
private readonly IMvxAndroidBindingContext _bindingContext;
public BindableEditText(Context context)
: base(context)
{
_bindingContext = new MvxAndroidBindingContext(context, null);
}
public BindableEditText(Context context, IAttributeSet attributes)
: base(context, attributes)
{
_bindingContext = new MvxAndroidBindingContext(context, null);
}
public BindableEditText(Context context, IAttributeSet attributes, int defStyle)
: base(context, attributes, defStyle)
{
_bindingContext = new MvxAndroidBindingContext(context, null);
}
public BindableEditText(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
protected IMvxAndroidBindingContext AndroidBindingContext
{
get { return _bindingContext; }
}
public IMvxBindingContext BindingContext
{
get { return _bindingContext; }
set { throw new NotImplementedException("BindingContext is readonly in the radio button"); }
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
this.BindingContext.ClearAllBindings();
}
base.Dispose(disposing);
}
public override void SetText(Java.Lang.ICharSequence text, TextView.BufferType type)
{
try
{
base.SetText(text, type);
}
catch (Exception ex)
{
}
}
protected View Content { get; set; }
public object DataContext
{
get { return _bindingContext.DataContext; }
set { _bindingContext.DataContext = value; }
}
}
}
Así que mi problema es que de vez en cuando, cuando estoy desplazándome y haciendo clic en mi vista de lista, obtengo un error de tiempo de ejecución como el siguiente, y mi aplicación "falla", lo que significa que navega de regreso a la pantalla anterior.
10-29 14:04:37.140 D/dalvikvm( 5989): GC_EXPLICIT freed 751K, 11% free 11369K/12679K, paused 0ms+1ms
10-29 14:04:39.970 D/dalvikvm( 5989): GC_FOR_ALLOC freed 715K, 16% free 10692K/12679K, paused 5ms
10-29 14:04:41.831 E/mono-rt ( 5989): Stacktrace:
10-29 14:04:41.831 E/mono-rt ( 5989):
10-29 14:04:41.831 E/mono-rt ( 5989): at <unknown> <0xffffffff>
10-29 14:04:41.831 E/mono-rt ( 5989): at (wrapper managed-to-native) object.wrapper_native_0xb71f1820 (intptr,intptr,intptr,intptr,Android.Runtime.JValue[]) <IL 0x00124, 0xffffffff>
10-29 14:04:41.831 E/mono-rt ( 5989): at Android.Runtime.JNIEnv.CallNonvirtualVoidMethod (intptr,intptr,intptr,Android.Runtime.JValue[]) [0x00000] in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.8.2-branch/a25a31d0/source/monodroid/src/Mono.Android/src/Runtime/JNIEnv.g.cs:612
10-29 14:04:41.831 E/mono-rt ( 5989): at Android.Widget.CompoundButton.set_Checked (bool) [0x00070] in /Users/builder/data/lanes/monodroid-mlion-monodroid-4.8.2-branch/a25a31d0/source/monodroid/src/Mono.Android/platforms/android-14/src/generated/Android.Widget.CompoundButton.cs:255
10-29 14:04:41.831 E/mono-rt ( 5989): at (wrapper runtime-invoke) <Module>.runtime_invoke_void__this___byte (object,intptr,intptr,intptr) <IL 0x00054, 0xffffffff>
10-29 14:04:41.831 E/mono-rt ( 5989): at <unknown> <0xffffffff>
10-29 14:04:41.831 E/mono-rt ( 5989): at (wrapper managed-to-native) System.Reflection.MonoMethod.InternalInvoke (System.Reflection.MonoMethod,object,object[],System.Exception&) <IL 0x00030, 0xffffffff>
10-29 14:04:41.831 E/mono-rt ( 5989): at System.Reflection.MonoMethod.Invoke (object,System.Reflection.BindingFlags,System.Reflection.Binder,object[],System.Globalization.CultureInfo) <IL 0x0004a, 0x0016f>
10-29 14:04:41.831 E/mono-rt ( 5989): at System.Reflection.MethodBase.Invoke (object,object[]) <IL 0x00006, 0x00048>
10-29 14:04:41.831 E/mono-rt ( 5989): at Cirrious.MvvmCross.Binding.Bindings.Target.MvxPropertyInfoTargetBinding.SetValueImpl (object,object) <IL 0x0001a, 0x000a7>
10-29 14:04:41.831 E/mono-rt ( 5989): at Cirrious.MvvmCross.Binding.Bindings.Target.MvxConvertingTargetBinding.SetValue (object) <IL 0x0008c, 0x002b1>
The program ''Mono'' has exited with code 0 (0x0).
No sé cuál podría ser el problema ... ¿Podría ser que algún objeto de Java ya esté finalizado y mis enlaces intenten llamarlo?
Es un poco difícil seguir el fragmento de código provisto, pero supongo que el problema que está viendo se debe al hecho de que está reciclando la cell
y que cada vez que la recicla es:
- creando una nueva vista infantil de celda,
- creando un nuevo enlace,
- borrar la vista del viejo niño
- pero no eliminando el enlace anterior.
Una forma de resolver esto podría ser utilizar el método fluido WithClearBindingKey
para permitirle borrar estos enlaces.
Por ejemplo, si un enlace se crea como:
var set = this.CreateBindingSet<Cell, CellViewModel>();
set.Bind(text).To(vm => vm.TextValue).WithClearBindingKey("MyDynamicBindings");
set.Apply();
entonces solo los enlaces creados con esta etiqueta se pueden borrar usando:
this.ClearBindings("MyDynamicBindings");
Gracias Stuart, pero solo puse algo del código que escribí ya que mi listadapter se ha vuelto bastante complejo. Entonces, en realidad, ya estaba reciclando los controles que creé en el código. Sin embargo, parece que encontré la solución. Vi mi aplicación se bloquea últimamente y todos parecían ser porque algunas llamadas vinculantes el colocador de un widget de Android (EditText, Spinner, Checkbox, etc.) que resultó en un error nativo. Así que coloqué mensajes de rastreo en los métodos "Dispose" y "JavaFinalize" y, como resultado, el error siempre ocurría después de algunas llamadas "JavaFinalize". Entonces, lo que hice fue agregar el siguiente código a todos los controles, que implementé como algo "enlazable". (como BindableEditText en el fragmento de arriba)
protected override void JavaFinalize()
{
if (this.BindingContext != null)
this.BindingContext.ClearAllBindings();
base.JavaFinalize();
}
¡Así que eso hace totalmente el trabajo!
ADVERTENCIA: también tuve que agregar esto a "MvxListItemView". Entonces, ¿debería agregarse también a las fuentes de mvvmcross?