Does Xamarin Forms have memory leak on UWP? - xamarin.forms

I wrote UWP but this can be also on Android on IOS because I profiled only UWP application using VS2017.
Steps to create problem.
- Open VS 2017 and start a new xamarin forms project by selecting tabbed page or masterdetail page. No need to write any single code.
Problem;
First snapshot is after application is loaded.
2nd one has taken after selecting an item in the list and navigating to the ItemDetailsPage
3rd snapshot was taken after navigating back to ItemsPage
Expections; to not see ItemDetailsPage on 3rd snapshot because I am navigating back and this page is popped from the navigation stack. so it should be removed, collected by GC or disposed.
Here is the 3rd snapshot details;
Do I read this snapshot wrong or there is something wrong with the xamarin forms applications?
EDIT: Below screenshot also stats that there is "cycle detected". what does that mean? i thought cycles cause memory leaks, dont they?

not see ItemDetailsPage on 3rd snapshot because I am navigating back and this page is popped from the navigation stack. so it should be removed, collected by GC or disposed.
If you try navigating to different detailed pages then come back and take a snapshot again, you will find the count of ItemDetailPage stays 1.
Sometimes, for performance's sake, objects like page or resources will be properly cached and reused. If you have a page which contains large images or resources, you should try to dispose them manually to reduce memory usage.
Update:
Now question is how to properly dispose it? where to do that? I keep reading OnDissapearing method which is not safe for me as it can be called on navigating forward , not only navigating back in the stack. what is your suggestion for that?
If you don't want to dispose your resources for navigating forward, you can define a status parameter when navigating forward, do not dispose the resource. ex:
bool forwardNavigating=false;
...
protected override void OnDisappearing()
{
base.OnDisappearing();
if (!forwardNavigating)
{
//dispose your resources
}
}
elow screenshot also stats that there is "cycle detected". what does that mean? i thought cycles cause memory leaks, dont they?
cycle detected means:
An object can reference a second object that directly or indirectly holds a reference to the first object.
Please refer to Circular references of Analyze .NET Framework memory issues.
Personal thought:
I don't think it is a memory issue in terms of cycle detected. Under some situations we may need to visit parent object when we are in it's children. Thus we need a property Parent while the parent is still referencing the children. This happens a lot in views. I don't know how the analyzer works and whether it is the situation I described above. What I want to say is cycle detected doesn't equals Memory Leaks.

The same memory leak happens while dynamically changing the BindingContext (Reset the BindingContext by setting null and reassign the new view model) in Xamarin ListView. Does anyone come across this issue

Related

Storing a view in Xamarin.Forms MvvmCross

I have an app with four main pages, switched through a tab bar (no "back" button).
One page has a lot of content (ScrollView) and takes quite a few seconds until it's rendered. I handle that by showing a "loading" overlay while the work is done. But for that specific page I'd like to keep the view alive, so that when the user switches to another page and comes back later, the page is ready without loading everything again.
I'm not sure how to do that in MvvmCross, though.
I did read the documentation and from what I understood the View Presenter would be the right way to do it, since the docs say:
"Another kind of presentation changes your app can request through
hints includes clearing / modifying the BackStack, changing a root
while maintaining the existent views, … possibilities are really
endless. Once again your app is king here!"
I guess I would need to create a custom MvxPresentationHint for that, but I don't quite get it :(
How or rather where would I access and store/load the View?
I'm generally still quite unfamiliar with MvvmCross (how it works under the hood) and especially customization of Mvx classes, even though I've been using it for a while.
Any explanation and preferably code examples beyond what's written in the documentation would be extremely appreciated!
It isn't meaningful to attempt to "store" a view in MVVM. The XF view is a representation of what will be created with native (e.g. "Android" or "iOS") widgets. Creating and measuring/laying out those native widgets is what is slow. MVVM View Presenter won't speed up that logic.
Instead of "store", you need "keep alive":
For a ContentPage called MyPage, when you create it, store it in a static variable. Then re-use that variable. If you never need more than one of these, you can store it in the class itself.
Modify the "code behind", MyPage.xaml.cs:
public partial class MyPage : ContentPage
{
// Singleton Pattern.
private static MyPage _it;
public static MyPage It {
get {
if (_it == null)
_it = new MyPage();
return _it;
}
}
// "private", because calling this directly defeats the purpose. Instead, use `MyPage.It`.
private MyPage()
{
InitializeComponent();
}
}
To create it, whereever you would put:
new MyPage()
instead put this:
MyPage.It
For instance, you might do PushAsync(MyPage.It);
This will always return the SAME INSTANCE of MyPage. So once it has been created, it keeps its state.
IMPORTANT: Note that the constructor is only called ONCE. Any code that needs to be done each time the page appears, put in override .. OnAppearing() method.
LIMITATION: Views "expect" to be part of the visual hierarchy when they are manipulated. If you attempt to alter the page or its view model while it is not on the screen, you may encounter problems. Those are beyond the scope of this answer - create a new StackOverflow question with the details of any problem you encounter.

Issues with wrapping PrismForms NavigationService

In PrismForms we got the problem, that the NavigationStack is empty after navigating to a new page. That means after using the hardware back-button on the SecondPage, the app is closed. Although the back-arrow in the header on Android isnt shown. If looking closely you can see the back-arrow for a short moment after the page is switched. I guess thats before the NavigationStack gets cleared.
To the first page we navigate with the following command in OnInitialized() in our App.xaml.cs which derives from PrismApplication.
NavigationService.NavigateAsync("NavigationPage/StartPage");
(If only Navigating to „StartPage“ here, the Stack doesnt get cleared.)
That has do to with PageNavigationService.ProcessNavigationForNavigationPage(...) calling
bool clearNavStack = GetClearNavigationPageNavigationStack(currentPage); and PageNavigationService.ProcessNavigationForContentPage(...) not.
From the StartPage to the next we navigate with NavigateAsync("SecondPage")“. Here the described behaviour appears.
For navigation we use a class which wraps the Prism NavigationService. We hold him as a property and get him via Unity in our constructor:
this.PrismNavigation = prismNavigation ?? throw new ArgumentNullException(nameof(prismNavigation));
The methods „NavigateAsync“ and „GoBackAsync“, etc. we just pass through.
This way we want to seperate our ViewModel-Project from references to XamarinForms to later be able to use the same ViewModels for for example a WPF-GUI.
Why is the stack beeing cleared by our own NavigationService? If we register the original Prism NavigationService in App.xaml.cs instead, navigating back works as expected again. We found the point in the framework and could avoid the clearing with a drity hack, but that’s against the navigation-logic implemented in PrismForms, but we don’t understand how to do it the correct way.
Every help appreciated!
We edited a few things to get it working after finding some interesting information by Brian Lagunas in the forlast-post here:
https://github.com/PrismLibrary/Prism/issues/591
Although the topic was about something else, it led to improvements for overwriting the Navigation Service.
Remember that in your viewModels the Navigation Service must be named "navigationService" by convention. Also we switched from just holding the Prism Navigation Service as a parameter to deriving from it as suggested in the link above.
public class MyNavigationService : UnityPageNavigationService

Sometimes willHideViewController from UISplitViewControllerDelegate is not called

I have a problem.
My app is a tab bar controller and its first view controller is a split view controller.
This seems to be not ok for Apple because documents say a split voew controller must be the root, so perhaps that is the reason of my problems.
The problem is that sometimes, willHideViewController from UISplitViewControllerDelegate is not called, so, for this reason, the upper/left button sometimes is not created, which is anoying.
I realised, to reproduce this error, try several times this:
-Landscape mode.
-Select a tab different to split view controller tap.
-Move the iPad to portrait in that tab.
-Go to the split view controller tab, and sometimes, willHideViewController is not called so you will not see the upper button. However if I rotate my iPad to landscape and after that to portrait, it's fixed.
I tried to force manually several rotations to work around this problem, but no luck.
I still have to try any split view controller clone class from github or similar.
Do you have idea what's going on or any work around?
Here I show you two examples working properly.
Thanks a lot for your help.
Where do you set the splitViewControllers delegate? Perhaps you can set the delegate when you load the tab. It sounds like you set it only when you have rotated once?
Otherwise, see this example
Here they have the TableViewController be the delegate of the splitviewcontroller. Perhaps you could do the same with the TabBarController?
I've concluded that this can't be done in any way that I consider 'sufficiently' legitimate. It's possible to get frustratingly close, but the issue of having the willShow..., willHide disseminated to the split view controllers under each tab remains.
The solution that seems most likely to work is,
https://github.com/grgcombs/IntelligentSplitViewController/blob/master/IntelligentSplitViewController.m
Though this code is undoubtedly clever, it's a bit too 'side door' for me. I suspect (but don't know) that just invoking the delegate methods is not sufficient. Surely the UISplitViewController itself needs to change it's internal state as well as calling the delegate methods? This method 'just' invokes the delegate methods when there's an orientation change.
So... I've decided on a more legitimate solution, which is to use the new method introduced in iOS 5.
- (BOOL) splitViewController:(UISplitViewController *)svc
shouldHideViewController:(UIViewController *)vc
inOrientation:(UIInterfaceOrientation)orientation
{
return NO;
}
So, the master menu is never hidden, and therefore the problem of managing the popover doesn't arise.
Of course, this is still not totally 'legit' as it still includes UISplitViewControllers that are not at the top level (the UITabViewController is at the top level, and the split views are on each tab)
Good luck with whichever solution you choose.
I'll update this reply when I've confirmed Apple will approve an app using this solution.

IOErrorEvent Eluding Capture

I'm working on a Flex application that processes and displays small amounts of HTML, sometimes including images. I'm getting the HTML out of third-party RSS feeds. Sometimes, I see this in a pop-up window:
Error #2044: Unhandled IOErrorEvent:. text=Error #2035: URL Not Found.
The RSS URL is just fine, but there's apparently something in the downloaded HTML that's causing a problem. Since the application is meant to run as part of a non-interactive digital sign, anything that requires a click to continue is completely unacceptable. I don't care how useless or malformed a URL is; the app needs to ignore the problem without pestering the user.
Unfortunately, I'm having no luck trapping this event. I'm sprinkling calls like this liberally through the code:
[object].addEventListener(IOErrorEvent.IO_ERROR, handleIOError);
... where [object] is everything from the mx:Text object rendering the HTML to its mx:Canvas parent to the mx:Application top-level app, and handleIOError is a simple function that looks like this:
private function handleIOError(event:IOErrorEvent):void {
trace ("IO error occurred: " + event);
}
But so far, nothing; that bloody error keeps popping up in the Flash player. Does anybody have any insight as to where I'm going wrong?
Make sure you are putting the event on the right object. I haven't done a whole lot of remote loading in Flex, but in Flash, a hilarious and annoying quirk is that when you use the Loader class to load images, the object you need to put event handlers on is NOT the Loader itself, but a property of the loader called contentLoaderInfo.
Read the docs carefully on the objects you are using, a similar pitfall might be at play.
IOErrorEvent is not bubbled so you cant catch or control it if someone else is implementing it.
Please find out which third party component you are using and try to get source if its open source or read some documentation or ask support guys on how to turn off this alert.
For example, if I made RSS component for flex and on error if I displayed the alert, if you use my component, whatever you can do you cant turn off my error alert unless i have provided you a boolean switch to turn it off. So this is really a problem with who has written the code for this alert box. Whatever you do you will not be able to turn this thing off. Except reverse engineer, change the code and recompile it, but it should be legal.

How do I unload a externally loaded SWF file from a SWFLoader component in Adobe Flex?

I have an application that loads external SWF files and plays them inside a Adobe Flex / Air application via the SWFLoader Flex component. I have been trying to find a way to unload them from a button click event. I have Google'd far and wide and no one seems to have been able to do it without a hack. The combination of code I see people use is:
swfLoader.source = ""; // Removes the external link to the SWF.
swfLoader.load(null); // Forces the loader to try to load nothing.
// Note: At this point sound from the SWF is still playing, and
// seems to still be playing in memory.
flash.media.SoundMixer.stopAll();
// Stops the sound. This works on my development machine, but not
// on the client's.
If the SWFs are closed (hidden) this way, eventually the program crashes.
Any ideas? I have found tons of posts in various forums with people having the same problem. I assume I will get one wrong/incomplete answer here, and than my post will sink into nothingness as usual, but either way, thanks in advance!
Edit 1: I can't edit the actual SWF movies, they're created by the client. If I can't close any SWF opened through Flex, isn't that a problem with the Flex architecture? Is my only option sending the SWFs to the web browser?
... isn't that a problem with the Flex architecture?
Yes it is, and it also affects Flash in general. Until you can take advantage of the Loader.unloadAndStop() method in FP10 (AIR 1.5), you can't guarantee that externally loaded content will not continue to consume memory and cpu resources, even if you use the Loader.unload() method. (To be honest, I'm not 100% sure that even that will guarantee freeing of resources, but maybe I'm a pessimist.)
The next best thing is for you to insist that the creators of the content you load adhere to a set of guidelines, including exposing something like a dispose() method which your app can call to ask it to release as many resources as possible before you unload() it. If this isn't possible, then your application will almost definitely bloat in memory and cpu usage each time you load an external swf. Sorry.
If it makes you feel any better, you're not alone. ;)
It is a problem that a badly created SWF can sink your application, and many of the issues with this will be fixed in Flash Player 10, as others have mentioned. However, regardless of platform you will always risk having problems if you load third party code, there's always the possibility that it contains bugs, memory leaks or downright malicious code. Unless you can load content into a sandbox (and you can't in Flash, at least not yet), loading bad things will sink your app, it's as simple as that.
I'm sorry to say that unless you can guarantee the quality of the loaded content you can't guarantee the quality of your own application. Flash developers are notorious for writing things that leak, or can't be unloaded, because Flash makes it easy to do the wrong thing, especially for things that live on the time line. Loading any Flash content that you don't have control over directly is very perilous.
The best solution is
swfLoader.autoLoad = false;
swfLoader.unloadAndStop();
swfLoader.autoLoad = true;
In this way you stop the player, unload the content from memory and avoid the sound to remain playing..
Cheers
The problem resides in the loaded swf, it simply does not clean up the audio after itself.
Try attaching an unload event onto movieclips like this:
MovieClip(event.target.content).loaderInfo.addEventListener(Event.UNLOAD, unloadMovieClipHandler);
private function unloadMovieClipHandler(event:Event) : void
{
SoundMixer.stopAll();
}
I'd generally stay away from SWFLoader and use the classes in the mx.modules package.
Flex has a module system that enables this type of behavior. You can check it out here : http://livedocs.adobe.com/flex/3/html/help.html?content=modular_3.html . In general, dynamically loading and unloading swf components is tricky, especially if those modules modify any global state in the application (styles, etc..). But if you create an interface for your modules, and then each class you load/unload implement that interface as well as extend the flex module class, you can load and unload them cleanly.
Try the following:
try {
new LocalConnection().connect('foo');
new LocalConnection().connect('foo');
} catch (e:*) {}
That will force a Garbage Collection routine. If your SWF is still attached, then you've missed some sort of connection, like the audio.
There are a couple ways to force GC, which all kind of suck because they spike CPU, but the good news is that an official way is coming in Flash Player 10:
unloadAndStop
link: http://www.gskinner.com/blog/archives/2008/07/unloadandstop_i.html
Until then, I'm afraid you'll have to force it with hacks like I showed above.
You have not shown all of your code so I am going to assume you didn't use the unload method of the Loader class. Also swfLoader.load(null) seems wrong to me as the load method is expecting a URLRequest object. When you want to clean things up at the end, set the object's value to null instead of calling a null load. The fact that your still hearing audio indicates that your data wasn't unloaded, or the audio file does not reside inside the content that was unloaded. Lets walk through this.
Example below
var loader:Loader = new Loader();
var request:URLRequest = new URLRequest('test.swf');
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onSwfLoad, false, 0, true);
function onSwfLoad(e:Event):void
{
addChild(loader);
loader.contentLoaderInfo.addEventListener(Event.UNLOAD, onLoaderUnload, false, 0, true);
loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, onSwfLoad, false);
}
function onLoaderUnload(e:Event):void
{
trace('LOADER WAS SUCCESSFULLY UNLOADED.');
}
//Now to remove this with the click of a button, assuming the buttons name is button_mc
button_mc.addEventListener(MouseEvent.MOUSE_DOWN, onButtonDown, false, 0, true);
function onButtonDown(e:MouseEvent):void
{
loader.unload();
loader.contentLoaderInfo.removeEventListener(Event.UNLOAD, onLoaderUnload);
//When you want to remove things completely from memory you simply set their value to null.
loader = null;
button_mc.removeEventListener(MouseEvent.MOUSE_DOWN, onButtonDown);
}
I do hope that this was helpful, and I am sorry if it was redundant, but without seeing your code I have no way of knowing exactly how you approached this.

Resources