Sep 2 | 7:20 PM |
Mark M. | has entered the room |
Mark M. | turned on guest access |
Sep 2 | 7:25 PM |
Gabriele | has entered the room |
Mark M. |
hello, Gabriele
|
Mark M. |
how can I help you today?
|
Gabriele |
hi Mark :)
|
Gabriele |
I have some issue with viewpager/fragments, I did some error, probably
|
Gabriele |
I'm getting: java.lang.NullPointerException: Attempt to write to field 'android.support.v4.app.FragmentManagerImpl android.support.v4.app.Fragment.mFragmentManager' on a null object reference
|
Gabriele |
this is the code I have inside my FragmentActivity:
|
Gabriele |
View paste
|
Mark M. |
that sounds like something is trying to update mFragmentManager via reflection on a Fragment, but the Fragment is null
|
Mark M. |
can you post the full stack trace?
|
Gabriele |
sure
|
Sep 2 | 7:30 PM |
Gabriele |
View paste
(36 more lines)
|
Lucy | has entered the room |
Mark M. |
is there a scenario in which your PagerAdapter might return a null Fragment?
|
Lucy |
Hello! Joining my first Office Hours
|
Mark M. |
(BTW, hello, Lucy -- I will be with you shortly!)
|
Lucy |
No rush. Observing to start.
|
Gabriele |
View paste
(2 more lines)
|
Gabriele |
maybe I wrote a wrong main.xml? Can this be the reason?
|
Gabriele |
hi Lucy :)
|
Mark M. |
well, that code will return null if the WeakReference has been culled already
|
Mark M. |
that code looks a bit odd in general
|
Mark M. |
it will also return null if it cannot find anything in the list of the proper type
|
Gabriele |
I think it's still not called, will try to remove it
|
Gabriele |
yes, it wasn't the cause, but I've removed it anyway
|
Sep 2 | 7:35 PM |
Gabriele |
can I paste the entire xml?
|
Gabriele |
maybe I did some stupid error there
|
EGHDK | has entered the room |
Mark M. |
I wouldn't, as I don't see how that could cause this error
|
Gabriele |
ok
|
Gabriele |
hi EGHDK
|
Mark M. |
I'd put logging in getItem() in your FragmentPagerAdapter to see if it is returning null in some scenario
|
Mark M. |
let me take questions from the others, and I'll be back with you in a bit
|
Mark M. |
Lucy: your turn! do you have a question?
|
EGHDK |
Hello, everyone. No questions from me for now.
|
Lucy |
Hi Gabriele :-)
|
Gabriele |
sure, thank you Mark
|
Lucy |
Well..I have a tiny app in mind that is very simple. Its a tiny app for my friend's business. I want to bundle just a few audio things into it. So far I'm going to follow the advice in "Get Your Media On".
|
Mark M. |
OK
|
Lucy |
I see the various locations that audio files can be stored. If they are not "large" I'm tempted to go with storing them in /raw. Unless that's really an anti-pattern.
|
Mark M. |
no, res/raw/ is fairly typical for short audio clips
|
Mark M. |
I wouldn't put a symphony in there
|
Lucy |
OK cool. I will write the app initially referring to there. Can always change it once I have everything else in place.
|
Mark M. |
yes
|
Mark M. |
really, the biggest issue is whether your app will be too big with all the media
|
Sep 2 | 7:40 PM |
Mark M. |
and "too big" is in the eyes of the user
|
Lucy |
Yeah, that's what I'm worried about. There is a video I'd like to include. But the user would view it only once or twice. I thought about fetching it upon a button click, store it and then automatically delete it.
|
Mark M. |
or play it back as a stream, depending on the video hosting
|
Lucy |
If I gave buttons to delete the audio and download again...maybe that would be ok?
|
Lucy |
In that case, the audio would not be from /raw. I'd have to put it on SD card.
|
Lucy |
This app is my first Android app. :-)
|
Mark M. |
well, not an SD card, only because few devices have one
|
EGHDK |
Mark, theres no way to delete an asset once it has been downloaded with an apk? So theres no way to delete anything in lets say res/raw?
|
Mark M. |
you will need to put it on internal storage or external storage in most cases
|
Lucy |
Oh yes. OK, internal storage...as in data/data.
|
Mark M. |
assets/ and res/raw/ are read-only at runtime; they cannot be modified or deleted
|
Mark M. |
internal storage is sometimes data/data/ -- do not rely upon that
|
Mark M. |
use proper methods, like getFilesDir(), to learn where it is on any given environment
|
Mark M. | |
Mark M. | |
Lucy |
OK cool, I'll probably do internal storage. Thank you!
|
Mark M. |
those are links to a pair of blog posts of mine that try to explain storage a bit more
|
Mark M. |
(and eventually I will blend that material more into the book...)
|
Mark M. |
let me take questions from the others, and I'll be back with you in a bit
|
Lucy |
Terrific! I've been studying and studying and now I'm trying to get ready to actually do something.
|
Lucy |
Thanks, I'm all set for now.
|
Mark M. |
EGHDK: your turn! do you have a question?
|
EGHDK |
Yes.
|
EGHDK |
I have a simple application with just 3 activities. I'm interested in using parse.com for it's backend capabilities.
|
EGHDK |
Anyway, parse requires me to create an application class and override oncreate in it.
|
Mark M. |
:: grumble ::
|
Sep 2 | 7:45 PM |
EGHDK |
I put a simple toast notification in the onCreate of the Application class, and I get that toast at times that I don't expect it.
|
Mark M. |
personally, I'd use Log statements, just in case there's something with UI processing that is affecting matters
|
EGHDK |
I'll lock my device. Put it in my pocket. Turn it back on again. Enter password... and bam. Toast shows. Even though the app isn't in the foreground. What gives?
|
Mark M. |
Android recreated the process for some reason
|
Mark M. |
could be an alarm from AlarmManager scheduled by the Parse code
|
EGHDK |
Yeah, but I get that toast every time. Exactly. I don't have anything from parse in there. I commented it out just to check.
|
Mark M. |
:: shrug ::
|
Mark M. |
without knowing your app, I cannot tell you why Android is recreating your process
|
EGHDK |
Had me curious about the lifecycle. I thought one application class per process... and one process per application (unless you specify otherwise)
|
Mark M. |
you might have a START_STICKY or START_REDELIVER_INTENT service, for example
|
EGHDK |
No services...
|
Mark M. |
there is one Application instance per process, and by default one process per app
|
EGHDK |
I'll create a total dummy application, and add the toast in that Applications onCreate and see what happens. Just to make sure its nothing else. Thanks.
|
EGHDK |
So Application is a singleton essentially?
|
Mark M. |
yes
|
EGHDK |
Thoughts on storing data in it?
|
Mark M. |
I'd rather use regular static data members
|
Mark M. |
as you can have N of those, lazy-initialized, etc.
|
Mark M. |
Application is handy for a few things related to cache management, as a central onTrimMemory() handler, for example
|
Sep 2 | 7:50 PM |
EGHDK |
Wouldn't it make more sense to just put them in an application class? Where would you use a static data member? Create a custom class just to store it?
|
Mark M. |
that depends on the nature of the data
|
EGHDK |
True. Okay. Thanks
|
Mark M. |
let me take questions from the others, and I'll be back with you in a bit
|
Mark M. |
Gabriele: your turn! do you have another question?
|
Gabriele |
Mark, the problem was inside getItem, I was returning a wrong null there
|
Gabriele |
for now I don't, thank you :)
|
Mark M. |
OK, glad to hear you found the problem
|
Mark M. |
Lucy: your turn! do you have another question?
|
Lucy |
No technical questions yet. Thanks!
|
Mark M. |
OK
|
Mark M. |
EGHDK: back to you -- do you have another question?
|
Sep 2 | 7:55 PM |
EGHDK |
Yeah, so another issue I'm having is with memory leaks.
|
EGHDK |
According to effective java, they are "unintentional object retention"
|
Mark M. |
much as a water leak is "unintentional liquid dispersal"
|
Gabriele |
ahah
|
EGHDK |
So they show an example of an array, and basically they are poping off the last element, but it's still in the array, so it's a memory leak.
|
Mark M. |
um, that doesn't make a lot of sense
|
Mark M. |
for example, you can't pop things off of a Java array
|
Mark M. |
you can pop things off of an ArrayList, but then the object is no longer in the ArrayList
|
EGHDK |
Yeah, but I think they do it by "hand" to explain the point.
|
Mark M. |
um, OK
|
EGHDK |
They create a method called pop
|
EGHDK |
and write the code in it
|
EGHDK |
What I'm really asking is... "Well if the activity get's "finished" doesn't everything in it also get GC'd?"
|
Sep 2 | 8:00 PM |
Mark M. |
yes, which gets me back to the "that doesn't make a lot of sense" point
|
Mark M. |
if their array was in a static data member, and you accidentally had stuff in the array that you forgot about, *then* you have a memory leak
|
Mark M. |
or if their array were otherwise ineligible for garbage collection, such as a thread still being able to reach it
|
EGHDK |
Hmm... you don't think it would be wise for me to post the example would it?
|
Mark M. |
unless they have published the code under an open source license, I would not post it
|
EGHDK |
Alright, yeah so I guess it's hard to explain. But mostly.... since an Activity is an "object", if it get's finished does that mean it's GCd?
|
Mark M. |
not necessarily
|
Mark M. |
let's switch to the physical world for a bit
|
Mark M. |
imagine a chain
|
Mark M. |
a chain is made of interlocking links
|
Mark M. |
if the chain is not connected to anything, you can collect it and throw it in the garbage
|
Mark M. |
(which would be a waste of a perfectly good chain, but I digress...)
|
Mark M. |
now, suppose that one end of the chain is nailed to a wall
|
Mark M. |
at this point, none of the links can be collected and put in the garbage, because they all connect, directly or indirectly, to the wall
|
Mark M. |
(and we'll assume the garbage can is on the far side of the room, and you're really too tired to move it)
|
Mark M. |
but, you take a bolt cutter and snip one of the links, part-way along the chain
|
Sep 2 | 8:05 PM |
Mark M. |
the part from that point downwards is now disconnected from the wall, and you can collect it and put it in the garbage
|
Mark M. |
switching back to the world of garbage collection in an object-oriented language...
|
Mark M. |
there are certain objects whose scope is considered "static"
|
Mark M. |
in Java, this includes static data members and threads
|
Mark M. |
those objects cannot be garbage collected, by definition
|
Mark M. |
anything they hold onto cannot be garbage collected, because those objects are reachable from a static reference
|
Mark M. |
anything that *those* objects hold onto cannot be garbage collected, because those objects are indirectly reachable from a static reference
|
Mark M. |
ad nauseum
|
Mark M. |
here, the objects are the links of the chain, and static references are the nail in the wall
|
Mark M. |
while an Activity is running (i.e., it has not yet been finish()ed), it is being held in a list of running activities somewhere in framework code
|
Mark M. |
once it is finish()ed and destroyed, it is removed from that list
|
EGHDK |
Gotcha. So when it's running it's being referenced to... in some list somewhere.
|
Mark M. |
now, it is eligible for garbage collection if and only if nothing else can reach it
|
EGHDK |
Okay, so lets say nothing else can reach it.
|
Mark M. |
at that point, it is eligible for garbage collection
|
EGHDK |
Is there anything in the activity that could keep the Activity from being GCd?
|
Lucy |
And if and only if it gets GC'ed? Is it possible it may not get GC'ed?
|
Mark M. |
EGHDK: you said "lets say nothing else can reach it"
|
Mark M. |
either that is true, or it is not
|
Mark M. |
if it is true, then nothing can prevent it from being garbage collected, though how quickly that will occur in Android will vary
|
Sep 2 | 8:10 PM |
EGHDK |
So just because let's say I have a static data member in my Activity, can my instance of my Activity be GCd?
|
Mark M. |
Lucy: something else could have a reference to the activity that prevents it from being garbage collected, like a thread
|
Lucy |
Thank you
|
Mark M. |
EGHDK: that depends on what the static data member holds
|
EGHDK |
Lets just say an int
|
Mark M. |
then no
|
Mark M. |
but, if it held the activity, or a widget in the activity, then that would prevent if from being garbage collected
|
EGHDK |
Okay, so why the widget?
|
Gabriele |
why the integer not, Mark?
|
Mark M. |
because the widget has a reference to the activity
|
EGHDK |
Button = (Button) findViewById(R.id.button); doesn't have an activity reference?
|
Mark M. |
Gabriele: an Integer does not hold onto anything other than an int primitive
|
Mark M. |
EGHDK: that is a Java statement, not an object, a variable, or a data member
|
Mark M. |
inside android.view.View, there is a data member that points to the activity
|
Mark M. |
Button inherits from View
|
Mark M. |
(indirectly)
|
EGHDK |
Yes.
|
Mark M. |
if you put the Button in a static data member, the Button cannot be garbage collected (it is a link nailed to the wall)
|
EGHDK |
But so when I call set contentView() I'm giving the view an activity?
|
Mark M. |
you are connecting a view to its activity, and vice versa
|
Mark M. |
an activity holds onto the root view (which represents the top of the view hierarchy for the activity's UI)
|
EGHDK |
Urg... but how? Where am I actually doing that?
|
Mark M. |
and each View has a reference back to the activity
|
Sep 2 | 8:15 PM |
Mark M. |
it is part of the implementation of Activity, View, LayoutInflater, etc.
|
Mark M. |
(BTW, Gabriele and Lucy, if you have a question on a different topic, let me know -- I'm letting this discussion run because you both indicated you were out of questions)
|
EGHDK |
So if all I did was `static Button myButton` defined in my class, then somewhere I actually set it to a button, and then the activity calls finish(), it will never be GC'd because of that static view?
|
Lucy |
Sure, I think I thought of a new flavor of my previous question...
|
Gabriele |
I have one, but this topic is interesting, too :P
|
Mark M. |
EGHDK: correct
|
Lucy |
I'm also enjoying that topic. My question is basic anyway I think...
|
Mark M. |
EGHDK: let me take these other questions, and we can circle back to this in a bit
|
EGHDK |
No prob
|
Mark M. |
Gabriele: let's take your question, as you're next in line
|
Gabriele |
Mark, with ViewPager, if the user is using a "small" screen, I'd like to use right-swipe to get the username list (remember my irc chat app?), if he's using a bigger screen (tablet) I'd like to show both the chat messages and the username list. The first one is easy achivied with ViewPager, but the second? Should I replace the ViewPager with two fragments aside, if the screen is bigger?
|
Mark M. |
that's a reasonable pattern
|
Mark M. |
the biggest thing is that trying to change approaches in a configuration change is tricky
|
Mark M. |
so for example, if in landscape you wanted them side by side, but in portrait you wanted them in pages in a ViewPager, you'll need my ArrayPagerAdapter or your own PagerAdapter
|
Mark M. |
FragmentPagerAdapter does not play nice with the other kids
|
Mark M. |
and FragmentStatePagerAdapter inherits that behavior from FragmentPagerAdapter
|
Gabriele |
ah, nice to know
|
Mark M. |
I seem to recall covering this scenario in the book, though I'm a bit fuzzy on that
|
Gabriele |
so in this case, I have two completely different approaches for small and bigger screen, it seemed strange to me
|
Mark M. |
well, think of it as you have two fragments, and just different ways of using them
|
Sep 2 | 8:20 PM |
Mark M. |
it's really not that different from other variations on the master/detail pattern
|
Mark M. |
check out the advanced ViewPager chapter, and I cover stuff in there that should help you
|
Gabriele |
ok, thank you, I will check inside the book if I find something, too
|
Gabriele |
yes, thanks
|
Mark M. |
Lucy: your turn! you had another question?
|
Lucy |
I want the user to be able to watch a video that they will only watch once or twice. The video won't be hosted on YouTube but I was thinking I could have the file that is the video, it could be hosted on the business web site as a resource. Then I'd download the video as a file on the fly, user watches it, then I delete automatically (at some appointed time). Does that sound reasonable?
|
Mark M. |
well, download-then-watch is slow for the user, as they have to wait for the download
|
Mark M. |
if your plan is to delete the video automatically, I'd stream it, which does not imply YouTube
|
Lucy |
Hm. Good point. To stream it, the user would have to be connected to the internet in that moment though?
|
Mark M. |
correct, but I kinda assumed that "download the video on the fly"
|
Mark M. |
meant that the user somewhere said "I want to view the video"
|
Mark M. |
and the downloading implies Internet access
|
Lucy |
Oh yeah, good point. :-) Maybe that's OK. For the audio snippets I will keep them local.
|
Lucy |
For the audio I don't want the user to need internet access. OK! Thanks!
|
Mark M. |
downloading is fine if the user might watch the video several times, but I'd only download it on user request if the file is big, as they may not want to pay for the bandwidth
|
Lucy |
Oh! Great point on bandwidth.
|
Lucy |
That's all my questions.
|
Mark M. |
OK
|
Mark M. |
EGHDK: back to you
|
EGHDK |
What else are common things that hold onto an activity, that I may not know about right away? Besides views?
|
Mark M. |
fragments do
|
EGHDK |
Since I never pass an activity into a view arguement, I didn't know that they held onto it.
|
Mark M. |
anything that has getActivity() will
|
Sep 2 | 8:25 PM |
Mark M. |
you pass a Context into any View constructor
|
EGHDK |
Really?
|
Mark M. |
and that Context usually is an Activity
|
EGHDK |
Where?
|
Mark M. |
it's the first parameter, if not the only parameter, on every constructor on every subclass of View
|
Mark M. |
that's because all constructors on View take a Context, and you have to chain to the superclass' constructor from your own
|
Mark M. |
in the case of layout inflation (e.g., setContentView(R.layout.main)), LayoutInflater invokes the proper constructors and passes the Activity into them
|
EGHDK |
Okay, so set Content View knows to pass in the activity?
|
EGHDK |
setContentView()
|
Mark M. |
setContentView() is a method on activity
|
Mark M. |
er, Activity
|
Mark M. |
it creates a LayoutInflater, which handles converting R.layout.main into the View hierarchy
|
EGHDK |
Gotcha.
|
Mark M. |
LayoutInflater is the one calling the constructors on the various subclasses of View
|
Mark M. |
and LayoutInflater is the one passing the Activity in as the Context to the constructors
|
EGHDK |
So calling setContentView() has access to the activity how? That's the only part I'm confused with? Why don't I do setContentView(Context, R.id)?
|
Mark M. |
setContentView() is a method on Activity
|
Mark M. |
Activity inherits from Context
|
Mark M. |
hence, in the implementation of setContentView(), the "this" object is the Activity instance
|
Sep 2 | 8:30 PM |
Mark M. |
and that's a wrap for today's chat
|
EGHDK |
Gotcha. So because I call setContentView ON an Activity it has access to the object that called it
|
Mark M. |
the transcript will be posted to http://commonsware.com/office-hours/ shortly
|
Mark M. |
EGHDK: correct
|
EGHDK |
so it doesn't have to specifically request a context?
|
Lucy | has left the room |
EGHDK |
Okay, I'll look into it thanks!
|
Gabriele |
thank you, Mark
|
Mark M. |
a quick note: Version 6.0 of the book will be released in ~14 hours
|
EGHDK |
Is this the big pivot?
|
Gabriele |
oh, nice :P
|
Mark M. |
the next office hours chat is slated for Thursday morning at 9am US Eastern Time
|
Mark M. |
EGHDK: it's the Android Studio portion of the pivot, yets
|
Mark M. |
er, yes
|
Mark M. |
have a pleasant day!
|
Gabriele |
you too, Mark, thanks!
|
Gabriele | has left the room |
EGHDK | has left the room |
Mark M. | turned off guest access |