I am currently in a bit of a struggle. I have been coding a basic social media app for quite a while now, and the images are loading slowly. I am using picasso to load images from my firebase storage and it is quite slow.
When the app is started the posts that I have loaded into a recyclerview only load when I scroll over them, but they load relatively fast. As I do more stuff, switching activities etc my images will load slower and slower until they won't load and I am stuck with my placeholder image.
It is not only the recyclerviews because I have a settings activity to display a user's credentials, and I get the same slow loading image. Here is some code.
UserRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.hasChild("username")){
String Username = dataSnapshot.child("username").getValue().toString();
username.setText(Username);
}
if (dataSnapshot.hasChild("profileimage")){
String image = dataSnapshot.child("profileimage").getValue().toString();
Picasso.get().load(image).placeholder(R.drawable.user_circle2).into(profileImage);
}
}
The text loads instantly, no matter how much activity the user has on the app, but the images continually load slower.
Please can someone help, or suggest an alternate method for quickly displaying images, Thanks.
EDIT:
Thanks from Doug Stevenson for telling me to remove listeners, would I do it like this?
eventListener = UserRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.hasChild("username")){
String Username = dataSnapshot.child("username").getValue().toString();
username.setText(Username);
}
if (dataSnapshot.hasChild("profileimage")){
String image = dataSnapshot.child("profileimage").getValue().toString();
Picasso.get().load(image).placeholder(R.drawable.user_circle2).into(profileImage);
endListener();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
private void endListener() {
UserRef.removeEventListener(eventListener);
}
UPDATE:
I have added the remove listeners to the app.
I was debugging this on my phone, and of course I got the slow images as mentioned before, I tried this on an emulator though and the speed of the image loading stayed the same, and I could switch activities and even after I did quite a few things, the images were all loading ok when I switched back to my posts. Any ideas why???
I am also now getting the following message in the logcat:
Starting a blocking GC Alloc
05-07 19:28:20.493 15034-16164/com.sender.hp.sender I/art: Alloc concurrent mark sweep GC freed 3(72B) AllocSpace objects, 0(0B) LOS objects, 3% free, 494MB/510MB, paused 384us total 32.059ms
05-07 19:28:20.493 15034-16164/com.sender.hp.sender W/art: Throwing OutOfMemoryError "Failed to allocate a 48771084 byte allocation with 16777216 free bytes and 17MB until OOM"
I am now thinking that my images are eating up the memory, causing the slow image display and explaining why the text can still appear easily yet the images can't. I am going to try to compress the images before I add them to firebase.
While it's impossible to know for certain with the information here exactly what is causing slowness, it's almost certain that adding event listeners without removing them when they're no longer needed will cause problems over time.
If you don't remove a listener when it's no longer needed, it will continue to receive changes that occur at the location where it's listening. In your case, that also means Picasso will continue to load images. Every time you add a listener like this without removing it, all the prior listeners will all still receive results and load images. I can only imagine that repeating this many times will quickly consume the available network bandwidth on a device with a mobile data connection.
Always be sure to remove listeners that you don't need any more.
Related
In the new Android Vitals section in the console I'm getting warnings about more than 60% of sessions being affected by slow UI render times (missed Vsync: 1.02%, slow UI thread: 14.29%, slow draw commands: 96.84%). I've turned on GPU profiling on my test device (using the production version of the app) and I'm seeing the following TextView update causing render times well over 16ms (around 24-30ms):
updateTimer = new Timer();
updateTimer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
runOnUiThread(new Runnable() {
#Override
public void run() {
timeLeftView.setText(timeLeftString);
}
});
}
}, 100, 500);
When I comment out the textView update, nothing is being changed on the screen and profiler doesn't create any new bars.
One clue is that when opening the activity with the timer, the first 3-4 updates of the timer have rendering at about 8ms but then they rise to around 24-30ms.
Another clue is when I touch any part of the screen, the render times drop back to around 8ms for a few seconds before they shoot up again to 24-30ms. When I stop touch, the render times drop back again for a few seconds before they shoot up again.
So what I'd like to know is:
Is this normal for such a simple TextView update to cause high render times?
Is this what's messing up my Android vitals? Because it runs at only twice a second. Could the problem be elsewhere? The above code is the only thing that's creating high bars in GPU profiling, the other elements of the app work fine, long listviews with multiple textviews and images have rendering times of around 8ms.
What can I do to reduce these draw times? I've tried removing the centering and gravity in the layout for the TextView, as well as wrap_content (as suggested in another answer) but neither have any effect. Apart from that, I'm unsure what to do.
If you put a lot of layers in your xml it will force android to render multiple times ( if you have a lot of layers, refact your code!! ).
I strongly recommend this reading : https://developer.android.com/training/improving-layouts/index.html
About render the TextView multiple times, the speed of the rendering depends of the device you are running your application!
Tried pretty much every suggestion.
Finally solved it by increasing the frequency of the runnable from 500ms to 50ms or shorter. The problem was that the low frequency of the runnable let the CPU/GPU go to a low power state so draws took longer. By increasing the frequency of the runnable and the draws, the CPU/GPU doesn't go into low power state and frames are drawn much faster. Yes, it's more taxing on the battery but not as much as the screen being on in the first place. No users have complained either way and Android vitals are happy now.
Besides, looking at how default/official apps from device manufacturers work (including from Google itself), this is exactly how they handle TextView updates. Google's clock app for example (countdown timer, not stopwatch) updates the TextView ~60 times a second even though once a second would be all that's needed and most frugal.
I had a user comment that after viewing a bunch of images in my app, it crashes (he believes that it is due to out of memory error). I have the following relevant code:
int themeID = mNav[mPos];
String icon = getThemeData(DbAdapter.KEY_ICON, themeID);
ImageView viewer = (ImageView)findViewById(R.id.viewer);
Bitmap bMap = null;
try {
bMap = getJPG(icon + ".jpg");
} catch (IOException e) {
e.printStackTrace();
}
viewer.setImageBitmap(bMap);
That gets reran as the user flips between images. From here I see that you should call recycle() on bitmaps. Do i need to call it on bMap after setting the image? Or is there some way to pull it from viwer prior to setting the next one?
According to the documentation for recycle (if I call it on bMap) it appears I don't need to use it: This is an advanced call, and normally need not be called, since the normal GC process will free up this memory when there are no more references to this bitmap.
If you need to explicitly call recycle() it probably means that you have memory leak. Calling it is almost never a solution.
Did you try to check your app for potential mmory leak?
To check it you can for example rotate your device a few times and check how the Garbage Collector behaves. You should have something like GC_... freed 211K, 71% free 300K/1024K, external 0K/0K, paused 1ms+1ms in your LogCat nearly every time you rotate. Watch for changes in this part: 300K/1024K. If you don't have memory leaks, the first part should grow and then get smaller after a few GCs. If you have a memory leak, it will grow and grow, to the point of OOM error.
Check out my other answer about a memory leak.
If you're sure you don't have a leak and you're operating on Honeycomb you can increase the heap size accessible for your app like this: android:largeHeap="true" but it's only recommended when you deal with some huuuge bitmaps or videos, so don't overuse it.
i have an Android app that displays alot of images, it works, the images are gatherd from an url, added to a que and gathered by 4 threads,stored in a cache and then displayed in a listview view 4 images for row, there are abot six rows at each time on the screen. There is a total of usually 90 images.
The rows(and imageviews) are always recycled, so the amount of items is always the same and i'm not initializing anything.
This seems to work quite fine, i have always an average used heap size of 13MB.
The problem i have is that at the beginning mi max heap size is quite small and i get GC messages like:
01-20 16:48:39.191: D/dalvikvm(9743): GC_FOR_ALLOC freed <1K, 31% free 12048K/17351K, paused 25ms
but the more i scroll up down the view the heap size grows more and more untile i get things like
01-20 17:02:05.339: D/dalvikvm(11730): GC_FOR_ALLOC freed 544K, 72% free 13871K/49159K, paused 35ms
as you see even if the used is the same the maximum is increased even if i never got to that limit. and the true problem is that at this point i start to get outofmemory errors.
Can someone explain me what's wrong?
Thanks!
What version of Android are you using? If you're testing on pre 3.0 (ie 2.x), the byte arrays that store most of the information in Bitmaps are allocated and stored in native memory. This means that in heap dumps and in the GC notifications, you only see the small amount of memory used for pointers in Bitmaps, rather than the actual size.
For more information check out this google IO talk on memory management and detecting memory leaks: http://www.youtube.com/watch?v=_CruQY55HOk
Also I've worked on several apps doing similar things. My guess is that either your cache size is way too large, or (more likely) the images you're displaying and storing in the cache are much larger than the size you actually want. If you display a bitmap in an image view, the imageview will store the original bitmap in memory, even if it is significantly larger than what would actually fit in the view. Try resizing the images from disk to at least closer to the appropriate size before trying to display them: How do I scale a streaming bitmap in-place without reading the whole image first?
To cache my Images I use Map<String, Drawable> drawableMap. On a OutOfMemoryError I call this function:
private void cacheLeeren()
{
int size = drawableMap.size();
int del = (int) (size * 0.3);
Set<String> s = drawableMap.keySet();
for (String t : s)
{
if (del >= 0)
{
drawableMap.put(t, null);
del--;
}
}
}
I think it's not the best way...but it works ;-)
My guess is that your app reaches a very high peak of memory usage for a short time. It's true that on average you only use 13MB but if your heap grows to as much as 50MB, it means that momentarily you've consumed much more memory than you're thinking.
Let's try to figure out where this is happening. You've mentioned that you're using an LRU cache. This cache frees memory as soon as it fills up. My guess is that you're starting to free memory too late, and this memory isn't freed immediately - since it depends on the system GC. Whenever you're freeing some items from the cache, try to call System.gc() manually.
You've also mentioned that you're calling Bitmap.recycle(). To the best of my knowledge this is useless on Android 3+ because the native heap is no longer used for bitmaps. Since all bitmaps are on the dalvik heap, they will be freed by the GC.. You can't rush this like before unless you call System.GC() yourself.
Another idea for your source of problems is heap fragmentation. See my previous SO answer to a similar issue in this question.
I get a clasical "VM budget excedees memory - out of memory" type error crash report from the Android Market.
I checked the app for memory leaks over and over again. This error happens on a very small percent of total application installs, around 1-2% and it always happens on start-up. The app loads some bitmaps from internal memory for each activity, but does not crash on most devices. I thought all applications had a guaranteed minimum stack size for bitmaps so this should work for every device. Min SDK is 7.
Any reason why ? Does this sound familiar to anyone ?
I had quite a similar problem, and my images were simply too big for some devices.
I have read that you have one image per Activity and I guess this happens when switching from one to another as the newly allocated Drawable cannot fit. What you could do, to save some memory, would be to unload the background image of the Activities that are not shown:
#Override
protected void onResume() {
super.onResume();
Drawable d = loadMyDrawableFromDisk();
setBackgroundDrawable(d);
}
#Override
protected void onPause {
setBackgroundDrawable(null);
super.onPause();
}
It may help as the memory will be freed a few after onPause() is called, and not when the underlying View of your Activity will be unallocated by the system.
I'm having some trouble understanding why this code
public class BitmapAllocTest extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
byte[] b = new byte[20 * 1000 * 1000];
b = null;
Bitmap.createBitmap(2500, 2000, Bitmap.Config.ARGB_8888);
}
}
throws an OutOfMemory exception on a device with a 24mb heap limit. If I comment out either of the allocations it runs fine. I was under the impression that the java vm would try to garbage collect before throwing OutOfMemory exceptions.
I suspect it having to do with android allocating the bitmaps on the native heap.
I posted this on the issue tracker and got this answer:
There are a couple of things going on.
The VM on older devices uses
conservative collection. Most (but
not all) devices running >= 2.0 will
use type-precise GC, but none of them
yet have live-precise GC.
What this means is, the fact that you
set "b = null" doesn't guarantee that
all copies of that reference are gone
-- a copy might still be sitting in a register somewhere, and without
liveness detection the GC can't know
that it will never be used again.
It's also perfectly legal for the
compiler to discard the "b = null"
assignment since you never look at "b"
again.
Bitmap pixel data uses the magical
"external allocation" mechanism rather
than the usual heap allocator.
Sometimes you get unpleasant
interactions.
We're working on fixing all of these
issues.
Link: http://code.google.com/p/android/issues/detail?id=10821
I was under the impression that the java vm would try to garbage collect before throwing OutOfMemory exceptions.
You have to trigger the GC by yourself and retry. I had to do that recently and couldn't figure out another way to do that.