Office Hours — Today, July 10

Tuesday, July 8

Jul 10
7:20 PM
Mark M.
has entered the room
Mark M.
turned on guest access
7:25 PM
Jeff G.
has entered the room
Mark M.
hello, Jeff
how can I help you today?
Jeff G.
Hi.
7:30 PM
Jeff G.
I've got a couple of questions. The first one is that I would like to know if there is a way to auto-increment version numbers that are listed in the manifest or gradle.build file.
Mark M.
there are recipes for doing so in build.gradle
I cover one in the Advanced Gradle chapter
Jeff G.
is that in the current version of your book or the one you're currently working on?
Mark M.
it's been there for a few months
Jeff G.
i have version 5.8
"Introducing Gradle" ?
Mark M.
the chapter begins on page 2193 "Advanced Gradle for Android Tips"
"Automating APK Version Information" starts on page 2206
Jeff G.
got it. Thanks.
My second question is a bit more complex. Another ListView-related question.
I am querying an SQLite database (on the device) that could potentially result in thousands of rows being returned. How do I determine the maximum I can retrieve before getting an out-of-memory error? At the moment I just set an arbitrary limit, and then perform an SQL COUNT query to let the user know how many total are available even though I'm not returning all of them at once.
I'm sure I'm not the only one to encounter this so I'd like to know some best practices for how this situation is best handled.
Mark M.
"How do I determine the maximum I can retrieve before getting an out-of-memory error?" -- there's no way to do that
7:35 PM
Mark M.
"I'd like to know some best practices for how this situation is best handled" -- try to avoid UIs where you might try presenting a ListView with thousands of rows
Jeff G.
In this case it's unavoidable as it is a client requirement.
"the story of my life"
:)
They have asked me to report the total available, which I do by performing a count and then displaying that in page's tab. They they process rows and as they are processed they drop off the list.
Before I was just using the size of the result set returned but that is not accurate if the query is larger than the max I am currently returning. That's why I do a separate count so as soon as they process a row they can see the total in the tab getting decremented.
Mark M.
but there is no way they are going to process thousands of rows
their battery will run out
or their finger will develop a blister
Jeff G.
lol.
it is a government app for internal use only. i try not to ask too many questions.
ideally they will gradually refine their search until they get just what they want.
Mark M.
you're welcome to come up with some sort of algorithm to determine how big of a query you're willing to try
Jeff G.
I am fortunate to be focused on just one device, so I have been able to estimate about how many rows are the max I can deliver before running out of memory.
Mark M.
the problem is that your answer will vary with time
at least, depending upon the nature of your rows
Jeff G.
well, I suppose I could catch the out of memory exception and gradually try requerying less and less, but for the most part I've been able to get away with 500 rows, but they have asked for 1000s.
7:40 PM
Jeff G.
they are just rows with about 5 textviews.
imagine a checklist where they have to review something in the real world, verify it, then check it off the list in the database.
Mark M.
is any of the text likely to be especially long?
Jeff G.
no.
Mark M.
I don't know enough of the innards of the query system to know where the memory pressure comes from
a Cursor with 5 short text columns should be able to handle a few thousand rows
but it may be that you're running into problems before the Cursor can get created
Jeff G.
generally speaking, is it best to always specify a LIMIT in one's query?
Mark M.
um, I can't really answer that
getting back to your problem, another thing you could try is MergeCursor
Jeff G.
how else do Android/SQLite developers avoid out-of-memory exceptions?
I'll have to look into MergeCursor. I don't know anything about it.
Mark M.
"A convience class that lets you present an array of Cursors as a single linear Cursor"
you'd do shorter queries and stitch them together
Jeff G.
I see.
Mark M.
whether you use MergeCursor directly, or create your own workalike that supports adding Cursors after the constructor, is up to you
but this would allow you to query in chunks
until you OOM or reach some acceptable overall cap
and, if your memory problems are coming from the initial processing of the result set, you may be able to use this to get more total results
7:45 PM
Jeff G.
I guess the idea is that I just want to be able to allow the user to seamlessly scroll and see all of the results. They don't necessarily have to have them all available in memory at the same time.
Mark M.
ListView doesn't really handle that use case well
Jeff G.
In fact, if there was a way to just get a cursor with the ones that immediately need to be displayed then that's great.
Yeah, I inherited this project. That being said, I don't know of a better solution.
Mark M.
the problem is that you don't know, at any point in time, what the user will do
you're one fling away from having no relevant data
and, again, this is why a ListView for this data volume is not a very good solution
Jeff G.
i have one tab where they set up the search criteria, and then the next tab displays the results.
Mark M.
I don't know if the new RecyclerView will help with this or not
Jeff G.
What other solutions are there?
Mark M.
don't show thousands of rows in the ListView
if the COUNT() query returns a value over some threshold, tell them they're going to get a subset
and they should try a better query
Jeff G.
that's what I'm doing now.
Mark M.
if possible, use SELECT DISTINCT to try to suggest to them possible query options that may help
Jeff G.
are there any other alternatives to listviews for displaying large "checklist" items?
Mark M.
your problem is the expectation of being able to scroll through all possible results
you're welcome to use a ListView, so long as you give them a flow that does not present thousands of rows at a time
Jeff G.
i was wondering if there was a way to "preload" the next batch but I think the swiping could potentially be too fast to handle that.
right now I present a subset, but display an actual count so they know what the total available are.
7:50 PM
Mark M.
and to me, that seems reasonable, since they're searching
Jeff G.
awesome.
I will look into MergeCursor and RecycleView though.
Mark M.
you could extend it a bit perhaps by using something else as means of indicating "please, sir, can I have some more?"
for example, forward/next buttons to advance through 500-row chunks
(or horizontal swipes via a ViewPager if you're not already using that in the tabs)
anything that lets you dump old results to free up memory for new results
EGHDK
has entered the room
Mark M.
hello, EGHDK
7:55 PM
Mark M.
EGHDK: do you have a question?
EGHDK
Hey Mark, so on Monday (or Tuesday?) I had a question pertaining to multi threading in Android. Mostly dealing with the error message "something something handler hasn't called looper.prepare()" You recomended that I read your book on it, but sadly after reading it... it hasn't resolved any of my questions.
I did learn a few things though, but not exactly pertaining to handler/looper
Mark M.
did you look at your Java stack trace to see what in your code is triggering the need for a Handler?
EGHDK
Well maybe that's where my problem is coming from.
I don't know what a handler is or a Looper... =/
Mark M.
do you know what a stack trace is?
EGHDK
Yes.
You taught me that about a year ago!
Mark M.
in the stack trace that you are getting, are references to your code showing up?
EGHDK
Yes.
That's definitely clear.
I've gotten the Handler/Looper.prepare() error before.
Mark M.
look at the stack trace, see what the highest line in the trace is that refers to your code
that is what is in your code that is triggering the need for the Handler
EGHDK
Usually when performing something UI related in not the main thread.
8:00 PM
EGHDK
Okay, so I don't have my code in front of me, but all I did was put Looper.prepare() in my method... and it didn't crash. Is that possible?
Mark M.
I doubt that whatever you are trying to do will actually work
"not crashing" is the minimum threshold for avoiding failure
it is not indicative of success on its own
whatever you are doing that is triggering the need for a Handler isn't looking for a Handler because it's bored
it's looking to use a Handler for some specific reason
and a not-really-set-up Looper will not result in a Handler that can accomplish its job
EGHDK
Yeah, again what I'm trying to figure out is does that error message basically mean... "you gotta do this on the main thread".
Or can the looper.prepare() error mean something else?
Mark M.
again, that's *probably* the right answer
EGHDK
Okay,
So are Handler and Looper things that are native to Android or Java?
Mark M.
those are Android constructs
EGHDK
Okay, so first I guess I'm gonna read up on what those actually are.
I'm working with someone else on a joint project, and I came in the middle of it, and I just see Looper.prepare() in seemingly random places, and I'm just trying to figure what those are.
Mark M.
so, you read my threading chapter?
it helps if you did, for me to try to explain what Looper and Handler are
because I can point back to concepts that you read about
8:05 PM
Mark M.
Jeff: do you have another question?
EGHDK
Yes mark I read your threading chapter. But you don't talk about Looper and Handler. You mostly talk about threads/runnables/asynctasks.
Unless I read the wrong part of the book? But it's the only thing I found refering to threads in the glossary
Mark M.
EGHDK: I didn't say that the threading chapter covered Looper; I wanted to know if you had read the chapter so that I can *explain* Looper
Jeff G.
has left the room
EGHDK
Yes, I'd appreciate it greatly.
Mark M.
so, do you remember the discussion in that chapter about the main application thread?
EGHDK
Yes
Mark M.
OK
so the main application thread is responsible for processing all sorts of events: user input, screen updates, etc.
it works off of a work queue
specifically, that work queue is a MessageQueue: http://developer.android.com/reference/android/...
and that MessageQueue is being read by a Looper
EGHDK
it's "a" message queue or is it "the" messageQueue?
Mark M.
MessageQueue is a class
there can be multiple instances of a MessageQueue
EGHDK
Got it. Continue
Mark M.
the main application thread has a MessageQueue, and a Looper that processes its MessageQueue
the job of a thread with a Looper is to do exactly one thing: call loop() on the Looper
8:10 PM
Mark M.
this runs a "message loop", which is a long-standing concept in event-driven UI engines
I had to write a message loop for Windows 3.1 programs back in the early 1990's, for example
EGHDK
So its the "core" loop that makes sure all of the events keep happening?
Mark M.
yes
now, we almost never work with a Looper ourselves
that's an "implementation detail"
but we do sometimes use a Handler
EGHDK
This might be a far stretch, but it's almost like some java programs have a main() but usually have a while() loop to keep the application going?
Mark M.
something along those lines
EGHDK
some java programs... ALL java programs
Mark M.
no, definitely not "ALL java programs"
EGHDK
Got it.
Okay... well theres goes something I thought I knew for certain... heh.
Mark M.
we sometimes create a Handler ourselves, or sometimes we use other classes (like AsyncTask) that create a Handler
a Handler needs to be created on a thread that is set up with a MessageQueue and a Looper
and, more importantly, the Looper actually needs to be looping
EGHDK
The looper can only loop on the main thread... no?
Mark M.
no
Looper is a class
MessageQueue is a class
you can have multiple instances of a Looper
you can have multiple instances of a MessageQueue
EGHDK
Gotcha
Mark M.
but a *thread* will have at most one Looper and one MessageQueue
and, an ordinary thread created out of nowhere will have no Looper and no MessageQueue
8:15 PM
EGHDK
Oh
Mark M.
so, when you said "I called Looper.prepare() and I stopped crashing", that may have *set up* a Looper, but the Looper isn't looping
and so anything that the Handler tries doing is going to wind up being ignored
now, the vast majority of the time, when you get the "needs to be on a thread that has called Looper.prepare()" error, it is as you suggest, something that should be done on the main application thread
more specifically, it is probably something that itself wants to use a Handler, such as trying to start an AsyncTask
however, there are cases where you really do want to have a Handler, Looper, and MessageQueue that is a different thread than the main application thread
the simplest way to set one of those up is to use a HandlerThread rather than just a regular Thread
as a HandlerThread sets all of that up for you
(I suspect that the main application thread *is* a HandlerThread, though I don't know that for certain)
EGHDK
so... this is really grabbing my curiosity. What's the best way to learn more about it. I'm gonna take a look at messageQueue and Looper class, and Handler class. You said that the main thread has one messageQueue and one Looper. How do you know that?
Is there anywhere I can look up documentation for the main thread?
Mark M.
years of experience
not really
you're welcome to attempt to read a book on Android internals, but I suspect that it'll be out of your depth
EGHDK
Hmm... well with basically a year of experience... I now know it. haha. Thanks things are starting to come together.
Any book on android internals you recommend?
Mark M.
Karim Yaghmour's is the one I have
8:20 PM
EGHDK
"Embedded Android"?
Mark M.
that's it
EGHDK
Cool. I'll definitely take a look at it. Any more things I can do now to look into MessageQueue and Looper right now? Like reading over the weekend? Or is there any kind of project I can set up that would be helpful examples on showing me whats really going on in the background?
Mark M.
um, you're welcome to read the JavaDocs
they're kinda thin
EGHDK
Heh. Okay, almost as silly as asking you "what's a stacktrace" a year ago... what do you "actually" mean when you say JavaDocs?
Mark M.
click that link
that is the JavaDocs for the Looper class
EGHDK
Gotcha.
Mark M.
you've probably read them, just never knew that "JavaDocs" is the term
EGHDK
Don't people make javadocs?
I mean make javadocs in code?
Mark M.
JavaDocs are derived from code comments
EGHDK
I feel like I've seen that in eclipse. Generate javadocs or something
Mark M.
there are tools that generate linked sets of HTML pages out of those code comments
EGHDK
Oh. So everything from that looper javadocs page are actual comments?
Mark M.
well, some bits are based off of the Java code itself
but if it looks like English, it's in a code comment
EGHDK
awesome. didn't know that.
Thanks!
8:25 PM
EGHDK
so theres no javadocs for MainThread? MainThread probably isn't a class so I guess not.
Mark M.
right
again, the main application thread may be a HandlerThread
it behaves like one
EGHDK
Gotcha. Okay. few more rapid fire questions. In your book you said that getActivity() called on the main thread is guranteed by Android to get a valid activity. How do you know that?
Mark M.
in part, because Dianne Hackborn said so
EGHDK
Because I'm having a problem now that once in a while my getActivity is getting called but its coming up null.
Gotcha. Does dianne hackborn write any books on android? seems like it sure would be helpful
Mark M.
I think you may have taken a comment out of context
no, Dianne's a bit busy building Android
I suspect you are referring to using a retained fragment to manage an AsyncTask
in general, there certainly are times when getActivity() on a fragment will return null
such as when the fragment has not yet been added to the activity
EGHDK
Okay, next question(s) before you go. Thread.sleep() is static in the java docs.
Mark M.
correct
EGHDK
But Thread.sleep() only affects the current thread. How... if its static?
Mark M.
that's a rather odd question
suppose you have a line of code: int i = 1 + 1;
that will only affect the current thread
8:30 PM
EGHDK
Well, wouldn't it affect all threads? and put them all to sleep as sleep belongs to the class and not any object?
Mark M.
no more than int i = 1 + 1 affects all integers
EGHDK
Interesting.
Don't get it. but I'll have to deal.
Mark M.
static says nothing about what code will affect
anyway, that's a wrap for today's chat
the transcript will be posted shortly
EGHDK
But why wouldn't you just do Thread.getCurrentThread.sleep()? idk. I always thought that calling sleep pause my main thread, and my new thread. But after I found out it didn't I was confused.
Mark M.
the next chat is Tuesday at 7:30pm US Eastern Time
EGHDK
Alright. Thanks.
Appreciate the help. Till Tuesday. See ya.
Mark M.
have a pleasant day!
EGHDK
has left the room
Mark M.
turned off guest access

Tuesday, July 8

 

Office Hours

People in this transcript

  • EGHDK
  • Jeff Gonzales
  • Mark Murphy