Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplifying setup and forcing EnsureInitialize to block until seutp c… #1

Open
wants to merge 6 commits into
base: unblock-setup
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
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
Expand Down Expand Up @@ -86,5 +85,10 @@ protected virtual void TriggerFirstNavigate()
if (!startup.IsStarted)
startup.Start();
}

// do nothing on this override, as initial navigation is managed by TriggerFirstNavigate
protected override void RunAppStart(Bundle bundle)
{
}
}
}
115 changes: 63 additions & 52 deletions MvvmCross/Core/MvxSetupSingleton.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,15 @@ public abstract class MvxSetupSingleton
: MvxSingleton<MvxSetupSingleton>
{
private static readonly object LockObject = new object();
private static readonly object CompletionLockObject = new object();
private static TaskCompletionSource<bool> 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<TMvxSetup>() where TMvxSetup : IMvxSetup
public virtual TMvxSetup PlatformSetup<TMvxSetup>()
where TMvxSetup : IMvxSetup
{
try
{
Expand Down Expand Up @@ -59,59 +54,66 @@ protected static TMvxSetupSingleton EnsureSingletonAvailable<TMvxSetupSingleton>

public virtual void EnsureInitialized()
{
lock (LockObject)
{
if (_initialized)
return;
// This method is safe to call multiple times - will only run init once
StartSetupInitialization();

if (IsInitialisedTaskCompletionSource == null)
{
IsInitialisedTaskCompletionSource = StartSetupInitialization();
}
else
{
MvxLog.Instance.Trace("EnsureInitialized has already been called so now waiting for completion");
}
}
// Return immediately if the task is completed
if (IsInitialisedTaskCompletionSource.Task.IsCompleted)
return;

// Block waiting on Initialize to be completed
IsInitialisedTaskCompletionSource.Task.GetAwaiter().GetResult();
}

public virtual void InitializeAndMonitor(IMvxSetupMonitor setupMonitor)
{
lock (LockObject)
// Grab the lock for the setupMonitor to make sure
// InitializationComplete isn't attempted whilst we're
// checking to see if Initialize has completed
lock (CompletionLockObject)
{
_currentMonitor = setupMonitor;
if (_initialized)
var hasCompleted = false;
// Grab the lock for setting the completion state after Initialize is complete
lock (LockObject)
{
_currentMonitor?.InitializationComplete();
return;
hasCompleted = IsInitialisedTaskCompletionSource?.Task.IsCompleted ?? false;
// At this point if the completion state isn't completed, we know
// that we can set the _currentMonitor, and that InitializationComplete will get invoked
if (!hasCompleted)
{
_currentMonitor = setupMonitor;
}
}

if (IsInitialisedTaskCompletionSource != null)
// At this point, if completion state is completed, we should
// NOT set the _currentMonitor (as there is a risk InitializationComplete
// has already been invoked). Instead we should just call InitializationComplete
// directly, and return.
if (hasCompleted)
{
setupMonitor.InitializationComplete();
return;
}

IsInitialisedTaskCompletionSource = StartSetupInitialization();
}

// If we get here, we should attempt to start Initialize because all we
// know is that Initialize hasn't completed. StartSetupInitialization is
// clever enough to know if it needs to run Initialize, or if it's already running
StartSetupInitialization();
}

public virtual void CancelMonitor(IMvxSetupMonitor setupMonitor)
{
lock (LockObject)
lock (CompletionLockObject)
{
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
Expand All @@ -128,37 +130,46 @@ protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
lock (LockObject)
lock (CompletionLockObject)
{
_currentMonitor = null;
}
}
base.Dispose(isDisposing);
}

private TaskCompletionSource<bool> StartSetupInitialization()
private void StartSetupInitialization()
{
var completionSource = new TaskCompletionSource<bool>();
// Do double-test to detect if Initialize has started
if (IsInitialisedTaskCompletionSource != null) return;
lock (LockObject)
{
if (IsInitialisedTaskCompletionSource != null) return;

// At this point we know Initialize hasn't started, so create
// the completion source and kick of Initialize
IsInitialisedTaskCompletionSource = new TaskCompletionSource<bool>();
}

// InitializePrimary should be only init methods that needs to be done on the UI thread
_setup.InitializePrimary();
Task.Run(() =>
{
// InitializeSecondary should be the bulk of init methods (and done on non-UI thread)
_setup.InitializeSecondary();
lock (LockObject)
{
completionSource.SetResult(true);
_initialized = true;
var dispatcher = Mvx.GetSingleton<IMvxMainThreadDispatcher>();
dispatcher.RequestMainThreadAction(() =>
{
if (_currentMonitor != null)
{
_currentMonitor?.InitializationComplete();
}
});
IsInitialisedTaskCompletionSource.SetResult(true);
}
var dispatcher = Mvx.GetSingleton<IMvxMainThreadDispatcher>();
dispatcher.RequestMainThreadAction(() =>
{
lock (CompletionLockObject)
{
_currentMonitor?.InitializationComplete();
}
});
});

return completionSource;
}
}
}
5 changes: 5 additions & 0 deletions MvvmCross/Platforms/Android/Views/MvxSplashScreenActivity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,5 +85,10 @@ protected virtual void TriggerFirstNavigate()
if (!startup.IsStarted)
startup.Start();
}

// do nothing on this override, as initial navigation is managed by TriggerFirstNavigate
protected override void RunAppStart(Bundle bundle)
{
}
}
}
2 changes: 1 addition & 1 deletion MvvmCross/Platforms/Ios/Core/MvxApplicationDelegate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public abstract class MvxApplicationDelegate : UIApplicationDelegate, IMvxApplic
/// <summary>
/// UIApplicationDelegate.Window doesn't really exist / work. It was added by Xamarin.iOS templates
/// </summary>
public new UIWindow Window { get; set; }
public new virtual UIWindow Window { get; set; }

public MvxApplicationDelegate() : base()
{
Expand Down
2 changes: 1 addition & 1 deletion MvvmCross/Platforms/Tvos/Core/MvxApplicationDelegate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public abstract class MvxApplicationDelegate : UIApplicationDelegate, IMvxApplic
/// <summary>
/// UIApplicationDelegate.Window doesn't really exist / work. It was added by Xamarin.iOS templates
/// </summary>
public new UIWindow Window { get; set; }
public new virtual UIWindow Window { get; set; }

public MvxApplicationDelegate() : base()
{
Expand Down
1 change: 0 additions & 1 deletion Projects/Playground/Playground.Droid/MainApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
using Android.App;
using Android.Runtime;
using MvvmCross.Droid.Support.V7.AppCompat;
using MvvmCross.Platforms.Android.Views;
using Playground.Core;

namespace Playground.Droid
Expand Down
10 changes: 5 additions & 5 deletions Projects/Playground/Playground.Droid/Playground.Droid.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -114,11 +114,11 @@
<SubType>Designer</SubType>
</AndroidResource>
<AndroidResource Include="Resources\values\Strings.xml" />
<AndroidResource Include="Resources\mipmap-hdpi\Icon.png" />
<AndroidResource Include="Resources\mipmap-mdpi\Icon.png" />
<AndroidResource Include="Resources\mipmap-xhdpi\Icon.png" />
<AndroidResource Include="Resources\mipmap-xxhdpi\Icon.png" />
<AndroidResource Include="Resources\mipmap-xxxhdpi\Icon.png" />
<AndroidResource Include="Resources\mipmap-hdpi\icon.png" />
<AndroidResource Include="Resources\mipmap-mdpi\icon.png" />
<AndroidResource Include="Resources\mipmap-xhdpi\icon.png" />
<AndroidResource Include="Resources\mipmap-xxhdpi\icon.png" />
<AndroidResource Include="Resources\mipmap-xxxhdpi\icon.png" />
<AndroidResource Include="Resources\layout\SplashScreen.axml" />
<AndroidResource Include="Resources\values\styles.xml" />
<AndroidResource Include="Resources\values-v21\styles.xml" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:background="@color/colorAccent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
Expand Down
9 changes: 7 additions & 2 deletions Projects/Playground/Playground.Droid/SplashScreen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

using Android.App;
using Android.Content.PM;
using MvvmCross.Platforms.Android.Views;
using MvvmCross.Droid.Support.V7.AppCompat;

namespace Playground.Droid
{
Expand All @@ -15,11 +15,16 @@ namespace Playground.Droid
, Theme = "@style/AppTheme.Splash"
, NoHistory = true
, ScreenOrientation = ScreenOrientation.Portrait)]
public class SplashScreen : MvxSplashScreenActivity
public class SplashScreen : MvxSplashScreenAppCompatActivity
{
public SplashScreen()
: base(Resource.Layout.SplashScreen)
{
}

protected override void OnResume()
{
base.OnResume();
}
}
}