Skip to content

Commit

Permalink
Merge branch 'hotfix/9.1.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
Cheesebaron committed Aug 1, 2023
2 parents 29bc284 + b351cfd commit 07f487a
Show file tree
Hide file tree
Showing 28 changed files with 394 additions and 58 deletions.
6 changes: 3 additions & 3 deletions CHANGELOG.md
@@ -1,6 +1,6 @@
# Changelog

## [9.1.0](https://github.com/MvvmCross/MvvmCross/tree/10.0.0) (2023-07-27)
## [9.1.0](https://github.com/MvvmCross/MvvmCross/tree/9.1.0) (2023-07-27)

[Full Changelog](https://github.com/MvvmCross/MvvmCross/compare/9.0.10...9.1.0)

Expand All @@ -9,7 +9,6 @@
- Remove IMvxViewModel\<TResult\> and IMvxViewModel\<in TParameter, TResult\> [\#4651](https://github.com/MvvmCross/MvvmCross/issues/4651)
- Remove IMvxViewModelResult [\#4652](https://github.com/MvvmCross/MvvmCross/pull/4652) ([Cheesebaron](https://github.com/Cheesebaron))
- Remove ViewModel cache. It can lead to leaks and doesn't provide value [\#4650](https://github.com/MvvmCross/MvvmCross/pull/4650) ([Cheesebaron](https://github.com/Cheesebaron))
- Move Setup to MvxAndroidApplication +semver:breaking [\#4546](https://github.com/MvvmCross/MvvmCross/pull/4546) ([Cheesebaron](https://github.com/Cheesebaron))

**Implemented enhancements:**

Expand Down Expand Up @@ -59,6 +58,7 @@
- Mac view is not created until one of presentation attributes set explicitly [\#4572](https://github.com/MvvmCross/MvvmCross/issues/4572)
- Android Mvx.IoCProvider.Resolve hitting MvvmCross.Exceptions.MvxIoCResolveException within ListenableWorkers when App is closed [\#4558](https://github.com/MvvmCross/MvvmCross/issues/4558)
- \[Mac\] Correctly set view type when applying default presenter attribu… [\#4573](https://github.com/MvvmCross/MvvmCross/pull/4573) ([snechaev](https://github.com/snechaev))
- Move Setup to MvxAndroidApplication +semver:breaking [\#4546](https://github.com/MvvmCross/MvvmCross/pull/4546) ([Cheesebaron](https://github.com/Cheesebaron))

**Merged pull requests:**

Expand Down Expand Up @@ -1703,4 +1703,4 @@

## [Release-3.0.8.1](https://github.com/MvvmCross/MvvmCross/tree/Release-3.0.8.1) (2013-06-09)

\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
3 changes: 3 additions & 0 deletions Directory.Packages.props
Expand Up @@ -14,10 +14,13 @@
</PackageVersion>
<PackageVersion Include="Serilog" Version="3.0.1" />
<PackageVersion Include="Serilog.Extensions.Logging" Version="7.0.0" />
<PackageVersion Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageVersion Include="Serilog.Sinks.Trace" Version="3.0.0" />
<PackageVersion Include="Serilog.Sinks.Debug" Version="2.0.0" />
<PackageVersion Include="Serilog.Sinks.Xamarin" Version="1.0.0" />
<PackageVersion Include="SonarAnalyzer.CSharp" Version="9.6.0.74858" />
<PackageVersion Include="SourceLink.Create.CommandLine" Version="2.8.3" />
<PackageVersion Include="Xamarin.Android.Glide" Version="4.15.1.2" />
<PackageVersion Include="Xamarin.AndroidX.AppCompat" Version="1.6.1.3" />
<PackageVersion Include="Xamarin.AndroidX.AppCompat.AppCompatResources" Version="1.6.1.3" />
<PackageVersion Include="Xamarin.AndroidX.CardView" Version="1.0.0.21" />
Expand Down
16 changes: 16 additions & 0 deletions MvvmCross/Core/MvxSetup.cs
Expand Up @@ -176,6 +176,8 @@ public virtual void InitializeSecondary()
InitializeNavigationSerializer(_iocProvider);
SetupLog?.Log(LogLevel.Trace, "Setup: InpcInterception start");
InitializeInpcInterception(_iocProvider);
SetupLog?.Log(LogLevel.Trace, "Setup: InpcInterception start");
InitializeViewModelCache(_iocProvider);
SetupLog?.Log(LogLevel.Trace, "Setup: LastChance start");
InitializeLastChance(_iocProvider);
SetupLog?.Log(LogLevel.Trace, "Setup: Secondary end");
Expand All @@ -200,6 +202,19 @@ protected virtual void InitializeInpcInterception(IMvxIoCProvider iocProvider)
// by default no Inpc calls are intercepted
}

protected virtual IMvxChildViewModelCache InitializeViewModelCache(IMvxIoCProvider iocProvider)
{
ValidateArguments(iocProvider);

var cache = CreateViewModelCache(iocProvider);
return cache;
}

protected virtual IMvxChildViewModelCache CreateViewModelCache(IMvxIoCProvider iocProvider)
{
return iocProvider.Resolve<IMvxChildViewModelCache>();
}

protected virtual IMvxSettings InitializeSettings(IMvxIoCProvider iocProvider)
{
ValidateArguments(iocProvider);
Expand Down Expand Up @@ -311,6 +326,7 @@ protected virtual void RegisterDefaultSetupDependencies(IMvxIoCProvider iocProvi
iocProvider.LazyConstructAndRegisterSingleton<IMvxTypeToTypeLookupBuilder, MvxViewModelViewLookupBuilder>();
iocProvider.LazyConstructAndRegisterSingleton<IMvxCommandCollectionBuilder, MvxCommandCollectionBuilder>();
iocProvider.LazyConstructAndRegisterSingleton<IMvxNavigationSerializer, MvxStringDictionaryNavigationSerializer>();
iocProvider.LazyConstructAndRegisterSingleton<IMvxChildViewModelCache, MvxChildViewModelCache>();

iocProvider.RegisterType<IMvxCommandHelper, MvxWeakCommandHelper>();
}
Expand Down
Expand Up @@ -415,12 +415,12 @@ private bool ChangePagePresentation(MvxPagePresentationHint pagePresentationHint

if (request is MvxViewModelInstanceRequest viewModelInstanceRequest)
{
var intent = requestTranslator.GetIntentFor(
var intentWithKey = requestTranslator.GetIntentWithKeyFor(
viewModelInstanceRequest.ViewModelInstance,
viewModelInstanceRequest
);

return intent;
return intentWithKey.intent;
}

return requestTranslator.GetIntentFor(request);
Expand Down
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MS-PL license.
// See the LICENSE file in the project root for more information.

using System;
using Android.Content;
using MvvmCross.ViewModels;

Expand All @@ -10,6 +11,10 @@ namespace MvvmCross.Platforms.Android.Views
public interface IMvxAndroidViewModelRequestTranslator
{
Intent GetIntentFor(MvxViewModelRequest request);
Intent GetIntentFor(IMvxViewModel existingViewModelToUse, MvxViewModelRequest request);

// Important: if calling GetIntentWithKeyFor then you must later call RemoveSubViewModelWithKey on the returned key
(Intent intent, int key) GetIntentWithKeyFor(IMvxViewModel existingViewModelToUse, MvxViewModelRequest request);

void RemoveSubViewModelWithKey(int key);
}
}
5 changes: 2 additions & 3 deletions MvvmCross/Platforms/Android/Views/IMvxSingleViewModelCache.cs
@@ -1,8 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MS-PL license.
// See the LICENSE file in the project root for more information.

using Android.OS;
#nullable enable
using MvvmCross.ViewModels;

namespace MvvmCross.Platforms.Android.Views
Expand All @@ -11,6 +10,6 @@ public interface IMvxSingleViewModelCache
{
void Cache(IMvxViewModel toCache, Bundle bundle);

IMvxViewModel GetAndClear(Bundle bundle);
IMvxViewModel? GetAndClear(Bundle bundle);
}
}
Expand Up @@ -25,6 +25,10 @@ public static void AddEventListeners(this IMvxEventSourceActivity activity)
{
var bindingAdapter = new MvxBindingActivityAdapter(activity);
}
if (activity is IMvxChildViewModelOwner)
{
var childOwnerAdapter = new MvxChildViewModelOwnerAdapter(activity);
}
}

public static void OnViewCreate(this IMvxAndroidView androidView, Bundle bundle)
Expand Down
42 changes: 40 additions & 2 deletions MvvmCross/Platforms/Android/Views/MvxAndroidViewsContainer.cs
Expand Up @@ -18,6 +18,7 @@ public class MvxAndroidViewsContainer
, IMvxAndroidViewsContainer
{
private const string ExtrasKey = "MvxLaunchData";
private const string SubViewModelKey = "MvxSubViewModelKey";

private readonly Context _applicationContext;
private readonly ILogger<MvxAndroidViewsContainer>? _logger;
Expand All @@ -28,6 +29,8 @@ public MvxAndroidViewsContainer(Context applicationContext)
_logger = MvxLogHost.GetLog<MvxAndroidViewsContainer>();
}

#region Implementation of IMvxAndroidViewModelRequestTranslator

public virtual IMvxViewModel? Load(Intent intent, IMvxBundle? savedState)
{
return Load(intent, null, null);
Expand All @@ -53,6 +56,13 @@ public MvxAndroidViewsContainer(Context applicationContext)
return DirectLoad(savedState, viewModelTypeHint);
}

IMvxViewModel? mvxViewModel;
if (TryGetEmbeddedViewModel(intent, out mvxViewModel))
{
_logger?.Log(LogLevel.Trace, "Embedded ViewModel used");
return mvxViewModel;
}

_logger?.Log(LogLevel.Trace, "Attempting to load new ViewModel from Intent with Extras");
var toReturn = CreateViewModelFromIntent(intent, savedState);
if (toReturn != null)
Expand Down Expand Up @@ -94,6 +104,23 @@ public MvxAndroidViewsContainer(Context applicationContext)
return loaderService.LoadViewModel(viewModelRequest, savedState);
}

protected virtual bool TryGetEmbeddedViewModel(Intent intent, out IMvxViewModel? mvxViewModel)
{
var embeddedViewModelKey = intent.Extras?.GetInt(SubViewModelKey);
if (embeddedViewModelKey != null && embeddedViewModelKey.Value != 0)
{
mvxViewModel = Mvx.IoCProvider.Resolve<IMvxChildViewModelCache>().Get(embeddedViewModelKey.Value);
if (mvxViewModel != null)
{
RemoveSubViewModelWithKey(embeddedViewModelKey.Value);
return true;
}
}

mvxViewModel = null;
return false;
}

public virtual Intent GetIntentFor(MvxViewModelRequest request)
{
var viewType = GetViewType(request.ViewModelType);
Expand Down Expand Up @@ -121,13 +148,24 @@ protected virtual void AdjustIntentForPresentation(Intent intent, MvxViewModelRe
// intent.AddFlags(ActivityFlags.ClearTop);
}

public virtual Intent GetIntentFor(IMvxViewModel existingViewModelToUse, MvxViewModelRequest? request)
public virtual (Intent intent, int key) GetIntentWithKeyFor(IMvxViewModel existingViewModelToUse, MvxViewModelRequest? request)
{
request ??= MvxViewModelRequest.GetDefaultRequest(existingViewModelToUse.GetType());
var intent = GetIntentFor(request);

return intent;
var childViewModelCache = Mvx.IoCProvider.Resolve<IMvxChildViewModelCache>();
var key = childViewModelCache.Cache(existingViewModelToUse);
intent.PutExtra(SubViewModelKey, key);

return (intent, key);
}

public void RemoveSubViewModelWithKey(int key)
{
Mvx.IoCProvider.Resolve<IMvxChildViewModelCache>().Remove(key);
}

#endregion Implementation of IMvxAndroidViewModelRequestTranslator
}
#nullable restore
}
37 changes: 37 additions & 0 deletions MvvmCross/Platforms/Android/Views/MvxChildViewModelOwnerAdapter.cs
@@ -0,0 +1,37 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MS-PL license.
// See the LICENSE file in the project root for more information.

using System;
using MvvmCross.Exceptions;
using MvvmCross.Platforms.Android.Views.Base;

namespace MvvmCross.Platforms.Android.Views
{
public class MvxChildViewModelOwnerAdapter : MvxBaseActivityAdapter
{
protected IMvxChildViewModelOwner ChildOwner => (IMvxChildViewModelOwner)Activity;

public MvxChildViewModelOwnerAdapter(IMvxEventSourceActivity eventSource)
: base(eventSource)
{
if (!(eventSource is IMvxChildViewModelOwner))
{
throw new MvxException("You cannot use a MvxChildViewModelOwnerAdapter on {0}",
eventSource.GetType().Name);
}
}

protected override void EventSourceOnDestroyCalled(object sender, EventArgs eventArgs)
{
ChildOwner.ClearOwnedSubIndicies();
base.EventSourceOnDestroyCalled(sender, eventArgs);
}

protected override void EventSourceOnDisposeCalled(object sender, EventArgs eventArgs)
{
ChildOwner.ClearOwnedSubIndicies();
base.EventSourceOnDisposeCalled(sender, eventArgs);
}
}
}
Expand Up @@ -28,7 +28,27 @@ public static Intent CreateIntentFor<TTargetViewModel>(this IMvxAndroidView view

public static Intent CreateIntentFor(this IMvxAndroidView view, MvxViewModelRequest request)
{
return Mvx.IoCProvider.Resolve<IMvxAndroidViewModelRequestTranslator>()?.GetIntentFor(request);
return Mvx.IoCProvider.Resolve<IMvxAndroidViewModelRequestTranslator>().GetIntentFor(request);
}

public static Intent CreateIntentFor(this IMvxChildViewModelOwner view, IMvxViewModel subViewModel)
{
var requestTranslator = Mvx.IoCProvider.Resolve<IMvxAndroidViewModelRequestTranslator>();
var (intent, key) = requestTranslator.GetIntentWithKeyFor(subViewModel, null);

view.OwnedSubViewModelIndicies.Add(key);

return intent;
}

public static void ClearOwnedSubIndicies(this IMvxChildViewModelOwner view)
{
var translator = Mvx.IoCProvider.Resolve<IMvxAndroidViewModelRequestTranslator>();
foreach (var ownedSubViewModelIndex in view.OwnedSubViewModelIndicies)
{
translator.RemoveSubViewModelWithKey(ownedSubViewModelIndex);
}
view.OwnedSubViewModelIndicies.Clear();
}
}
}
12 changes: 10 additions & 2 deletions MvvmCross/Platforms/Android/Views/MvxFragmentExtensions.cs
Expand Up @@ -46,15 +46,23 @@ public static Type FindAssociatedViewModelType(this IMvxFragmentView fragmentVie
if (viewModelType == null
|| viewModelType == typeof(IMvxViewModel))
{
MvxLogHost.Default?.Log(LogLevel.Trace, "No ViewModel class specified for {FragmentViewType} in LoadViewModel",
MvxLogHost.Default?.Log(LogLevel.Trace, "No ViewModel class specified for {fragmentViewType} in LoadViewModel",
fragmentView.GetType().Name);
}

if (request == null)
request = MvxViewModelRequest.GetDefaultRequest(viewModelType);

var viewModelCache = Mvx.IoCProvider.Resolve<IMvxChildViewModelCache>();
if (viewModelCache.Exists(viewModelType))
{
var viewModelCached = viewModelCache.Get(viewModelType);
viewModelCache.Remove(viewModelType);
return viewModelCached;
}

var loaderService = Mvx.IoCProvider.Resolve<IMvxViewModelLoader>();
var viewModel = loaderService?.LoadViewModel(request, savedState);
var viewModel = loaderService.LoadViewModel(request, savedState);

return viewModel;
}
Expand Down
39 changes: 21 additions & 18 deletions MvvmCross/Platforms/Android/Views/MvxSingleViewModelCache.cs
@@ -1,8 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MS-PL license.
// See the LICENSE file in the project root for more information.

using Android.OS;
#nullable enable
using MvvmCross.ViewModels;

namespace MvvmCross.Platforms.Android.Views
Expand All @@ -14,32 +13,36 @@ public class MvxSingleViewModelCache

private int _counter;

private IMvxViewModel _currentViewModel;
private WeakReference<IMvxViewModel>? _currentViewModel;

public void Cache(IMvxViewModel toCache, Bundle bundle)
{
_currentViewModel = toCache;
_currentViewModel = new WeakReference<IMvxViewModel>(toCache);
_counter++;

if (_currentViewModel == null)
{
return;
}

bundle.PutInt(BundleCacheKey, _counter);
}

public IMvxViewModel GetAndClear(Bundle bundle)
public IMvxViewModel? GetAndClear(Bundle? bundle)
{
var storedViewModel = _currentViewModel;
_currentViewModel = null;

if (bundle == null)
return null;
try
{
if (bundle == null)
return null;

if (_currentViewModel?.TryGetTarget(out var storedViewModel) == true)
{
var key = bundle.GetInt(BundleCacheKey);
var toReturn = key == _counter ? storedViewModel : null;
return toReturn;
}
}
finally
{
_currentViewModel = null;
}

var key = bundle.GetInt(BundleCacheKey);
var toReturn = key == _counter ? storedViewModel : null;
return toReturn;
return null;
}
}
}
Expand Up @@ -91,7 +91,7 @@ protected virtual string GetRequestText(MvxViewModelRequest request)
string requestText = string.Empty;
if (request is MvxViewModelInstanceRequest)
{
requestText = requestTranslator.GetRequestTextFor(((MvxViewModelInstanceRequest)request).ViewModelInstance);
requestText = requestTranslator.GetRequestTextWithKeyFor(((MvxViewModelInstanceRequest)request).ViewModelInstance);
}
else
{
Expand Down

0 comments on commit 07f487a

Please sign in to comment.