From 1f3f0757535adf896d1fa10b7fd969df0387da20 Mon Sep 17 00:00:00 2001 From: nmilcoff Date: Fri, 30 Mar 2018 21:51:53 -0300 Subject: [PATCH 1/7] MvxSetupSingleton: Use tcs instead of bool value and throw exception if cancelling monitor with wrong value --- MvvmCross/Core/MvxSetupSingleton.cs | 58 ++++++++++++----------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/MvvmCross/Core/MvxSetupSingleton.cs b/MvvmCross/Core/MvxSetupSingleton.cs index 64b1aa7d0..a41111d95 100644 --- a/MvvmCross/Core/MvxSetupSingleton.cs +++ b/MvvmCross/Core/MvxSetupSingleton.cs @@ -16,18 +16,12 @@ namespace MvvmCross.Core private static readonly object LockObject = new object(); private static TaskCompletionSource IsInitialisedTaskCompletionSource; private IMvxSetup _setup; - private bool _initialized; private IMvxSetupMonitor _currentMonitor; - protected virtual IMvxSetup Setup - { - get - { - return _setup; - } - } + protected virtual IMvxSetup Setup => _setup; - public virtual TMvxSetup PlatformSetup() where TMvxSetup : IMvxSetup + public virtual TMvxSetup PlatformSetup() + where TMvxSetup : IMvxSetup { try { @@ -61,19 +55,18 @@ namespace MvvmCross.Core { lock (LockObject) { - if (_initialized) - return; - if (IsInitialisedTaskCompletionSource == null) { - IsInitialisedTaskCompletionSource = StartSetupInitialization(); + StartSetupInitialization(); } else { + if (IsInitialisedTaskCompletionSource.Task.IsCompleted) + return; + MvxLog.Instance.Trace("EnsureInitialized has already been called so now waiting for completion"); } } - IsInitialisedTaskCompletionSource.Task.GetAwaiter().GetResult(); } @@ -82,18 +75,21 @@ namespace MvvmCross.Core lock (LockObject) { _currentMonitor = setupMonitor; - if (_initialized) - { - _currentMonitor?.InitializationComplete(); - return; - } - + + // if the tcs is not null, it means the initialization is running if (IsInitialisedTaskCompletionSource != null) { + // If the task is already completed at this point, let the monitor know it has finished. + // but don't do it otherwise because it's done elsewhere + if(IsInitialisedTaskCompletionSource.Task.IsCompleted) + { + _currentMonitor?.InitializationComplete(); + } + return; } - - IsInitialisedTaskCompletionSource = StartSetupInitialization(); + + StartSetupInitialization(); } } @@ -101,17 +97,14 @@ namespace MvvmCross.Core { lock (LockObject) { - if (setupMonitor == _currentMonitor) + if (setupMonitor != _currentMonitor) { - _currentMonitor = null; + throw new MvxException("The specified IMvxSetupMonitor is not the one registered in MvxSetupSingleton"); } + _currentMonitor = null; } } - protected MvxSetupSingleton() - { - } - protected virtual void CreateSetup() { try @@ -136,17 +129,16 @@ namespace MvvmCross.Core base.Dispose(isDisposing); } - private TaskCompletionSource StartSetupInitialization() + private void StartSetupInitialization() { - var completionSource = new TaskCompletionSource(); + IsInitialisedTaskCompletionSource = new TaskCompletionSource(); _setup.InitializePrimary(); Task.Run(() => { _setup.InitializeSecondary(); lock (LockObject) { - completionSource.SetResult(true); - _initialized = true; + IsInitialisedTaskCompletionSource.SetResult(true); var dispatcher = Mvx.GetSingleton(); dispatcher.RequestMainThreadAction(() => { @@ -157,8 +149,6 @@ namespace MvvmCross.Core }); } }); - - return completionSource; } } } From 9b8b5fcf35d74ac07c4b4ecff79fa29504efe9db Mon Sep 17 00:00:00 2001 From: nmilcoff Date: Sat, 31 Mar 2018 00:39:27 -0300 Subject: [PATCH 2/7] Android Activities: Avoid repeated calls to setup singleton and run start only when not monitoring startup --- .../V7.AppCompat/MvxAppCompatActivity.cs | 14 +++++++------- MvvmCross/Platforms/Android/Views/MvxActivity.cs | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/MvvmCross.Android.Support/V7.AppCompat/MvxAppCompatActivity.cs b/MvvmCross.Android.Support/V7.AppCompat/MvxAppCompatActivity.cs index baec5634c..efffc504b 100644 --- a/MvvmCross.Android.Support/V7.AppCompat/MvxAppCompatActivity.cs +++ b/MvvmCross.Android.Support/V7.AppCompat/MvxAppCompatActivity.cs @@ -13,7 +13,6 @@ using MvvmCross.Core; using MvvmCross.Droid.Support.V7.AppCompat.EventSource; using MvvmCross.Platforms.Android.Binding.BindingContext; using MvvmCross.Platforms.Android.Binding.Views; -using MvvmCross.Platforms.Android.Core; using MvvmCross.Platforms.Android.Views; using MvvmCross.ViewModels; @@ -85,15 +84,16 @@ namespace MvvmCross.Droid.Support.V7.AppCompat } protected override void OnCreate(Bundle bundle) - { - var setup = MvxAndroidSetupSingleton.EnsureSingletonAvailable(ApplicationContext); - setup.EnsureInitialized(); - + { base.OnCreate(bundle); ViewModel?.ViewCreated(); - RunAppStart(bundle); - } + if(!(this is IMvxSetupMonitor)) + { + RunAppStart(bundle); + } + } + protected virtual void RunAppStart(Bundle bundle) { var startup = Mvx.Resolve(); diff --git a/MvvmCross/Platforms/Android/Views/MvxActivity.cs b/MvvmCross/Platforms/Android/Views/MvxActivity.cs index 176d63912..98a69e21a 100644 --- a/MvvmCross/Platforms/Android/Views/MvxActivity.cs +++ b/MvvmCross/Platforms/Android/Views/MvxActivity.cs @@ -15,7 +15,6 @@ using MvvmCross.Platforms.Android.Binding.BindingContext; using MvvmCross.Platforms.Android.Binding.Views; using MvvmCross.ViewModels; using MvvmCross.Core; -using MvvmCross.Platforms.Android.Core; namespace MvvmCross.Platforms.Android.Views { @@ -113,14 +112,15 @@ namespace MvvmCross.Platforms.Android.Views protected override void OnCreate(Bundle bundle) { - var setup = MvxAndroidSetupSingleton.EnsureSingletonAvailable(ApplicationContext); - setup.EnsureInitialized(); - base.OnCreate(bundle); ViewModel?.ViewCreated(); - RunAppStart(bundle); - } + if (!(this is IMvxSetupMonitor)) + { + RunAppStart(bundle); + } + } + protected virtual void RunAppStart(Bundle bundle) { var startup = Mvx.Resolve(); From 8e9d2b29bc14e553c8f20ac615f8bc9f8f17391a Mon Sep 17 00:00:00 2001 From: nmilcoff Date: Sat, 31 Mar 2018 00:40:29 -0300 Subject: [PATCH 3/7] Improve comments about setup on Android Activity classes --- .../Platforms/Android/Views/MvxFormsAppCompatActivity.cs | 2 ++ .../Platforms/Android/Views/MvxFormsApplicationActivity.cs | 3 ++- MvvmCross/Platforms/Android/Views/MvxActivityViewExtensions.cs | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/MvvmCross.Forms/Platforms/Android/Views/MvxFormsAppCompatActivity.cs b/MvvmCross.Forms/Platforms/Android/Views/MvxFormsAppCompatActivity.cs index 8c7eea80a..44b53ec1a 100644 --- a/MvvmCross.Forms/Platforms/Android/Views/MvxFormsAppCompatActivity.cs +++ b/MvvmCross.Forms/Platforms/Android/Views/MvxFormsAppCompatActivity.cs @@ -97,6 +97,8 @@ namespace MvvmCross.Forms.Platforms.Android.Views protected override void OnCreate(Bundle bundle) { + // ensuring mvvmcross is running here is required + // otherwise app will crash when inflating the view because of the Forms base class var setup = MvxAndroidSetupSingleton.EnsureSingletonAvailable(ApplicationContext); setup.EnsureInitialized(); diff --git a/MvvmCross.Forms/Platforms/Android/Views/MvxFormsApplicationActivity.cs b/MvvmCross.Forms/Platforms/Android/Views/MvxFormsApplicationActivity.cs index d0c1992b7..b33283f49 100644 --- a/MvvmCross.Forms/Platforms/Android/Views/MvxFormsApplicationActivity.cs +++ b/MvvmCross.Forms/Platforms/Android/Views/MvxFormsApplicationActivity.cs @@ -95,7 +95,8 @@ namespace MvvmCross.Forms.Platforms.Android.Views protected override void OnCreate(Bundle bundle) { - // Required for proper Push notifications handling + // ensuring mvvmcross is running here is required + // otherwise app will crash when inflating the view because of the Forms base class var setup = MvxAndroidSetupSingleton.EnsureSingletonAvailable(ApplicationContext); setup.EnsureInitialized(); diff --git a/MvvmCross/Platforms/Android/Views/MvxActivityViewExtensions.cs b/MvvmCross/Platforms/Android/Views/MvxActivityViewExtensions.cs index 6ca0343b4..5aaee125f 100644 --- a/MvvmCross/Platforms/Android/Views/MvxActivityViewExtensions.cs +++ b/MvvmCross/Platforms/Android/Views/MvxActivityViewExtensions.cs @@ -139,7 +139,7 @@ namespace MvvmCross.Platforms.Android.Views { if (androidView is IMvxSetupMonitor) { - // splash screen views manage their own setup initialization + // setup monitor views manage their own setup initialization return; } From c027ff1271c95f53e545a74075805dd9788e5eaa Mon Sep 17 00:00:00 2001 From: nmilcoff Date: Sat, 31 Mar 2018 00:41:12 -0300 Subject: [PATCH 4/7] Android SplashScreens: Remove TriggerFirstNavigate and use RunAppStart instead --- .../MvxSplashScreenAppCompatActivity.cs | 14 +++++--------- .../Android/Views/MvxSplashScreenActivity.cs | 13 +++++-------- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/MvvmCross.Android.Support/V7.AppCompat/MvxSplashScreenAppCompatActivity.cs b/MvvmCross.Android.Support/V7.AppCompat/MvxSplashScreenAppCompatActivity.cs index c6d6ac44e..83ab05e67 100644 --- a/MvvmCross.Android.Support/V7.AppCompat/MvxSplashScreenAppCompatActivity.cs +++ b/MvvmCross.Android.Support/V7.AppCompat/MvxSplashScreenAppCompatActivity.cs @@ -7,7 +7,6 @@ using Android.Runtime; using Android.Views; using MvvmCross.Core; using MvvmCross.Platforms.Android.Core; -using MvvmCross.Platforms.Android.Views; using MvvmCross.ViewModels; namespace MvvmCross.Droid.Support.V7.AppCompat @@ -20,6 +19,8 @@ namespace MvvmCross.Droid.Support.V7.AppCompat private readonly int _resourceId; + private Bundle _bundle; + public new MvxNullViewModel ViewModel { get { return base.ViewModel as MvxNullViewModel; } @@ -40,6 +41,8 @@ namespace MvvmCross.Droid.Support.V7.AppCompat { RequestWindowFeatures(); + _bundle = bundle; + var setup = MvxAndroidSetupSingleton.EnsureSingletonAvailable(ApplicationContext); setup.InitializeAndMonitor(this); @@ -77,14 +80,7 @@ namespace MvvmCross.Droid.Support.V7.AppCompat if (!_isResumed) return; - TriggerFirstNavigate(); - } - - protected virtual void TriggerFirstNavigate() - { - var startup = Mvx.Resolve(); - if (!startup.IsStarted) - startup.Start(); + RunAppStart(_bundle); } } } diff --git a/MvvmCross/Platforms/Android/Views/MvxSplashScreenActivity.cs b/MvvmCross/Platforms/Android/Views/MvxSplashScreenActivity.cs index 95ed4a891..1920d76f8 100644 --- a/MvvmCross/Platforms/Android/Views/MvxSplashScreenActivity.cs +++ b/MvvmCross/Platforms/Android/Views/MvxSplashScreenActivity.cs @@ -19,6 +19,8 @@ namespace MvvmCross.Platforms.Android.Views private readonly int _resourceId; + private Bundle _bundle; + public new MvxNullViewModel ViewModel { get { return base.ViewModel as MvxNullViewModel; } @@ -39,6 +41,8 @@ namespace MvvmCross.Platforms.Android.Views { RequestWindowFeatures(); + _bundle = bundle; + var setup = MvxAndroidSetupSingleton.EnsureSingletonAvailable(ApplicationContext); setup.InitializeAndMonitor(this); @@ -76,14 +80,7 @@ namespace MvvmCross.Platforms.Android.Views if (!_isResumed) return; - TriggerFirstNavigate(); - } - - protected virtual void TriggerFirstNavigate() - { - var startup = Mvx.Resolve(); - if (!startup.IsStarted) - startup.Start(); + RunAppStart(_bundle); } } } From c83eb05e5a4a5162c27fd73e7157a616cc874ddf Mon Sep 17 00:00:00 2001 From: nmilcoff Date: Sat, 31 Mar 2018 00:43:58 -0300 Subject: [PATCH 5/7] Playground.Droid: Fix icon name and use AppCompat SplashScreen --- .../Playground/Playground.Droid/MainApplication.cs | 1 - .../Playground.Droid/Playground.Droid.csproj | 10 +++++----- .../Resources/layout/SplashScreen.axml | 3 ++- Projects/Playground/Playground.Droid/SplashScreen.cs | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Projects/Playground/Playground.Droid/MainApplication.cs b/Projects/Playground/Playground.Droid/MainApplication.cs index b11536964..03cf7fab3 100644 --- a/Projects/Playground/Playground.Droid/MainApplication.cs +++ b/Projects/Playground/Playground.Droid/MainApplication.cs @@ -6,7 +6,6 @@ using System; using Android.App; using Android.Runtime; using MvvmCross.Droid.Support.V7.AppCompat; -using MvvmCross.Platforms.Android.Views; using Playground.Core; namespace Playground.Droid diff --git a/Projects/Playground/Playground.Droid/Playground.Droid.csproj b/Projects/Playground/Playground.Droid/Playground.Droid.csproj index a9ff6bfe8..713250333 100644 --- a/Projects/Playground/Playground.Droid/Playground.Droid.csproj +++ b/Projects/Playground/Playground.Droid/Playground.Droid.csproj @@ -114,11 +114,11 @@ Designer - - - - - + + + + + diff --git a/Projects/Playground/Playground.Droid/Resources/layout/SplashScreen.axml b/Projects/Playground/Playground.Droid/Resources/layout/SplashScreen.axml index 60edacd23..23a17cc0f 100644 --- a/Projects/Playground/Playground.Droid/Resources/layout/SplashScreen.axml +++ b/Projects/Playground/Playground.Droid/Resources/layout/SplashScreen.axml @@ -2,7 +2,8 @@ + android:layout_height="match_parent" + android:background="@color/colorAccent"> Date: Sat, 31 Mar 2018 00:44:17 -0300 Subject: [PATCH 6/7] Playground.Forms.Droid SplashScreen: Use RunAppStart method --- Projects/Playground/Playground.Forms.Droid/SplashScreen.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Projects/Playground/Playground.Forms.Droid/SplashScreen.cs b/Projects/Playground/Playground.Forms.Droid/SplashScreen.cs index 3084ba4e6..7a8207403 100644 --- a/Projects/Playground/Playground.Forms.Droid/SplashScreen.cs +++ b/Projects/Playground/Playground.Forms.Droid/SplashScreen.cs @@ -4,6 +4,7 @@ using Android.App; using Android.Content.PM; +using Android.OS; using MvvmCross.Core; using MvvmCross.Platforms.Android.Views; @@ -25,10 +26,10 @@ namespace Playground.Forms.Droid this.RegisterSetupType(); } - protected override void TriggerFirstNavigate() + protected override void RunAppStart(Bundle bundle) { StartActivity(typeof(MainActivity)); - base.TriggerFirstNavigate(); + base.RunAppStart(bundle); } } } From eaecd1405281892ccbc5cc12e118e3798660f487 Mon Sep 17 00:00:00 2001 From: Nick Randolph Date: Sun, 1 Apr 2018 14:01:24 +1000 Subject: [PATCH 7/7] Adding some documentation on the setup singleton class --- MvvmCross/Core/MvxSetupSingleton.cs | 42 ++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/MvvmCross/Core/MvxSetupSingleton.cs b/MvvmCross/Core/MvxSetupSingleton.cs index a41111d95..c4c7c91ae 100644 --- a/MvvmCross/Core/MvxSetupSingleton.cs +++ b/MvvmCross/Core/MvxSetupSingleton.cs @@ -10,6 +10,29 @@ using MvvmCross.Logging; namespace MvvmCross.Core { + /// + /// The setup singleton is designed to ensure only a single instance + /// of MvxSetup is created and invoked. There are three important methods + /// to the MvxSetupSingleton class: + /// EnsureSingletonAvailable - this is a static method that will return + /// the one and only instance of MvxSetupSingleton. This method is protected + /// as it's assumed that each platform will provide a platform specific + /// public overload for this method which will include any platform parameters + /// required + /// EnsureInitialized - this is an instance method that should be called + /// to guarrantee that setup has been created and initialized. This method + /// is blocking so make sure it's only called at a point where there + /// are no other UI methods are being invoked. This method is typically called + /// in applications where there is no splash screen. + /// InitializeAndMonitor - this is an instance method that can be called + /// to make sure that the initialization of setup has begun. It registers + /// an object to be notified when setup initialization has completed. The callback + /// will be raised on the UI thread. This method is not blocking, and doesn't + /// guarrantee setup initialize has finished when it returns. This method is + /// typically called by the splash screen view of an application, passing + /// itself in as the object to be notified. On notification the splash screen + /// view will trigger navigation to the first view + /// public abstract class MvxSetupSingleton : MvxSingleton { @@ -20,6 +43,13 @@ namespace MvvmCross.Core protected virtual IMvxSetup Setup => _setup; + /// + /// Returns a platform specific instance of Setup + /// A useful overload to allow for platform specific + /// setup logic to be invoked. + /// + /// The platform specific setup type + /// A platform specific instance of Setup public virtual TMvxSetup PlatformSetup() where TMvxSetup : IMvxSetup { @@ -34,17 +64,27 @@ namespace MvvmCross.Core } } + /// + /// Returns a singleton object that is used to manage the creation and + /// execution of setup + /// + /// The platform specific setup singleton type + /// A platform specific setup singleton protected static TMvxSetupSingleton EnsureSingletonAvailable() where TMvxSetupSingleton : MvxSetupSingleton, new() { + // Double null - check before creating the setup singleton object if (Instance != null) return Instance as TMvxSetupSingleton; - lock (LockObject) { if (Instance != null) return Instance as TMvxSetupSingleton; + // Go ahead and create the setup singleton, and then + // create the setup instance. + // Note that the Instance property is set within the + // singleton constructor var instance = new TMvxSetupSingleton(); instance.CreateSetup(); return Instance as TMvxSetupSingleton;