Jul 10 | 7:20 PM |
Mark M. | has entered the room |
Mark M. | turned on guest access |
Jul 10 | 7:25 PM |
Jeff G. | has entered the room |
Mark M. |
hello, Jeff
|
Mark M. |
how can I help you today?
|
Jeff G. |
Hi.
|
Jul 10 | 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
|
Mark M. |
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
|
Jeff G. |
"Introducing Gradle" ?
|
Mark M. |
the chapter begins on page 2193 "Advanced Gradle for Android Tips"
|
Mark M. |
"Automating APK Version Information" starts on page 2206
|
Jeff G. |
got it. Thanks.
|
Jeff G. |
My second question is a bit more complex. Another ListView-related question.
|
Jeff G. |
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.
|
Jeff G. |
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
|
Jul 10 | 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.
|
Jeff G. |
"the story of my life"
|
Jeff G. |
:)
|
Jeff G. |
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.
|
Jeff G. |
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
|
Mark M. |
their battery will run out
|
Mark M. |
or their finger will develop a blister
|
Jeff G. |
lol.
|
Jeff G. |
it is a government app for internal use only. i try not to ask too many questions.
|
Jeff G. |
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
|
Mark M. |
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.
|
Jul 10 | 7:40 PM |
Jeff G. |
they are just rows with about 5 textviews.
|
Jeff G. |
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
|
Mark M. |
a Cursor with 5 short text columns should be able to handle a few thousand rows
|
Mark M. |
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
|
Mark M. |
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?
|
Jeff G. |
I'll have to look into MergeCursor. I don't know anything about it.
|
Mark M. | |
Mark M. |
"A convience class that lets you present an array of Cursors as a single linear Cursor"
|
Mark M. |
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
|
Mark M. |
but this would allow you to query in chunks
|
Mark M. |
until you OOM or reach some acceptable overall cap
|
Mark M. |
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
|
Jul 10 | 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.
|
Jeff G. |
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
|
Mark M. |
you're one fling away from having no relevant data
|
Mark M. |
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
|
Mark M. |
if the COUNT() query returns a value over some threshold, tell them they're going to get a subset
|
Mark M. |
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
|
Mark M. |
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.
|
Jeff G. |
right now I present a subset, but display an actual count so they know what the total available are.
|
Jul 10 | 7:50 PM |
Mark M. |
and to me, that seems reasonable, since they're searching
|
Jeff G. |
awesome.
|
Jeff G. |
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?"
|
Mark M. |
for example, forward/next buttons to advance through 500-row chunks
|
Mark M. |
(or horizontal swipes via a ViewPager if you're not already using that in the tabs)
|
Mark M. |
anything that lets you dump old results to free up memory for new results
|
EGHDK | has entered the room |
Mark M. |
hello, EGHDK
|
Jul 10 | 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.
|
EGHDK |
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.
|
EGHDK |
I don't know what a handler is or a Looper... =/
|
Mark M. |
do you know what a stack trace is?
|
EGHDK |
Yes.
|
EGHDK |
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.
|
EGHDK |
That's definitely clear.
|
EGHDK |
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
|
Mark M. |
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.
|
Jul 10 | 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
|
Mark M. |
"not crashing" is the minimum threshold for avoiding failure
|
Mark M. |
it is not indicative of success on its own
|
Mark M. |
whatever you are doing that is triggering the need for a Handler isn't looking for a Handler because it's bored
|
Mark M. |
it's looking to use a Handler for some specific reason
|
Mark M. |
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".
|
EGHDK |
Or can the looper.prepare() error mean something else?
|
Mark M. |
again, that's *probably* the right answer
|
EGHDK |
Okay,
|
EGHDK |
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.
|
EGHDK |
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?
|
Mark M. |
it helps if you did, for me to try to explain what Looper and Handler are
|
Mark M. |
because I can point back to concepts that you read about
|
Jul 10 | 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.
|
EGHDK |
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
|
Mark M. |
so the main application thread is responsible for processing all sorts of events: user input, screen updates, etc.
|
Mark M. |
it works off of a work queue
|
Mark M. |
specifically, that work queue is a MessageQueue: http://developer.android.com/reference/android/...
|
Mark M. |
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
|
Mark M. |
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
|
Mark M. |
the job of a thread with a Looper is to do exactly one thing: call loop() on the Looper
|
Jul 10 | 8:10 PM |
Mark M. |
this runs a "message loop", which is a long-standing concept in event-driven UI engines
|
Mark M. |
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
|
Mark M. |
now, we almost never work with a Looper ourselves
|
Mark M. |
that's an "implementation detail"
|
Mark M. |
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.
|
EGHDK |
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
|
Mark M. |
a Handler needs to be created on a thread that is set up with a MessageQueue and a Looper
|
Mark M. |
and, more importantly, the Looper actually needs to be looping
|
EGHDK |
The looper can only loop on the main thread... no?
|
Mark M. |
no
|
Mark M. |
Looper is a class
|
Mark M. |
MessageQueue is a class
|
Mark M. |
you can have multiple instances of a Looper
|
Mark M. |
you can have multiple instances of a MessageQueue
|
EGHDK |
Gotcha
|
Mark M. |
but a *thread* will have at most one Looper and one MessageQueue
|
Mark M. |
and, an ordinary thread created out of nowhere will have no Looper and no MessageQueue
|
Jul 10 | 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
|
Mark M. |
and so anything that the Handler tries doing is going to wind up being ignored
|
Mark M. |
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
|
Mark M. |
more specifically, it is probably something that itself wants to use a Handler, such as trying to start an AsyncTask
|
Mark M. |
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
|
Mark M. |
the simplest way to set one of those up is to use a HandlerThread rather than just a regular Thread
|
Mark M. |
as a HandlerThread sets all of that up for you
|
Mark M. |
(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?
|
EGHDK |
Is there anywhere I can look up documentation for the main thread?
|
Mark M. |
years of experience
|
Mark M. |
not really
|
Mark M. |
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.
|
EGHDK |
Any book on android internals you recommend?
|
Mark M. |
Karim Yaghmour's is the one I have
|
Jul 10 | 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
|
Mark M. |
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. | |
Mark M. |
click that link
|
Mark M. |
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?
|
EGHDK |
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
|
Mark M. |
but if it looks like English, it's in a code comment
|
EGHDK |
awesome. didn't know that.
|
EGHDK |
Thanks!
|
Jul 10 | 8:25 PM |
EGHDK |
so theres no javadocs for MainThread? MainThread probably isn't a class so I guess not.
|
Mark M. |
right
|
Mark M. |
again, the main application thread may be a HandlerThread
|
Mark M. |
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.
|
EGHDK |
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
|
Mark M. |
no, Dianne's a bit busy building Android
|
Mark M. |
I suspect you are referring to using a retained fragment to manage an AsyncTask
|
Mark M. |
in general, there certainly are times when getActivity() on a fragment will return null
|
Mark M. |
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
|
Mark M. |
suppose you have a line of code: int i = 1 + 1;
|
Mark M. |
that will only affect the current thread
|
Jul 10 | 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.
|
EGHDK |
Don't get it. but I'll have to deal.
|
Mark M. |
static says nothing about what code will affect
|
Mark M. |
anyway, that's a wrap for today's chat
|
Mark M. |
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.
|
EGHDK |
Appreciate the help. Till Tuesday. See ya.
|
Mark M. |
have a pleasant day!
|
EGHDK | has left the room |
Mark M. | turned off guest access |