Merge pull request #2746 from DGenix/unblock-setup
MvxSetupSingleton optimizations / Fix SplashScreen initialization on Android
This commit is contained in:
Коммит
e37c07f790
|
@ -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;
|
||||
|
||||
|
@ -86,12 +85,13 @@ 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)
|
||||
|
|
|
@ -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<IMvxAppStart>();
|
||||
if (!startup.IsStarted)
|
||||
startup.Start();
|
||||
RunAppStart(_bundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -10,24 +10,48 @@ using MvvmCross.Logging;
|
|||
|
||||
namespace MvvmCross.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
public abstract class MvxSetupSingleton
|
||||
: MvxSingleton<MvxSetupSingleton>
|
||||
{
|
||||
private static readonly object LockObject = 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
|
||||
/// <summary>
|
||||
/// Returns a platform specific instance of Setup
|
||||
/// A useful overload to allow for platform specific
|
||||
/// setup logic to be invoked.
|
||||
/// </summary>
|
||||
/// <typeparam name="TMvxSetup">The platform specific setup type</typeparam>
|
||||
/// <returns>A platform specific instance of Setup</returns>
|
||||
public virtual TMvxSetup PlatformSetup<TMvxSetup>()
|
||||
where TMvxSetup : IMvxSetup
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -40,17 +64,27 @@ namespace MvvmCross.Core
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a singleton object that is used to manage the creation and
|
||||
/// execution of setup
|
||||
/// </summary>
|
||||
/// <typeparam name="TMvxSetupSingleton">The platform specific setup singleton type</typeparam>
|
||||
/// <returns>A platform specific setup singleton</returns>
|
||||
protected static TMvxSetupSingleton EnsureSingletonAvailable<TMvxSetupSingleton>()
|
||||
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;
|
||||
|
@ -61,19 +95,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 +115,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 +137,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 +169,16 @@ namespace MvvmCross.Core
|
|||
base.Dispose(isDisposing);
|
||||
}
|
||||
|
||||
private TaskCompletionSource<bool> StartSetupInitialization()
|
||||
private void StartSetupInitialization()
|
||||
{
|
||||
var completionSource = new TaskCompletionSource<bool>();
|
||||
IsInitialisedTaskCompletionSource = new TaskCompletionSource<bool>();
|
||||
_setup.InitializePrimary();
|
||||
Task.Run(() =>
|
||||
{
|
||||
_setup.InitializeSecondary();
|
||||
lock (LockObject)
|
||||
{
|
||||
completionSource.SetResult(true);
|
||||
_initialized = true;
|
||||
IsInitialisedTaskCompletionSource.SetResult(true);
|
||||
var dispatcher = Mvx.GetSingleton<IMvxMainThreadDispatcher>();
|
||||
dispatcher.RequestMainThreadAction(() =>
|
||||
{
|
||||
|
@ -157,8 +189,6 @@ namespace MvvmCross.Core
|
|||
});
|
||||
}
|
||||
});
|
||||
|
||||
return completionSource;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,12 +112,13 @@ 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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<IMvxAppStart>();
|
||||
if (!startup.IsStarted)
|
||||
startup.Start();
|
||||
RunAppStart(_bundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
@ -15,7 +15,7 @@ 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)
|
||||
|
|
|
@ -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<Setup>();
|
||||
}
|
||||
|
||||
protected override void TriggerFirstNavigate()
|
||||
protected override void RunAppStart(Bundle bundle)
|
||||
{
|
||||
StartActivity(typeof(MainActivity));
|
||||
base.TriggerFirstNavigate();
|
||||
base.RunAppStart(bundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче