NativeScript: [Android] Production app crashing in runtime
I was asked by the NativeScript-team to post my issue here, so here it goes. Thanks in advance.
On February 26th we published a new version of both our Android and iOS apps to the app stores. The published apps are based on {N} v2.4. The issues we are having are only on Android.
We use Google-Analytics innside the app, so we try to log exceptions this way. But this only logs the first line of the exception, for some reason.
Still, we see an outrageous increase in the amount of crashes logged. Since the release date and to this date, there are in total 2240 logged exceptions/crashes.
This is just a part of them. There are more, and those are all OutOfMemoryError exceptions (around 400 of them): http://i.imgur.com/lf7Z95h.png
The problem we are having is that (of course) we are struggling to reproduce many of these. The one we are able to reproduce is the OutOfMemoryError. It happens at random, just tapping around the app, opening/closing different views. Our app is image-rich, but the images are not bigger than 200kb. We also use a image-caching plugin called nativescript-web-image-cache.
One other exception, IllegalStateException (@ViewPager:populate:1167) {main} is probably caused by our own Carousel plugin. But we are still having a hard time to reproduce the issue locally, and are still looking into it. https://github.com/manijak/nativescript-carousel
We are using Genymotion as our main emulator for Android. And for devices we have several phones (Samsung , LG, Nexus) and also a high-end tablet (Nvidia Shield K1). The only exception we have managed to reproduce is still, OutOfMemoryError.
Luckily, we have some users who care, so they reported the crashes, so have have full stack-traces on some of them. I can provide those if needed.
Please, let me know if you need more information regarding the issue(s). I could also provide access to the source code if needed.
Apps are available here: Android: https://play.google.com/store/apps/details?id=ba.fkzeljeznicar iOS: https://itunes.apple.com/us/app/fk-željezničar/id1097417103?mt=8
About this issue
- Original URL
- State: closed
- Created 7 years ago
- Reactions: 3
- Comments: 42 (24 by maintainers)
Hey, @manijak we’ve managed to stably reproduce the no Weak Reference related issue and @Pip3r4o is currently looking at it. I will let you know if we have further update on that.
Meanwhile some key notes regarding how to improve your application performance and also eliminate the out-of-memory issues.(without the need to use GCs)
I’ve noticed that you are using Repeater in your partial-views (e.g. partial-articles-rest.xml and _partial-matches.xm_l and maybe on other places as well) and those partials are nested in your heaviest layout (home.xml) which is loaded in the first tab of your entry point file. Now notice that the main differences within ListView and Repeater are that the ListView is optimized and is using virtualisation and recycling of cells. The Repeater lacks both!. This said it is always good to use Repeater only for a small number of items when it will be expected for all items to be visible. For complex scenarios (like the home page of your application) the usage of Repeaters is the main reason for the increasing memory usage and out of memory problems. As a rule of thumb, I would suggest to always use ListView when creating heavy duty galleries like pages with a lot of dynamic content which will use images and data… This article explains why it is important to use ListView (in the context of Angular application but the same principle is 100 percent valid in NativeScript core application) and also explains how virtualization and recycling are helping to improve greatly the overall performance while decreasing the memory consumption. Suggested solution: I would suggest recreating your partials (and other pages where Repeater is used) with ListView.
Another technique I would recommend is to minimize the nesting of the layouts as much as possible. The more complex your nesting is the more resources your app will need and in the mobile world where some phones are still with limited hardware resources, this is important issue. Example from the code in your application: main-view.xml
can reduce the nesting and in the same time without chaning the actual UX
In the same page, you have also a GridLayout which contains the TabView which again can be reduced just to a TabView. Some pages like partial-view.xml can reduce the nested layouts greatly! The less nesting you have the fewer operations will be executed when your UI is recreated (e.g. like propagating the inherited CSS) and the faster your app will load while using minimum resources.
Some of the nested layouts are kinda hidden by the custom components (in your case partials). For example the page home.xml looks like this
But when opening one of the partial we see another empty stacklayout as a container. partial-sponsor.xml
Again the nested layout can be eliminated without sacrificing nothing from UX perspective. The rule of thumb should be to use as less as possible nesting - even the most complex layouts can be recreated with only a few levels of nesting
@manijak I have isolated the issue and can reliably reproduce it. I also managed to trace the cause of it, and am going to start working on a fix as soon as the holidays are over.
@manijak regarding the OOM issue from java.lang.OutOfMemoryError (dalvik.system.VMRuntime.newNonMovableArray).txt
As suggested by @NathanaelA and @Pip3r4o you can manually trigger garbage collector and also take advantage of the android flags set in app/package.json to trigger the cleanup of memory on some time interval. Keep in mind that the global.GC() will trigger the JavaScript garbage collector only and not the JAVA one. You can combine both tactics to force the cleanup in your project. e.g. open your application package.json (not the root one but the one located in your app folder) app/package.json
The best explanation what this will do can be found here
Also as @NathanaelA mentioned you can trigger the JavaScript GC on key points in your application (for example when the user is leaving a heavy-duty image page) with
After combining both techniques and monitoring the memory usage resulted taht the application memory has been now stable and is freed on some time interval. I’ve used adb dumpsys to check the memory usage.
I also think it has something to do with transitions or animations. I just re-added all my Repeaters, but removed animated transitions when navigating to other views.
The app did not crash once, it didn’t even OOM. App also ran smoother, faster. But it looks awful when switching views… Very interesting.
Regarding the RLV, thanks for the heads up. I own the pro/licensed version of nativescript-ui and it just doesn’t suit my needs for now. Not until each component is split into its own plugin. I do not want to bloat my app needlessly.
@NathanaelA - please allow me to disagree with you on that. The link that you have provided lists issues on the NativeScript UI’s public repository which people use for various reasons, many of which are simply handicaps that they have hit while developing but which were later resolved. They were not even bugs with the component. Other people report stuff and stop sharing information after some time, especially when it comes to reproducing a problem they report. They might simply have resolved it. We do not close all off the issues as they might be helpful for people having similar cases. It is, to say the least, pretty superficial to judge based on that. Moreover, some of the issues you see there are simply logged by us to inform people what we’re working/planning to work on for the near future. On top of that, many of the issues in the Feedback repository were migrated from the SDK Samples repositories by ourselves when we decided that we will stop using them for feedback from customers. They contain useful information and that is why we decided not to close them.
As for your experience with using RadListView - we have a couple of statements to make:
For example, our next major effort is enabling Grouping, Filtering and Sorting as it was previously postponed due to various reasons.
If you think there are still pending issues with RadListView that make it difficult to use, please let us know by providing enough information for us to reproduce.
@NickIliev - I would hope that manually calling
global.GC()doesn’t cause theNo Weak referror. It is used in the TNS core modules (which is why it is required to be exposed from the package.json).And even w/o using global.GC() my clients are hitting the
No Weak Referror because of internal GC’s occurring…@danielgek – the link seems to be fixed now.
@manijak
Here is what we have so far: Regarding the error log from com.tns.NativeScriptException (com.tns.Runtime.callJSMethodNative)_2.txt
This one appears to be caused by the ScrollView implemented in views/home/home.xml. To reproduce the crash open the app >> navigate to “more” >> minimize the app >> return back to the app >> the app will throw with this error (in debug mode) or will simply exit in production.
The reason for this to happen is because the scroll view is not yet initialized on view loaded event. I am guessing that the partials loaded inside causing this behavior as I was unable to reproduce it in a local project with the same scenario, however the fix was pretty easy - I’ve made some structural changes as follows: in your home.xml
Now that the ScrollView has guarantied single parent and is our root element there were also some minor changes in viewLoaded as follows: home.js
@NathanaelA here’s some more info in addition to what you said. It might be helpful to others https://docs.nativescript.org/runtimes/android/advanced-topics/memory-management
This looks like a transition listener that got prematurely collected:
com.tns.gen.android.transition.Transition_TransitionListener.onTransitionEnd@manijak as I mentioned earlier - Repeater may no longer be a problem when you upgrade your application to use the 3.0.0 bits. If upgrading everything to 3.0.0 is too big a step right now, you could use just the 3.0.0 runtime (it’s completely backwards compatible with 2.5 modules and CLI), which will solve the main GC problem. For the time being you could also remove/rewrite the views to not use a repeater.
@manijak I have made a PR(https://github.com/NativeScript/android-runtime/pull/744) that addressed part of the causes for the
No weak reference foundexceptions. Unfortunately I observed this faulty behavior when the Repeater view was used in your project. Upon navigating back from thearticles-pagefor example the crash is easy to reproduce. I believe it has something to do with the dependency properties used to store a reference to the parent view (stack-layout by default) getting out of scope at some point without strong reference to the Repeater javascript instance. I need a little more time to look into it.Edit: it came to my attention that
tns-core-modules@3.0rcand the upcoming 3.0.0 versions use a slightly different property system for their views, so it is possible that all related problems won’t be valid when you upgrade your project.Meanwhile, consider avoiding using the Repeater view, and if it’s inevitable - inflate layouts manually from code.
Thank you so much @NickIliev! This is what I was after. I wasn’t sure that some of my views were unnecessary complex (structure-wise). But it’s good to know, I’ll be doing some cleanup this Easter 😄
I had been testing with the ListView instead of Repeater in some places (not all, I admit). But had issues with height since the parent is most of the time a ScrollView (when mixing components, like
home.xml). Will have to figure out something here. 👍Props for finding out the Weak Reference Error! The third issue you found is actually a bug! Should definitely not do that. 🐛
I’ll be doing some refactoring in the coming days, will report back the results. Thanks again for your time. You guys are the best 🥇
@NickIliev article link is broken
@manijak for iOS you can trigger the GC following these instructions but you should use this carefully as it might lead to side effects. Overall in iOS memory usage problems are much harder to achieve so you won’t need to manually handle it.
As for the issue in
home.xml- I’ve noticed that the error is thrown at this line (the log shows the same line in the transpiled JS file but this is the corresponding one in the source TS file) so I’ve simply started to “turn off” all scroll views in the app until the error was no longer reproducible.As for the other error logs - I am currently unable to reproduce them at my side - only once I got a page transition problem but it was inconsistent and does not re-appear. If you come to a stable way to hit one of those please let me know.
@manijak can you please provide the stack trace logs. Access to the source code will also be of great help to investigate what is causing the memory leak. You can send me private message in the community slack (niki_iliev) or to my email (nikolay.iliev at progress com)
Hey Ned, I have found that doing
if (global.android) { global.GC(); }at certain key points helps a lot. The issue that I found with OOM’s is that the images fill up the Java memory side; and so there is no memory pressure in JavaScript in NS. So basically the Java memory runs out and no GC is triggered. 😭 But if I manually trigger a GC on exit of the large memory screens, it so far appears to help and deal with the issue.I can’t 100% say if this is the solution to OOM’s as my next version of the app with this code hasn’t been deployed to the wild; but I haven’t seen a OOM internally since doing this and I have been working on this app a lot…