Merge pull request #2746 from DGenix/unblock-setup

MvxSetupSingleton optimizations / Fix SplashScreen initialization on Android
This commit is contained in:
Martijn van Dijk 2018-04-02 10:50:30 +02:00 коммит произвёл GitHub
Родитель d04996f8db eaecd14052
Коммит e37c07f790
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 105 добавлений и 78 удалений

Просмотреть файл

@ -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);
}
}
}