Bug 39821 - ViewExtension.TranslateTo cannot be invoked on Main thread(Android Marshmellow devices only).
Summary: ViewExtension.TranslateTo cannot be invoked on Main thread(Android Marshmello...
Status: RESOLVED FIXED
Alias: None
Product: Forms
Classification: Xamarin
Component: Forms (show other bugs)
Version: 2.1.0
Hardware: PC Mac OS
: --- normal
Target Milestone: ---
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2016-03-21 20:17 UTC by david.dai@zyamusic.com
Modified: 2016-04-22 02:38 UTC (History)
14 users (show)

Tags: ac
Is this bug a regression?: ---
Last known good build:


Attachments
RepoTranslateDoestREproduce (82.35 KB, application/zip)
2016-03-23 10:47 UTC, Rui Marinho
Details
Demo to reproduce the crash (76.68 KB, application/x-zip-compressed)
2016-03-23 16:15 UTC, Radek Bartoň
Details


Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED FIXED

Description david.dai@zyamusic.com 2016-03-21 20:17:06 UTC
I write the code blow to ensure TranslateTo task will be invoked on Main thread. However, on Android Marshmellow devices I still got crashed exception that says the TranslateTo task is not invoked on main thread.

Device.BeginInvokeOnMainThread(()=>{
    view.TranslateTo(100, 0.0, 150, Easing.CubicOut);							
});
Comment 1 Jason Smith [MSFT] 2016-03-21 20:36:55 UTC
can you please provide the exception info and stack trace?
Comment 2 david.dai@zyamusic.com 2016-03-21 20:49:51 UTC
Error: Animation operations must be invoked on the UI thread
  at Xamarin.Forms.AnimationExtensions.CheckAccess () [0x00007] in <filename unknown>:0 
  at Xamarin.Forms.AnimationExtensions.Animate[T] (IAnimatable self, System.String name, System.Func`2 transform, System.Action`1 callback, UInt32 rate, UInt32 length, Xamarin.Forms.Easing easing, System.Action`2 finished, System.Func`1 repeat) [0x00050] in <filename unknown>:0 
  at Xamarin.Forms.AnimationExtensions.Animate (IAnimatable self, System.String name, System.Action`1 callback, UInt32 rate, UInt32 length, Xamarin.Forms.Easing easing, System.Action`2 finished, System.Func`1 repeat) [0x00000] in <filename unknown>:0 
  at Xamarin.Forms.AnimationExtensions.Animate (IAnimatable self, System.String name, Xamarin.Forms.Animation animation, UInt32 rate, UInt32 length, Xamarin.Forms.Easing easing, System.Action`2 finished, System.Func`1 repeat) [0x00008] in <filename unknown>:0 
  at Xamarin.Forms.Animation.Commit (IAnimatable owner, System.String name, UInt32 rate, UInt32 length, Xamarin.Forms.Easing easing, System.Action`2 finished, System.Func`1 repeat) [0x00000] in <filename unknown>:0 
Error: Animation operations must be invoked on the UI thread
  at Xamarin.Forms.AnimationExtensions.CheckAccess () [0x00007] in <filename unknown>:0 
  at Xamarin.Forms.AnimationExtensions.Animate[T] (IAnimatable self, System.String name, System.Func`2 transform, System.Action`1 callback, UInt32 rate, UInt32 length, Xamarin.Forms.Easing easing, System.Action`2 finished, System.Func`1 repeat) [0x00050] in <filename unknown>:0 
  at Xamarin.Forms.AnimationExtensions.Animate (IAnimatable self, System.String name, System.Action`1 callback, UInt32 rate, UInt32 length, Xamarin.Forms.Easing easing, System.Action`2 finished, System.Func`1 repeat) [0x00000] in <filename unknown>:0 
  at Xamarin.Forms.AnimationExtensions.Animate (IAnimatable self, System.String name, Xamarin.Forms.Animation animation, UInt32 rate, UInt32 length, Xamarin.Forms.Easing easing, System.Action`2 finished, System.Func`1 repeat) [0x00008] in <filename unknown>:0 
  at Xamarin.Forms.Animation.Commit (IAnimatable owner, System.String name, UInt32 rate, UInt32 length, Xamarin.Forms.Easing easing, System.Action`2 finished, System.Func`1 repeat) [0x00000] in <filename unknown>:0 
  at Xamarin.Forms.ViewExtensions.TranslateTo (Xamarin.Forms.VisualElement view, Double x, Double y, UInt32 length, Xamarin.Forms.Easing easing) [0x000a5] in <filename unknown>:0 
  at Ditty.SwipeView+<ScrollTo>c__async0.MoveNext () [0x00174] in /Users/dtf51778/ditty/Ditty/Ditty/View/Swipe/SwipeView.cs:367 
  at Xamarin.Forms.ViewExtensions.TranslateTo (Xamarin.Forms.VisualElement view, Double x, Double y, UInt32 length, Xamarin.Forms.Easing easing) [0x000a5] in <filename unknown>:0 
  at Ditty.SwipeView+<ScrollTo>c__async0.MoveNext () [0x00174] in /Users/dtf51778/ditty/Ditty/Ditty/View/Swipe/SwipeView.cs:367 
Error: Animation operations must be invoked on the UI thread
  at Xamarin.Forms.AnimationExtensions.CheckAccess () [0x00007] in <filename unknown>:0 
  at Xamarin.Forms.AnimationExtensions.Animate[T] (IAnimatable self, System.String name, System.Func`2 transform, System.Action`1 callback, UInt32 rate, UInt32 length, Xamarin.Forms.Easing easing, System.Action`2 finished, System.Func`1 repeat) [0x00050] in <filename unknown>:0 
  at Xamarin.Forms.AnimationExtensions.Animate (IAnimatable self, System.String name, System.Action`1 callback, UInt32 rate, UInt32 length, Xamarin.Forms.Easing easing, System.Action`2 finished, System.Func`1 repeat) [0x00000] in <filename unknown>:0 
  at Xamarin.Forms.AnimationExtensions.Animate (IAnimatable self, System.String name, Xamarin.Forms.Animation animation, UInt32 rate, UInt32 length, Xamarin.Forms.Easing easing, System.Action`2 finished, System.Func`1 repeat) [0x00008] in <filename unknown>:0 
  at Xamarin.Forms.Animation.Commit (IAnimatable owner, System.String name, UInt32 rate, UInt32 length, Xamarin.Forms.Easing easing, System.Action`2 finished, System.Func`1 repeat) [0x00000] in <filename unknown>:0 
  at Xamarin.Forms.ViewExtensions.TranslateTo (Xamarin.Forms.VisualElement view, Double x, Double y, UInt32 length, Xamarin.Forms.Easing easing) [0x000a5] in <filename unknown>:0 
  at Ditty.SwipeView+<ScrollTo>c__async0.MoveNext () [0x00174] in /Users/dtf51778/ditty/Ditty/Ditty/View/Swipe/SwipeView.cs:367
Comment 3 Jason Smith [MSFT] 2016-03-21 20:54:06 UTC
Can you please provide SwipveView.cs or at least the context of line 367?
Comment 4 david.dai@zyamusic.com 2016-03-21 20:57:32 UTC
public async void ScrollTo(View view, bool animate = true)
		{
			if(HorizontalScrollLocked == true)
			{
				Debug.WriteLine("Swipe view can't scroll while locked");
			}

			List<Task> scrollList = new List<Task>();

			lock(scrollToLock)
			{
				Utils.ReportNotMainThread();

				try
				{
					if(fullWidthViews.Contains(view))
					{
						selectedIndex = fullWidthViews.IndexOf(view);

						System.Diagnostics.Debug.WriteLine("ScrollTo " + selectedIndex);

						//Move each view to the translation x
						int xMult = -selectedIndex;
						double roundedWidth = Math.Ceiling(Bounds.Width);

						//Assume that the swipe view should be full page width
						if(roundedWidth <= 0)
						{
							roundedWidth = DynamicDeviceInfo.screenWidthInPoints;
						}

						for(int i = 0; i < fullWidthViews.Count; i++, xMult++)
						{
							double transX = xMult * roundedWidth;

							if(animate == false)
							{
								fullWidthViews[i].TranslationX = transX;
							}
							else
							{
								Task task = fullWidthViews[i].TranslateTo(transX, 0.0, 150, Easing.CubicOut);
								scrollList.Add(task);
							}
						}

						associatedTabbedControl.SelectedItem = selectedIndex;
					}
				}
				catch(Exception ex)
				{
					Utils.Write(ex);
					Xamarin.Insights.Report(ex, SharedStrings.REASON, "Failed to swipe", Xamarin.Insights.Severity.Warning);
				}
			}

			await Task.WhenAll(scrollList);
		}

This is the original code. I tried to wrap the whole function or just the translation task into BeginInvokeOnMainThread(), but neither of them work.
Comment 5 Jason Smith [MSFT] 2016-03-21 21:01:58 UTC
Can you check what Forms.Context is set to right before you call TranslateTo? Im wondering if the wrong Context is what it is being marshalled to. In particular you are looking to see if Forms.Context is the current executing Context.
Comment 6 david.dai@zyamusic.com 2016-03-21 21:16:16 UTC
The context is right. It is MainActivity.
Comment 7 Jason Smith [MSFT] 2016-03-21 21:28:57 UTC
considering this is the method:

public void BeginInvokeOnMainThread(Action action)
{
	var activity = Context as Activity;
	if (activity != null)
		activity.RunOnUiThread(action);
}

I have to say Im a little stumped, maybe IsInvokeRequired is failing on marshmallow... Can you check the results of IsInvokeRequired? (sorry I will get around to doing that its just Im in the middle of something else right now)
Comment 8 david.dai@zyamusic.com 2016-03-21 21:45:54 UTC
Sorry, I'm familiar with IsInvokeRequired. I don't understand where is it or how to checkout it.
Comment 9 david.dai@zyamusic.com 2016-03-21 21:46:27 UTC
I mean Im not familiar.
Comment 10 Jason Smith [MSFT] 2016-03-21 21:50:48 UTC
Check the results of Device.IsInvokeRequired, is it true or false right before you invoke the problem code?
Comment 11 david.dai@zyamusic.com 2016-03-21 21:55:13 UTC
It is a internal boolean, not public. I cannot refer it in my code.
Comment 12 Jason Smith [MSFT] 2016-03-21 22:01:07 UTC
Oh right sorry... okay will look into it
Comment 13 david.dai@zyamusic.com 2016-03-21 23:41:56 UTC
With further investigation, I found out this exception only raised in Debug mode.
Comment 14 Rui Marinho 2016-03-23 10:46:51 UTC
Thank you for taking the time to submit the bug. We tried to reproduce the issue you reported but were unable given the description. If you could please attach a reproduction to the bug by starting with a clean Xamarin.Forms project and adding just the code necessary to demonstrate the issue, we would very much appreciate it.


I m attaching the reproduction case we used to try reproduce the problem but with success.

Warm regards,
Xamarin Forms Team
Comment 15 Rui Marinho 2016-03-23 10:47:31 UTC
Created attachment 15493 [details]
RepoTranslateDoestREproduce
Comment 16 Radek Bartoň 2016-03-23 11:01:09 UTC
Hello.

We are experiencing the same issue in our project using Xamarin.Forms 2.1.6526/6529 animations on Lenovo Yoga Tablet 2 1050F with Android 4.4.2. I'm 99.99% sure that all animation requests are performed on UI thread. The animation that is causing the crash is custom "progress" animation performed when new detail page is added/swapped to MasterDetailPage (which causes the page reload and shows progress). In other words the crash occurs only when animation is started while MasterDetailPage detail pages are swapped.

The animation looks like:

while (_animatedView != null && IsRunning) {
                await Task.WhenAll(new [] {
                    _animatedView.TranslateTo(+translationAnimationWidth, 0, FullAnimationLength / 4),
                    _animatedView.ScaleTo(0.5, FullAnimationLength / 4, Easing.Linear),
                    _animatedView.FadeTo(0.5, FullAnimationLength / 4, Easing.CubicOut),

                    _animatedView2.TranslateTo(-translationAnimationWidth, 0, FullAnimationLength / 4),
                    _animatedView2.ScaleTo(0.5, FullAnimationLength / 4, Easing.Linear),
                    _animatedView2.FadeTo(0.5, FullAnimationLength / 4, Easing.CubicOut)
                });

                await Task.WhenAll(new [] {
                    _animatedView.TranslateTo(0, 0, FullAnimationLength / 4),
                    _animatedView.ScaleTo(1, FullAnimationLength / 4, Easing.Linear),
                    _animatedView.FadeTo(1, FullAnimationLength / 4, Easing.CubicOut),

                    _animatedView2.TranslateTo(0, 0, FullAnimationLength / 4),
                    _animatedView2.ScaleTo(0.1, FullAnimationLength / 4, Easing.Linear),
                    _animatedView2.FadeTo(0, FullAnimationLength / 4, Easing.CubicOut),
                });

                _animatedView2.Text = NextAnimatedNumber(int.Parse(_animatedView.Text)).ToString();

                await Task.WhenAll(new [] {
                    _animatedView.TranslateTo(-translationAnimationWidth, 0, FullAnimationLength / 4),
                    _animatedView.ScaleTo(0.5, FullAnimationLength / 4, Easing.Linear),
                    _animatedView.FadeTo(0.5, FullAnimationLength / 4, Easing.CubicOut),

                    _animatedView2.TranslateTo(+translationAnimationWidth, 0, FullAnimationLength / 4),
                    _animatedView2.ScaleTo(0.5, FullAnimationLength / 4, Easing.Linear),
                    _animatedView2.FadeTo(0.5, FullAnimationLength / 4, Easing.CubicOut),
                });

                await Task.WhenAll(new [] {
                    _animatedView.TranslateTo(0, 0, FullAnimationLength / 4),
                    _animatedView.ScaleTo(0.1, FullAnimationLength / 4, Easing.Linear),
                    _animatedView.FadeTo(0, FullAnimationLength / 4, Easing.CubicOut),

                    _animatedView2.TranslateTo(0, 0, FullAnimationLength / 4),
                    _animatedView2.ScaleTo(1, FullAnimationLength / 4, Easing.Linear),
                    _animatedView2.FadeTo(1, FullAnimationLength / 4, Easing.CubicOut)
                });

                _animatedView.Text = NextAnimatedNumber(int.Parse(_animatedView.Text)).ToString();
            }

I'll try to reproduce the issue with the attached demo...
Comment 17 Radek Bartoň 2016-03-23 16:15:43 UTC
Created attachment 15499 [details]
Demo to reproduce the crash
Comment 18 Radek Bartoň 2016-03-23 16:18:46 UTC
I've attached a demo that can reproduce the "animation on background thread" crash on Lenovo Yoga Tablet 2. It happens only rarely when you massively touch "Animate" button for many times. The probability of crash raises when the application/device is on heavy load so if you run the demo with attached debugger the probability to reproduced the crash is higher.
Comment 19 Jason Smith [MSFT] 2016-03-23 19:39:20 UTC
Apologies this should not have been set to Needs Info. Rui was mistaken :)
Comment 20 Jason Smith [MSFT] 2016-03-26 22:58:49 UTC
Okay so I am having trouble getting access to a Lenovo Yoga 2, is it possible we could do a screenshare session and try to figure out what is going on here. The stacktraces here seem to indicate that the caller is awaiting but it started the await chan from a background thread.

If you are getting a different stack trace can you provide it?
Comment 21 aed 2016-03-30 04:10:50 UTC
Stepping in here as well as I have repros on our app store deployed app. The stack trace even shows runonuithread.

I have crashes from fadeto, translateto and layoutto. I'd say from the logs about 10% of cases result in a crash on Android. There are no crashes on iOS.

System.InvalidOperationExceptionAnimation operations must be invoked on the UI thread
Raw
  at Xamarin.Forms.AnimationExtensions.CheckAccess () [0x00007] in <filename unknown>:0 
  at Xamarin.Forms.AnimationExtensions.Animate[T] (IAnimatable self, System.String name, System.Func`2 transform, System.Action`1 callback, UInt32 rate, UInt32 length, Xamarin.Forms.Easing easing, System.Action`2 finished, System.Func`1 repeat) [0x00050] in <filename unknown>:0 
  at Xamarin.Forms.AnimationExtensions.Animate (IAnimatable self, System.String name, System.Action`1 callback, UInt32 rate, UInt32 length, Xamarin.Forms.Easing easing, System.Action`2 finished, System.Func`1 repeat) [0x00000] in <filename unknown>:0 
  at Xamarin.Forms.AnimationExtensions.Animate (IAnimatable self, System.String name, Xamarin.Forms.Animation animation, UInt32 rate, UInt32 length, Xamarin.Forms.Easing easing, System.Action`2 finished, System.Func`1 repeat) [0x00008] in <filename unknown>:0 
  at Xamarin.Forms.Animation.Commit (IAnimatable owner, System.String name, UInt32 rate, UInt32 length, Xamarin.Forms.Easing easing, System.Action`2 finished, System.Func`1 repeat) [0x00000] in <filename unknown>:0 
  at Xamarin.Forms.ViewExtensions.FadeTo (Xamarin.Forms.VisualElement view, Double opacity, UInt32 length, Xamarin.Forms.Easing easing) [0x0004f] in <filename unknown>:0 
  at myapp.LoginPage+<NextStep>c__async1+<NextStep>c__AnonStoreyA.<>m__6 () [0x0001d] in <filename unknown>:0 
  at Java.Lang.Thread+RunnableImplementor.Run () [0x0000b] in <filename unknown>:0 
  at Java.Lang.IRunnableInvoker.n_Run (IntPtr jnienv, IntPtr native__this) [0x00009] in <filename unknown>:0 
  at (wrapper dynamic-method) System.Object:2060071c-51a4-40d5-a512-c6367e5949fb (intptr,intptr)
--- End of stack trace from previous location where exception was thrown ---
  at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <filename unknown>:0 
  at Android.Runtime.JNIEnv.CallVoidMethod (IntPtr jobject, IntPtr jmethod, Android.Runtime.JValue* parms) [0x00069] in <filename unknown>:0 
  at Android.App.Activity.RunOnUiThread (IRunnable action) [0x0004a] in <filename unknown>:0 
  at Android.App.Activity.RunOnUiThread (System.Action action) [0x00007] in <filename unknown>:0 
  at Xamarin.Forms.Forms+AndroidPlatformServices.BeginInvokeOnMainThread (System.Action action) [0x00005] in <filename unknown>:0 
  at Xamarin.Forms.Device.BeginInvokeOnMainThread (System.Action action) [0x00005] in <filename unknown>:0 
  at myapp.LoginPage+<NextStep>c__async1.MoveNext () [0x001f6] in <filename unknown>:0
Comment 22 aed 2016-03-30 04:20:47 UTC
Also, three things:
1- It reproes on most devices (Nexus, Samsung, others)
2- I've seen crashes on both 4.4 and 5.1
3- It seems to have started in XF 2.1.xxxxx I have no reports of crashes before then.

@Jason: "The stacktraces here seem to indicate that the caller is awaiting but it started the await chan from a background thread."

This is what I had also concluded. It looks like the await (FadeTo) operation start it on the wrong thread/context.
Comment 23 Chase Florell 2016-04-06 18:00:38 UTC
@Jason I'm also seeing this with FadeTo and TranslateTo. What info do you need from us to get this going? This is a regression that is severely impacting the app.

AFAICT

 - It's Android only
 - It happens with FadeTo and TranslateTo
 - I fix it with silly try/catch

```
try
{
    // try/catch because XamForms 2.1.0
    await FabLocate.TranslateTo(-100, 0, easing: Easing.SpringOut);
}
catch
{
    FabLocate.TranslationX = -100;
}
```
Comment 24 Andy 2016-04-07 22:37:51 UTC
Happening here too.
Comment 25 Simon Davies 2016-04-12 11:05:00 UTC
Having the same issue here. What is the status of this? Does Xamarin still need solid repro steps on this one?

Seems much more likely to happen if there are lots of animations firing at the same time in my experience.
Comment 26 Pete Schmitz 2016-04-14 20:05:44 UTC
Our beta users are experiencing this as well. 

Our Insights shows 9 users with devices on Android 5.0 and above.

This error popped up right after we updated forms: 
Xamarin Forms 2.0.1.6505 -> 2.1.0.6529
Comment 27 Chase Florell 2016-04-14 20:09:29 UTC
I can consistently reproduce it on my dashboard page. Our dashboard has some parallax scrolling, and when the page is showing and I hit the app switcher button and then return to the app, my scrolling is locked due to the `catch` block.

        private void ScrollContainerOnScrolled(object sender, ScrolledEventArgs scrolledEventArgs)
        {
            var offset = ScrollContainer.ScrollY / 2;
            var halfImgHeight = Img.Height / 2;
            var scrollRegion = Grid.Height - ScrollContainer.Height;

            if (offset < halfImgHeight)
            {
                Img.TranslationY = -offset;
                Logo.TranslationY = -offset;
                LogoSimple.TranslationY = -offset;
                ImageBorder.TranslationY = -offset;
            }

            if (Math.Abs(offset) > 0)
            {
                if (Convert.ToInt32(scrollRegion) == 0 && offset > 0)
                {
                    //todo: fix this with Xamarin Forms fixes https://bugzilla.xamarin.com/show_bug.cgi?id=39821
                    try
                    {
                        LogoSimple.FadeTo(0);
                        Logo.FadeTo(1);
                    }
                    catch
                    {
                        Logo.Opacity = 1;
                        LogoSimple.Opacity = 0;
                    }
                }
                else
                {
                    var maxScrollForOpacity = halfImgHeight < scrollRegion ? halfImgHeight : scrollRegion;
                    var parallaxArea = maxScrollForOpacity - offset;
                    var opacityBase = parallaxArea < 0 ? 0.001 : parallaxArea;
                    var logoOpacity = offset / opacityBase;
                    var simpleLogOpacity = 1 - logoOpacity;
                    LogoSimple.Opacity = simpleLogOpacity;
                    Logo.Opacity = logoOpacity;
                }
            }
            else
            {
                //todo: fix this with Xamarin Forms fixes https://bugzilla.xamarin.com/show_bug.cgi?id=39821
                try
                {
                    LogoSimple.FadeTo(1);
                    Logo.FadeTo(0);
                }
                catch
                {
                    LogoSimple.Opacity = 1;
                    Logo.Opacity = 0;
                }
            }
        }

Side note, try/catch around ALL animations keeps the app from crashing.
Comment 28 Cody Beyer (MSFT) 2016-04-17 21:50:42 UTC
Confirming due to reproduction cases
Comment 29 thierry.van.tillo 2016-04-20 09:02:17 UTC
This issue is also present on Android 5.1 devices. Confirmed on at least 2 physical devices.

Both "TranslateTo" and "FadeTo" have about a 1 in 20 chance to cause an exception even when called from inside "Device.BeginInvokeOnMainThread" method.
Comment 30 E.Z. Hart [MSFT] 2016-04-22 02:38:54 UTC
Should be fixed in 2.2.0-pre4