Mar 1 | 7:20 PM |
Mark M. | has entered the room |
Mark M. | turned on guest access |
Mar 1 | 7:25 PM |
the_new_mr | has entered the room |
Mark M. |
hello!
|
Mark M. |
how can I help you today?
|
the_new_mr |
hi Mark :)
|
the_new_mr |
how are things?
|
Mark M. |
Android Things is somewhat interesting, though it's pretty rough right now
|
Mark M. |
oh, no, wait... that's probably not what you meant...
|
Mark M. |
:-)
|
the_new_mr |
:)
|
the_new_mr |
haha. it's not... but good answer :)
|
the_new_mr |
how are things with *you*? ;)
|
Mark M. |
busy as usual -- and you?
|
the_new_mr |
hectic as usual :)
|
H_Walter | has entered the room |
Mark M. |
(BTW, hello, H_Walter -- I will be with you shortly!)
|
the_new_mr |
speaking of hectic... i'm here to ask something related to work
|
the_new_mr |
do you remember before when i asked about the situation we were having with the null pointer exception on the context?
|
the_new_mr |
going back into last year here
|
Mark M. |
ummm... vaguely
|
the_new_mr |
i know you do a lot of chats so you may not remember
|
Mark M. |
that rounds down to "no" :-)
|
the_new_mr |
lol
|
the_new_mr |
well, basically, we were getting NPEs when trying to do context.getApplicationContext()
|
Mark M. |
is this the chat: https://commonsware.com/office-hours/2017-01-05 ?
|
the_new_mr |
so the context itself is null
|
Paul R. | has entered the room |
the_new_mr |
that's the one :)
|
Mark M. |
(BTW, hello, Paul -- I will be with you after the_new_mr and H_Walter!)
|
Mar 1 | 7:30 PM |
the_new_mr |
so... the problem is the context
|
Mark M. |
OK, what's your question?
|
Paul R. |
Evening. I cam prepared with questions about services. And will wait patiently to get my turn.
|
the_new_mr |
for some bizarre reason, it's null
|
the_new_mr |
just a plain old context
|
Mark M. |
right, and I thought it might be a timing thing due to configuration changes
|
the_new_mr |
right
|
the_new_mr |
so you said to get the context and pass it to the constructor of the async task
|
the_new_mr |
which i did
|
the_new_mr |
but we're still getting the NPE
|
the_new_mr |
so it must be something else we're doing wrong
|
Mark M. |
with the same basic stack trace?
|
the_new_mr |
we actually have loads of different strack traces all over the code
|
the_new_mr |
all related to this
|
the_new_mr |
perhaps it's a general context thing...
|
Mark M. |
"all over the code" -- you mean, from more than just the one fragment you cited from the earlier chat?
|
the_new_mr |
yes
|
the_new_mr |
i was under the impression that we should always use the application context when passing it outside of the activity/fragment
|
the_new_mr |
but this seems to yield the NPE
|
the_new_mr |
is that understanding incorrect?
|
the_new_mr |
to prevent memory leaks
|
Mark M. |
I am not certain what "passing it outside the activity/fragment" means
|
Mark M. |
if you mean "stuffing something in a static field", then, yes, that needs to be the Application context
|
the_new_mr |
like, say, to the preference manager
|
the_new_mr |
yes
|
the_new_mr |
in case it gets held in a reference somewhere
|
Mark M. |
then perhaps you are not initializing the static fields lazily
|
the_new_mr |
particularly a static reference
|
Mark M. |
if your process gets terminated, your new process will not have the old values for those fields
|
Mar 1 | 7:35 PM |
the_new_mr |
well yes. but that doesn't seem to be the problem
|
the_new_mr |
today, a user demoed the app crashing while they were using it
|
Mark M. |
um, well, no offense, but you aren't really giving me a lot to go on
|
the_new_mr |
and i can't for the life of me think why the context would be null in this situation
|
the_new_mr |
ok. tell you what. chat to Walter and i'll try and get you some specific stuff
|
Mark M. |
that's just what I was going to ask for -- I'll be with you in a bit
|
the_new_mr |
i didn't get the chance to prepare before the chat like i had planned
|
Mark M. |
H_Walter: your turn! do you have a question?
|
the_new_mr |
ok
|
H_Walter |
View paste
|
Mark M. |
sure: use applicationId
|
Mark M. |
assuming that by "different names" you mean "different package names", so they can be installed at the same time
|
H_Walter |
Yes. Thanks. I'll look into it.
|
Mark M. | |
Mark M. |
or, for debug/release, use applicationIdSuffix
|
Mark M. | |
Mark M. |
drop it in buildTypes { debug { } } and your chosen suffix will be appended to your regular applicationId for the debug builds
|
Mark M. |
that was a nice and easy question, with the downside being that I now get to take questions from the others :-)
|
Mark M. |
I'll be back with you in a bit
|
Mark M. |
Paul: your turn! you had a question?
|
Paul R. |
I do. The info is in the clipboard so I'll paste it...
|
Paul R. |
View paste
|
Mar 1 | 7:40 PM |
Mark M. |
wow, that was epic :-)
|
Paul R. |
The end got truncated. I'll post that after we deal with the first two questions.
|
Paul R. |
Thamks. :-)
|
Mark M. |
actually, I think I have it all, via the "View paste" link
|
Paul R. |
cool
|
Mark M. |
with regards to service instances, services are natural singletons, so you don't have many choices there
|
Mark M. |
with regards to the process model, you could use android:process in the manifest to have one of the components (e.g., the service) be in a separate process
|
Paul R. |
Good, I understand that correctly.
|
Mark M. |
IMHO, having some long-running durable process for a game may not go over all that well, but that's just me
|
Paul R. |
That opens up a question: is this a good idea or bad to have separate processes.
|
Mark M. |
you take up more memory, and you now incur IPC overhead and limitations for communications to the service
|
Mark M. |
OTOH, Android can terminate the non-service process, freeing up that system RAM sooner
|
H_Walter | has left the room |
Mark M. |
a very well-written app, using onLowMemory() and kin, can get much of the same memory savings with a single process, but that's hard
|
Paul R. |
Sounds like one process for both activity and service is not a good idea.
|
Mark M. |
it is the simplest way to jettison the UI memory
|
Mark M. |
I am a bit of a curmudgeon on the subject, as I remember the old days with low-RAM devices
|
Mark M. |
and, if your app might be distributed to Android One markets, you have low-RAM devices there too
|
Mar 1 | 7:45 PM |
Mark M. |
with regards to the notifications... is a User something independent from Android's concept of user accounts?
|
Paul R. |
My app User is essentially an Android account User.
|
Mark M. |
I haven't played with notifications in a multi-user scenario, but I would be surprised if one user can see another user's notifications
|
Mark M. |
privacy, and all that
|
Mark M. |
so I suspect that you have no choice but one notification per user
|
Paul R. |
I'll check that out with Hangouts. I use multiple accounts with hangouts.
|
Mark M. |
assuming that "is essentially" really means "is", as you either are or are not relying on Android's multi-user support, AFAIK
|
Paul R. |
I am relying on the fact that there will be multiple accounts on the device.
|
Mark M. |
yeah, I haven't done much work with that in general, let alone with notifications
|
Paul R. |
They can be facebook, twitter, etc.
|
Mark M. |
um, no, that's not what I meant
|
Mark M. |
Settings > Users
|
Mar 1 | 7:50 PM |
Mark M. |
(on Android 4.2+ tablets and Android 5.0+ devices)
|
Mark M. |
that's what I am referring to
|
Paul R. |
In that case, I have in mind only a single User.
|
Mark M. |
then you could go either way on the notifications, assuming that the small icons would differ by User
|
Mark M. |
if the small icons would be identical per User, you are better off grouping them, IMHO
|
Mark M. |
but, that's more of a design/UX opinion, more so than a hard-and-fast rule or anything
|
Mark M. |
let me take another question from the_new_mr, and I'll swing back to you in a bit
|
Paul R. |
Grouping would be my preference.
|
Mark M. |
the_new_mr: back to you!
|
Paul R. |
OK
|
Mark M. |
the_new_mr: are you there?
|
Mark M. |
the_new_mr: when you return, chime in and let me know that you are around
|
Mark M. |
Paul: back to you in the meantime
|
Paul R. |
ok, a question about data sizes
|
Mar 1 | 7:55 PM |
Paul R. |
My estimate is that on average, over many days, the data size will be constant and in the ballpark of tens of KBytes. I think that is tiny, even for Android One. Would I be wrong?
|
the_new_mr |
back when ready
|
Mark M. |
(the_new_mr: OK, one moment)
|
the_new_mr |
sure
|
Mark M. |
Paul: data size... in memory? on disk? something else?
|
Paul R. |
in memory
|
Mark M. |
well, there should be a second copy of all your classes
|
Mark M. |
or at least the ones that you touch
|
Mark M. |
I'm hazy as to how the AOT compilation on Android 5.0+ affects that sort of thing
|
Mark M. |
so, the second process is definitely more heavyweight than just the objects that you instantiate
|
Paul R. |
btw, classes.dex is tiny too.
|
Paul R. |
the size is in resources, all in the activity.
|
Mark M. |
:: shrug :: adding android:process takes less time than it does to type in a chat entry :-)
|
Mark M. |
try it, and see what the system reports for your RAM usage
|
Mark M. |
now, if it will break your activity <-> service communication, that may be a longer test
|
Paul R. |
Good enough. Just wanted to check to see if my thinking was brain-dead. :-)
|
Mark M. |
let me switch back to the_new_mr for realz, and I'll be back with you in a bit
|
Mark M. |
the_new_mr: OK, back to you!
|
the_new_mr |
ok
|
Mar 1 | 8:00 PM |
the_new_mr |
so.... let me ask a few general questions first if you don't mind
|
Mark M. |
go ahead
|
the_new_mr |
when passing context, say to an async task or to the preference manager (or to something that might pass it to the preference manager or similar), it's generally better to pass the application context, right?
|
Mark M. |
I would phrase it as: you want the lifespan of the use of the Context to match the lifespan of the Context
|
the_new_mr |
right...
|
the_new_mr |
so application context where its lifespan may outlive the thing you're passing it from
|
Mark M. |
so, for example, PreferenceManager.getDefaultSharedPreferences() should be safe to call with just the activity as the context
|
the_new_mr |
ok...
|
the_new_mr |
and, is there an advantage to doing so?
|
Mark M. |
in that specific case? no
|
the_new_mr |
i mean, using the activity?
|
the_new_mr |
ok. in other cases?
|
Mark M. |
the activity as a context is important for UI work
|
the_new_mr |
right
|
Mark M. | |
the_new_mr |
so that you don't have to traverse the whole app
|
Mark M. |
no, it's more so that your styles and themes work
|
the_new_mr |
ok... that looks like a good read
|
Mark M. |
yes, it's my go-to resource for "when to use what context"
|
the_new_mr |
i see. ok
|
the_new_mr |
thanks for that
|
the_new_mr |
still learning android by the way... as you can probably tell :)
|
Mark M. |
this is a funky area
|
the_new_mr |
tell me about it!
|
the_new_mr |
ok...
|
Mark M. |
so, let's take your AsyncTask scenario
|
the_new_mr |
another general question...
|
Mark M. |
OK
|
Mar 1 | 8:05 PM |
Mark M. |
sorry, go ahead with your next question
|
the_new_mr |
storing the context as a static reference is a bad idea because of memory leaks, right? but is this a bad idea when it comes to storing the application context as a static member in the application class (or rather, a class that extends the application class)
|
Mark M. |
storing a context other than Application represents a leak
|
the_new_mr |
because the application class is over-arching
|
Mark M. |
Application is a process-wide singleton
|
the_new_mr |
yes, that's what i was thinking
|
Mark M. |
or, as I like to describe it, Application is pre-leaked
|
Mark M. |
you cannot leak it further
|
the_new_mr |
it exists as long as everything else exists
|
the_new_mr |
ha, right
|
Mark M. |
for 99.99% of the life of your process, yes
|
the_new_mr |
ok. so that answers that one. so far, my understanding seems correct
|
the_new_mr |
what about the 0.01%? :)
|
Mark M. |
(it's a bit weird right when the process is starting up, if you also have ContentProviders, but that usually isn't an issue for most people)
|
the_new_mr |
ah ok
|
the_new_mr |
now. part of our problems seems to be that getContext() itself returns null
|
Mark M. |
getContext() called on what?
|
the_new_mr |
not sure why. maybe because the fragment is detached from the activity or something
|
the_new_mr |
inside a fragment
|
Mark M. |
that method was added in API Level 23
|
Mark M. |
are you sure that you are using it? or is your minSdkVersion that high?
|
Mark M. |
or are you using the fragment backport?
|
the_new_mr |
backport
|
the_new_mr |
support v4
|
Mark M. |
OK, I'll assume for the moment that it just returns the same thing as getActivity() does
|
the_new_mr |
ok
|
the_new_mr |
anyway...
|
Mark M. |
getActivity() will return null when your fragment is not attached to an activity
|
Mar 1 | 8:10 PM |
the_new_mr |
we have a static method in the application class that returns the static reference to the application context
|
the_new_mr |
i'm inclined to use this where we are having these NPE problems. is this a bad way to go?
|
the_new_mr |
i'll read that article obviously
|
Mark M. |
it depends on what the context is used for
|
the_new_mr |
yeah
|
the_new_mr |
i guess the article will help with that
|
the_new_mr |
say to get values from shared preferences
|
Mark M. |
in the previous chat, you were trying to access SharedPreferences from doInBackground() of an AsyncTask kicked off by the fragment
|
the_new_mr |
yes. actually, looking at the crashes, it seems your solution did help after all so apologies for that
|
Mark M. |
OK, that's good
|
the_new_mr |
but, as i was saying, we're having context issues elsewhere
|
Mark M. |
while you can paper over the problem by using the Application context, you have deeper issues
|
the_new_mr |
i think so at least... we're using splunk mint for crash reporting and it's not the best
|
the_new_mr |
not bad but not amazing
|
the_new_mr |
hmmm... yeah
|
the_new_mr |
true for sure
|
Mark M. |
I am worried about what you are doing, and when, that is trying to use getContext() on a fragment and you are getting null
|
the_new_mr |
don't i know it!
|
the_new_mr |
yes
|
Mark M. |
this is one of the reasons I don't like the "use a static Application reference" solutions
|
Mark M. |
IMHO, you're kicking the can down the road
|
Mark M. |
and you'll eventually get bitten by the problem in some other fashion
|
the_new_mr |
honestly, dealing with legacy code, fire fighting, trying to get crash count down... i'm just trying to sort out the big fish at the moment and then get to the little fish when i can get the time!
|
the_new_mr |
yes, i know what you mean
|
the_new_mr |
clearly, the getContext returning null is an issue
|
Mark M. |
what you might consider doing is making your change to Application, but making very careful notes about where and why you are doing it
|
the_new_mr |
let me check the API reference... maybe the API level 23 thing you mentioned is part of the problem
|
Mark M. |
so when you can devote the time, you can try to figure out the underlying issue
|
the_new_mr |
yeah
|
Mar 1 | 8:15 PM |
the_new_mr |
some comment with a codeword. that's a good idea
|
Mark M. |
and with that, let me give Paul another shot -- with luck, I'll be back with you before the chat ends
|
Mark M. |
Paul: do you have another question?
|
Paul R. |
The approach seems ok: app starts service in a separate process and binds to the service. Service forwards data until bind is broken. Then service collects data while app is dead (unbound) to provide notifications. When the app is running in the foreground again, service dumps the data to the app. Seems clean. You agree?
|
the_new_mr | |
the_new_mr |
ok
|
Mark M. |
you'll need to both start and bind to the service to get that effect, but you're presumably doing that already
|
the_new_mr |
that's the getContext reference when you're finished with Paul
|
Mark M. |
and you'll need to think through the triggers for clearing those notifications, when they are no longer needed
|
Mark M. |
otherwise, that seems reasonable
|
Paul R. |
Yup. What constitutes a "large" amount of data that should give me pause to reconsider the approach?
|
Mark M. |
reconsider what aspect of the approach? using two processes?
|
Mark M. |
having a long-running service in general?
|
Paul R. |
Holding data in the service.
|
Paul R. |
for a long time.
|
Mark M. |
well, that data would have to just be a cache, as you cannot guarantee that the service will be around for a long time
|
Mark M. |
your clear upper bound is your heap limit
|
Mark M. |
personally, I'd want that process to be leaner than that
|
Mark M. |
a few MB, tops
|
Mark M. |
but, that's me, not a system requirement
|
Paul R. |
That's a fair answer. And much appreciated.
|
Mar 1 | 8:20 PM |
Mark M. |
the heap limit is the system's way of trying to ensure that all apps "play fair" with respect to sharing system RAM
|
Mark M. |
the longer your process might be around, the greater the need to tighten the proverbial belt
|
Mark M. |
consider overriding onTrimMemory() in the service and using that to prune back the cache
|
Paul R. |
The process can be around forever. The key is to make sure the cache gets drained from time to time.
|
Mark M. |
yup, that's what onTrimMemory() is for, plus any other internal triggers that you want (e.g., time-based)
|
Paul R. |
Setting an upper bound on the cache is reasonable.
|
Mark M. |
LruCache helps with that
|
Paul R. |
I'll look into the method.
|
Paul R. |
Is that a class?
|
Mark M. |
LruCache is, yes
|
Mark M. | |
Mark M. |
pretty much what it says on the box: it's a least-recently-used cache implementation
|
Paul R. |
Perfect.
|
Mark M. |
either by # of objects or their size, depending on your implementation
|
Paul R. |
Thanks for your help. Talk to you soon I'm sure.
|
Mark M. |
OK, sounds good
|
Mark M. |
the_new_mr: back to you!
|
Paul R. | has left the room |
the_new_mr |
just reading that article
|
the_new_mr |
quality stuff!
|
Mark M. |
Dave Smith is awesome
|
the_new_mr |
really wish i'd known about it about two years ago :)
|
the_new_mr |
clearly
|
Mar 1 | 8:25 PM |
the_new_mr |
you're not bad yourself. i'd hire ya ;)
|
the_new_mr |
you have potential :p
|
Mark M. |
I try
|
the_new_mr |
:)
|
the_new_mr |
ok... so i checked that getContext for the v4 fragment
|
the_new_mr | |
the_new_mr |
seems ok. no mention of api level 23. and i noticed crashes on marshmallow and above phones
|
the_new_mr |
so no issues there
|
the_new_mr |
so just must be that the fragment is somehow being detached
|
the_new_mr |
can't imagine why but that's for another day some day
|
the_new_mr |
i guess
|
Mark M. |
it'll be detached on a configuration change
|
Mark M. |
if the fragment is retained, it will be attached to a new activity instance shortly thereafter
|
the_new_mr |
i have a feeling the fragments are not being handled properly in our app
|
the_new_mr |
i've noticed multiple calls to the lifecycle methods
|
the_new_mr |
legacy code with some serious issues
|
Mark M. |
*to* the lifecycle methods?
|
the_new_mr |
trying to make it better as much as we can
|
the_new_mr |
yes
|
Mark M. |
other than chaining to the superclass, *calling* a lifecycle method is just plain evil
|
the_new_mr |
oh.. not explicit calls
|
Mark M. |
oh, whew
|
the_new_mr |
yes, that is evil. nothing like that thankfully
|
Mark M. |
you had me scared there for a bit
|
the_new_mr |
:)
|
the_new_mr |
:)
|
the_new_mr |
i meant that they are getting calle
|
the_new_mr |
d
|
Mark M. |
another thing that you might try doing is switching from getContext() to getActivity()
|
the_new_mr |
which implies bad fragment management i guess, right?
|
Mar 1 | 8:30 PM |
the_new_mr |
i see...
|
the_new_mr |
ok. that might be worth a try
|
Mark M. |
looking at the code for the backport, getContext() and getActivity()are independent
|
the_new_mr |
ok. i might give that a go
|
the_new_mr |
thanks Mark. you've been very helpful as always
|
Mark M. |
most of the "institutional knowledge" on this stuff is tied to fragments being hosted by activities, and using getActivity()
|
the_new_mr |
greetings from the mother land btw ;)
|
Mark M. |
pardon?
|
the_new_mr |
Ireland
|
Mark M. |
yeesh, it's late for you there
|
the_new_mr |
it is
|
the_new_mr |
but this was the only time that suited this week
|
Mark M. |
which means it's a fine time to note that we have run out of time for the chat
|
Mark M. |
er, that wasn't really tied to your last comment
|
the_new_mr |
ha. yes :)
|
Mark M. |
more the "you should get some sleep" angle
|
the_new_mr |
yeah, i got that
|
Mark M. |
the next chat is tomorrow at 9am US Eastern
|
Mark M. |
which is a more Ireland-friendly hour, at least
|
the_new_mr |
yeah. had a meeting clash
|
Mark M. |
ah
|
the_new_mr |
then friday's i'm otherwise engaged
|
the_new_mr |
so had to be now
|
Mark M. |
OK
|
the_new_mr |
plus kinda wanted to get to the bottom of this
|
Mark M. |
sorry if my switch of the chat time yesterday -> Friday messed things up for you
|
Mark M. |
my site was suffering from S3 issues, like lots of others
|
the_new_mr |
anyway... thanks and good evening/night :)
|
Mark M. |
and I wasn't sure that was going to get cleared up in time
|
the_new_mr |
no worries at all ;)
|
the_new_mr |
i didn't even notice
|
the_new_mr |
didn't check office hours till today :)
|
Mark M. |
OK, well, have a pleasant rest of your evening!
|
the_new_mr |
thanks. you too!
|
the_new_mr |
good night!!
|
the_new_mr | has left the room |
Mark M. | turned off guest access |