Oct 18 | 3:55 PM |
Mark M. | has entered the room |
Mark M. | turned on guest access |
Oct 18 | 4:00 PM |
EGHDK | has entered the room |
Mark M. |
hello, EGHDK
|
Mark M. |
how can I help you today?
|
EGHDK |
Hey Mark, so I have gotten pretty far with the chat bubble.
|
EGHDK |
It works really really well. There are two things I'm still having trouble with. I'm not sure if you can help but I'll give it a go.
|
EGHDK |
So, my chat bubble is a path with 7 points. I want to round only 4 points out of the 7. How would I go about that?
|
Mark M. |
um, what's a "point"?
|
EGHDK |
I know I can do `paint.setPathEffect(new CorenerPath...` but that rounds all of the points.
|
Mark M. |
I have never used the Canvas API
|
Mark M. |
at least, for anything other than blitting a bitmap onto the screen
|
Mark M. |
certainly none of the line or text drawing APIs
|
EGHDK |
Hrm... alright.
|
EGHDK |
Second is... I want to make sure I'm drawing efficiently. Like... my code works... but I don't know if it's "right". Ya know?
|
Mark M. |
Use Traceview to determine how much time you are spending
|
Oct 18 | 4:05 PM |
Mark M. |
if your onDraw() implementation is a couple of hundred microseconds per invocation, you'll probably be fine, unless you were planning on lots of chat bubbles
|
EGHDK |
Possibly.
|
EGHDK |
Here's a pastebin. I was just wondering if you could take a look to see if there is any big rules I'm breaking. http://pastebin.com/itXy85pq
|
Mark M. |
cache your Paint object
|
Mark M. |
you should be able to initialize that in the constructor
|
Mark M. |
rather than creating new Paint objects every onDraw() call
|
Mark M. |
you may be able to cache your Path object too, clearing out the old data and reusing the instance rather than creating new instances on each onDraw() call
|
Oct 18 | 4:10 PM |
Mark M. |
basically, try to avoid memory allocations where you can, since onDraw() gets called a fair bit
|
EGHDK |
Okay, so try not to put object creation in onDraw()?
|
Mark M. |
(BTW, reset() on Path seems to clear its contents, if I am reading the docs correcty)
|
Mark M. |
er, correctly
|
Mark M. |
where possible, yes, avoid object creation in onDraw()
|
Mark M. |
so, for example, I'd also see if you can get rid of the Point stuff
|
Mark M. |
go with int x[] and int y[] to avoid those allocations
|
EGHDK |
How would you draw without points?
|
Mark M. |
you're not using the Points
|
Mark M. |
more specifically, you are not passing the Points to anyone
|
Mark M. |
they are purely your own bookkeeping with the method
|
Mark M. |
and you are only using them for tracking X's and Y's
|
Mark M. |
which you can track via two int arrays, instead of an array of Point objects
|
EGHDK |
Okay, so two seperate int array would be smaller in size than point objects?
|
Mark M. |
more to the point (pun lightly intended), you avoid creating extra garbage
|
Mark M. |
particularly since your arrays can be precomputed in onSizeChanged(), as they depend only upon your width and height
|
Oct 18 | 4:15 PM |
EGHDK |
How do you know that a Point object is more garbage than an array?
|
Mark M. |
first, you are comparing apples and oranges
|
Mark M. |
you do not have one Point
|
Mark M. |
you have an array of Points
|
Mark M. |
hence, the proper comparison is between an array of Points and two arrays of int
|
Mark M. |
and from a memory consumption standpoint, it's a wash
|
Mark M. |
however, an int is not an object, and therefore has no constructor
|
Mark M. |
by making your arrays be data members, and setting their contents in onSizeChanged(), you generate no new garbage on each onSizeChanged() or each onDraw()
|
Mark M. |
your current implementation creates a seven-element Point array, creates seven Point objects, and tosses it all in the garbage at the end of onDraw()
|
Mark M. |
so that's seven constructor calls and eight total allocations per onDraw()
|
EGHDK |
Interesting take on that. I haven't thought of it in that way before. Thanks Mark.
|
Oct 18 | 4:20 PM |
EGHDK |
I did pick up that head first into Java book. It's pretty good.
|
EGHDK |
About 15% of the way through. Learned a lot of stuff that I knew already, but learned more about some basics like heap and GC.
|
Oct 18 | 4:25 PM |
Mark M. |
yup, a firm grounding in Java will go a long way towards making Android app development a bit less painful
|
EGHDK |
Yep, learning the basics of Java programs is pretty funny.
|
EGHDK |
I now finally know why (String[] args) is there for main. To pass a parameter for a cmd line java program. Never would've guessed it.
|
Mark M. |
if you have another question, go right ahead
|
EGHDK |
Since I'm here, I guess I'll ask as I just asked at #android-dev, but wasn't fulfilled with the answer. What is a leak/memory leak? Why is it bad.
|
Mark M. |
you learned what the heap is in the book, right?
|
Mark M. |
("the book" being _Head First Java_)
|
Oct 18 | 4:30 PM |
EGHDK |
Yep!
|
Mark M. |
the heap space available to a Java program is limited
|
EGHDK |
Especially in Android right?
|
Mark M. |
correct
|
Mark M. |
a memory leak, in a garbage-collected language like Java, is when you have an object that you are no longer using, but cannot be garbage collected
|
Mark M. |
that object's memory is tied up and cannot be reused
|
Mark M. |
this basically subtracts that object's memory out of your available heap space
|
EGHDK |
Oh no! That's not good. It'll keep building up till it's killed by the OS.
|
Mark M. |
correct, or you may get an OutOfMemoryError
|
Mark M. |
OutOfMemoryError, in Android, occurs when you try to create a new object (or array of primitives) and there is insufficient memory for your request
|
Mark M. |
there are two main culprits for this
|
Mark M. |
1. memory leaks, tying up heap space
|
Mark M. |
2. memory fragmentation
|
Mark M. |
remember how I suggested that you avoid creating all those Point objects earlier in this chat?
|
EGHDK |
Yeah
|
Mark M. |
part of the reason for that was speed, to avoid extra constructors and extra CPU time doing garbage collection
|
Mark M. |
part of the reason was to cut down on memory fragmentation
|
Mark M. |
in the beginning, when your process starts up, you have one huuuuuuuuuuuuuge block of memory that is your heap
|
Mark M. |
as you start creating objects (and arrays of primitives), you consume that heap
|
Mark M. |
but, as you let go of those objects, they can be garbage-collected, returning their memory to the heap... sorta
|
Oct 18 | 4:35 PM |
Mark M. |
the "sorta" is that you no longer necessarily have the one huuuuuuuuuuuuuuuuuge block of memory anymore
|
Mark M. |
you have lots of smaller blocks
|
Mark M. |
normally, this is not a problem, as the objects we create tend to themselves be fairly small
|
EGHDK |
Oh... so garbage collection causes the fragmentation?
|
Mark M. |
however, bitmaps become an issue
|
Mark M. |
allocation causes the fragmentation
|
Mark M. |
garbage collection *in Android* does not completely clear up the fragmentation
|
Mark M. |
(garbage collection in standard Java does clear up the fragmentation, through what is known as a "compacting garbage collector", that Android lacks)
|
Mark M. |
bitmaps, under the covers, are large byte arrays (byte[])
|
Mark M. |
and therefore they represent decent-sized chunks of heap space individually
|
EGHDK |
Gotcha.
|
Mark M. |
*if* your heap becomes sufficiently fragmented, you can get an OutOfMemoryError when trying to create a bitmap (e.g., load a PNG off of disk), not because you are truly out of heap space, but that there is no contiguous block of heap big enough for the byte array
|
EGHDK |
OH man. That makes sense.
|
EGHDK |
So how do you fix that?
|
Mark M. |
the fewer objects you create in frequently-called methods (like onDraw() of a View), the less you contribute towards this fragmentation
|
Mark M. |
also, there are special tricks for working with bitmaps that can reduce this problem, such as maintaining your own pool of byte arrays for reuse
|
Oct 18 | 4:40 PM |
Mark M. |
it's the sort of thing that, if you know that you are writing an app that will be prone to this sort of problem (e.g., photo album), you tend to try to deal with it up front
|
Mark M. |
for more general-purpose apps, you tend to not worry about memory fragmentation a ton, in hopes that it never proves to be an issue
|
EGHDK |
Yeah. That clears a lot of things up. So, memory leaks and memory fragmentation. How do I fix memory leaks?
|
EGHDK |
Should I always null out an object when I'm done with it?
|
Mark M. |
no, that is not necessary
|
Mark M. |
Step #1: be very careful when declaring static data members
|
Mark M. |
they represent intentional leaks, as it were
|
EGHDK |
I do that a lot... that means that they're stuck in memory forever?
|
EGHDK |
Oh man. I do do that a lot.
|
Mark M. |
where "forever" = "until the process is terminated", yes
|
EGHDK |
But something like Toast.LENGTH_LONG is a static data member?
|
Mark M. |
yes, but that's a constant
|
Mark M. |
specifically an int
|
Mark M. |
it leaks 4 bytes
|
Mark M. |
not a big deal
|
Mark M. |
static final String KEY_THINGY="foo"; leaks 6 bytes
|
Mark M. |
so don't worry about static final primitives and strings
|
EGHDK |
True. Okay, so what about when an activity finishes. Does all of that stuff get put up for garbage collection?
|
Mark M. |
it depends on the "that stuff"
|
Mark M. |
anything not referenced by a static data member will eventually get garbage collected
|
Oct 18 | 4:45 PM |
Mark M. |
so, usually, your widgets and OnClickListeners and such will all get garbage collected once the activity is destroyed
|
EGHDK |
Wouldn't it be smart to force garbage collection sometimes?
|
Mark M. |
that's usually not necessary and is rarely helpful
|
Mark M. |
bear in mind that Android uses an incremental garbage collector specifically to reduce load on the CPU
|
Mark M. |
it will catch up in time
|
Mark M. |
but do worry about static Customer heyIWantToBeAbleToGetAtThisFromAnywhere=new Customer();
|
Mark M. |
(for any arbitrary Java class like Customer)
|
Mark M. |
if Customer can point to lots of stuff -- like, say, other Customers -- that could wind up being a big memory leak
|
Mark M. |
there's a chapter in my book on using the Memory Analysis Tool (MAT) to help diagnose these sorts of leaks
|
EGHDK |
Yeah, that makes sense. Or if you have a bitmap that's a static. As well.
|
EGHDK |
Okay, I think I will have to read up on that. It's a pretty interesting topic. I just want to make sure I'm getting rid of my objects properly.
|
Mark M. |
or a static cache of bitmaps, to save you reloading them from disk or the network
|
EGHDK |
But I won't go around and start "nulling" everything.
|
Mark M. |
every now and then you will see a code sample that shows setting a variable or something explicitly to null
|
Mark M. |
admittedly, that may slightly increase the speed at which that object can get garbage collected
|
Mark M. |
it's the sort of thing where, if you see Google do it, it's probably a good idea
|
Mark M. |
but if you see J. Random Blogger do it, take that part of the advice with a grain of salt
|
EGHDK |
Duly noted.
|
Oct 18 | 4:50 PM |
EGHDK |
So let's say I have a bitmap static variable, but now I know 100% I don't need it. How would I get rid of it, so I can get that heap back?
|
Mark M. |
*that's* when you set things to null
|
Mark M. |
so if you have static Bitmap avatar; somewhere
|
Mark M. |
where at some point you assign a Bitmap to avatar
|
Mark M. |
that Bitmap is intentionally leaked
|
Mark M. |
to de-leak it, and let it be garbage collected, set avatar to null
|
Mark M. |
my point about not setting things to null was more for garden-variety stuff, like local variables in a method
|
EGHDK |
Yeah. Okay, this is starting to make more and more sense now.
|
Mark M. |
particularly for things like bitmaps, generally try to use some tested library that will handle the caching for you
|
Mark M. |
e.g., Picasso, as covered in the Internet chapter of my book
|
EGHDK |
I didn't know that a heap gets fragmented. That's important to know.
|
EGHDK |
Yeah, JakeWharton was talking to me about mem leaks.
|
Mark M. |
Jake at least had a hand in Picasso, and perhaps wrote it completely
|
EGHDK |
Becuase he said that it works in between activities, which is why you would never want a StrongReference.
|
EGHDK |
Because he said a view with a strong reference will keep the entire activity lingering around.
|
Mark M. |
correct
|
EGHDK |
Someone was complaining about WeakReference.
|
Mark M. |
there are ways in the Java language to "conditionally" hold onto objects
|
Oct 18 | 4:55 PM |
Mark M. |
WeakReference and SoftReference are the two common ones
|
Mark M. |
they tend to be used for cache implementations
|
EGHDK |
Yeah, I haven't really ever seen them used. I saw something similar with Obj C in iOS.
|
Mark M. |
if there are no references to an object, other than by WeakReference or SoftReference, the object can be garbage-collected
|
EGHDK |
Wait... can you rephrase that?
|
Mark M. |
let's suppose we have a Bitmap
|
Mark M. |
we loaded that Bitmap off of disk via BitmapFactory
|
EGHDK |
Yep
|
Mark M. |
in our load-the-Bitmap method, we put it into an ImageView
|
Mark M. |
now the ImageView has a reference to the Bitmap, so the Bitmap cannot be garbage-collected until the ImageView is
|
Mark M. |
that's a strong reference
|
Mark M. |
however, loading bitmaps off of disk is a relatively expensive operation, in terms of disk I/O and CPU time
|
Mark M. |
and we might need that Bitmap again in the future
|
Mark M. |
we could have a HashMap<String, Bitmap> as a static data member somewhere, and put the Bitmap in there, keyed by its path
|
Mark M. |
that way, in the future, we could try to retrieve the Bitmap from the HashMap, and reuse it if we find it
|
Mark M. |
if it is not in the HashMap, we have not referenced this Bitmap before, so we load it from disk
|
Mark M. |
however, a HashMap also is a strong reference
|
Mark M. |
so as we keep adding Bitmaps to the HashMap, our memory leak grows and grows
|
Mark M. |
until eventually we get in trouble
|
EGHDK |
Hmm... alright. Any other specific memory sections in your book?
|
Oct 18 | 5:00 PM |
Mark M. |
there are two main solutions to this problem: limit how many things you put in the HashMap, or use weak or soft references
|
Mark M. |
there's a couple chapters on memory management, including that MAT chapter
|
EGHDK |
View paste
|
Mark M. |
pages 2249 through 2274 in the book (Version 5.2)
|
Mark M. |
and that's a wrap for today's chat
|
Mark M. |
the transcript will be posted to http://commonsware.com/office-hours/ shortly
|
Mark M. |
no chats next week due to travel
|
Mark M. |
next one is 10am Eastern on October 29th
|
EGHDK |
Thanks
|
Mark M. |
have a pleasant day!
|
EGHDK |
You too.
|
EGHDK | has left the room |
Mark M. | turned off guest access |