commons-app/apps-android-commons

[Bug]: App should be reliably usable on low memory devices

Opened this issue · 2 comments

Summary

Our app used to function decently well on low memory devices. Over time, it has become severely unreliable on such devices. I have a low memory device (1GB RAM) and test the app in it from time to time. I was testing the v5.1.0 release candidate on the device. The app crashed very often with a OOM exception. It crashed when trying to view an image, when trying to initiate an upload (specifically after picking the image) etc. The images weren;t that huge. The app crashes even for images of size around 2.5MB.

We need to work on identifying the root cause and figure out how to handle this gracefully rather than ending up with OOMs.

Steps to reproduce

  1. Install app on a low-memory device.
  2. Try to use standard functionalities of the app like upload, viewing contributions
  3. Observe if the app functions smoothly.

Expected behaviour

App works fine without issues on low memory devices.

Actual behaviour

App crashes often on low memory devices.

Device name

Samsung J1 Ace (J111F; 1GB RAM)

Android version

Android 5.1

Commons app version

main

Device logs

One such OOM exception when trying to initiate an upload. Specifically after selecting the image.

java.lang.OutOfMemoryError: Failed to allocate a 48000012 byte allocation with 4194304 free bytes and 33MB until OOM
  at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
  at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
  at android.graphics.BitmapFactory.decodeStreamInternal(BitmapFactory.java:863)
  at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:839)
  at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:488)
  at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:526)
  at android.graphics.drawable.Drawable.createFromPath(Drawable.java:1207)
  at android.widget.ImageView.resolveUri(ImageView.java:798)
  at android.widget.ImageView.onMeasure(ImageView.java:895)
  at android.view.View.measure(View.java:18926)
  at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:728)
  at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:464)
  at android.view.View.measure(View.java:18926)
  at androidx.viewpager.widget.ViewPager.onMeasure(ViewPager.java:1638)
  at android.view.View.measure(View.java:18926)
  at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:728)
  at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:464)
  at android.view.View.measure(View.java:18926)
  at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5908)
  at android.widget.FrameLayout.onMeasure(FrameLayout.java:436)
  at androidx.appcompat.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:145)
  at android.view.View.measure(View.java:18926)
  at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5908)
  at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1435)
  at android.widget.LinearLayout.measureVertical(LinearLayout.java:721)
  at android.widget.LinearLayout.onMeasure(LinearLayout.java:612)
  at android.view.View.measure(View.java:18926)
  at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5908)
  at android.widget.FrameLayout.onMeasure(FrameLayout.java:436)
  at android.view.View.measure(View.java:18926)
  at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5908)
  at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1435)
  at android.widget.LinearLayout.measureVertical(LinearLayout.java:721)
  at android.widget.LinearLayout.onMeasure(LinearLayout.java:612)
  at android.view.View.measure(View.java:18926)
  at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5908)
  at android.widget.FrameLayout.onMeasure(FrameLayout.java:436)
  at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:3076)
  at android.view.View.measure(View.java:18926)
  at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2411)
  at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1429)
  at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1677)
  at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1314)
  at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7057)
  at android.view.Choreographer$CallbackRecord.run(Choreographer.java:829)
  at android.view.Choreographer.doCallbacks(Choreographer.java:606)
  at android.view.Choreographer.doFrame(Choreographer.java:576)
  at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:815)
  at android.os.Handler.handleCallback(Handler.java:739)
  at android.os.Handler.dispatchMessage(Handler.java:95)
  at android.os.Looper.loop(Looper.java:145)
  at android.app.ActivityThread.main(ActivityThread.java:6934)
  at java.lang.reflect.Method.invoke(Native Method)
  at java.lang.reflect.Method.invoke(Method.java:372)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)

Another such OOM exception when trying to upload

java.lang.OutOfMemoryError: Failed to allocate a 192000012 byte allocation with 4193584 free bytes and 114MB until OOM
  at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
  at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
  at android.graphics.BitmapFactory.decodeStreamInternal(BitmapFactory.java:863)
  at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:839)
  at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:488)
  at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:526)
  at android.graphics.drawable.Drawable.createFromPath(Drawable.java:1207)
  at android.widget.ImageView.resolveUri(ImageView.java:798)
  at android.widget.ImageView.onMeasure(ImageView.java:895)
  at android.view.View.measure(View.java:18926)
  at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:728)
  at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:464)
  at android.view.View.measure(View.java:18926)
  at androidx.viewpager.widget.ViewPager.onMeasure(ViewPager.java:1638)
  at android.view.View.measure(View.java:18926)
  at android.widget.RelativeLayout.measureChildHorizontal(RelativeLayout.java:728)
  at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:464)
  at android.view.View.measure(View.java:18926)
  at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5908)
  at android.widget.FrameLayout.onMeasure(FrameLayout.java:436)
  at androidx.appcompat.widget.ContentFrameLayout.onMeasure(ContentFrameLayout.java:145)
  at android.view.View.measure(View.java:18926)
  at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5908)
  at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1435)
  at android.widget.LinearLayout.measureVertical(LinearLayout.java:721)
  at android.widget.LinearLayout.onMeasure(LinearLayout.java:612)
  at android.view.View.measure(View.java:18926)
  at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5908)
  at android.widget.FrameLayout.onMeasure(FrameLayout.java:436)
  at android.view.View.measure(View.java:18926)
  at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5908)
  at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1435)
  at android.widget.LinearLayout.measureVertical(LinearLayout.java:721)
  at android.widget.LinearLayout.onMeasure(LinearLayout.java:612)
  at android.view.View.measure(View.java:18926)
  at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5908)
  at android.widget.FrameLayout.onMeasure(FrameLayout.java:436)
  at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:3076)
  at android.view.View.measure(View.java:18926)
  at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2411)
  at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1429)
  at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1677)
  at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1314)
  at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7057)
  at android.view.Choreographer$CallbackRecord.run(Choreographer.java:829)
  at android.view.Choreographer.doCallbacks(Choreographer.java:606)
  at android.view.Choreographer.doFrame(Choreographer.java:576)
  at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:815)
  at android.os.Handler.handleCallback(Handler.java:739)
  at android.os.Handler.dispatchMessage(Handler.java:95)
  at android.os.Looper.loop(Looper.java:145)
  at android.app.ActivityThread.main(ActivityThread.java:6934)
  at java.lang.reflect.Method.invoke(Native Method)
  at java.lang.reflect.Method.invoke(Method.java:372)
  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199

Screen-shots

No response

Would you like to work on the issue?

None

Leak Canary was reporting many Memory Leaks. Fixing memory leaks might reduce memory usage overall and improve performance on low-RAM devices.

Even on a Pixel 9 Pro the app quickly becomes unusable and ends up being kicked out of memory when I switch to another map to double-check something. This started happening recently.