I was pretty sure that I understood the Android lifecycle and how applications/processes might be start/stopped/removed and how that effects whether variables remain initialized. I am, however, seeing some funky behavior in my app such that I am afraid I have missed something.
I use a singleton pattern for some of my objects. That is, the constructor is private and you call a static getInstance() method to construct the object. The static instance variable is, of course, initialized to NULL which is the trigger for getInstance to know whether the object needs to be constructed.
I have learned that any code that calls getInstance() cannot assume that the object it has constructed continues to live in memory since my application might have been killed by Android. So throughout my code I always call getInstance() to ensure that I have a valid object. Am I correct in assuming that had I been killed that getInstance() will create a new object? In other words, will my static instance variable have been reset to NULL or might my factory method give me back a stale pointer?
If your process is killed, the next time you run a fresh process must be created and re-initialized. There is no way to get a "stale" pointer across this. If the pointer was stale, you'd probably have a native crash because it would be completely unrelated to the current process you are running.
You can verify the behavior yourself just by using adb shell to kill your process.
On Wed, Jun 1, 2011 at 5:39 PM, Jake Colman <col...@ppllc.com> wrote:
> I was pretty sure that I understood the Android lifecycle and how > applications/processes might be start/stopped/removed and how that > effects whether variables remain initialized. I am, however, seeing > some funky behavior in my app such that I am afraid I have missed > something.
> I use a singleton pattern for some of my objects. That is, the > constructor is private and you call a static getInstance() method to > construct the object. The static instance variable is, of course, > initialized to NULL which is the trigger for getInstance to know whether > the object needs to be constructed.
> I have learned that any code that calls getInstance() cannot assume that > the object it has constructed continues to live in memory since my > application might have been killed by Android. So throughout my code I > always call getInstance() to ensure that I have a valid object. Am I > correct in assuming that had I been killed that getInstance() will > create a new object? In other words, will my static instance variable > have been reset to NULL or might my factory method give me back a stale > pointer?
> ...Jake
> -- > Jake Colman -- Android Tinkerer
> -- > You received this message because you are subscribed to the Google > Groups "Android Developers" group. > To post to this group, send email to android-developers@googlegroups.com > To unsubscribe from this group, send email to > android-developers+unsubscribe@googlegroups.com > For more options, visit this group at > http://groups.google.com/group/android-developers?hl=en
Note: please don't send private questions to me, as I don't have time to provide private support, and so won't reply to such e-mails. All such questions should be posted on public forums, where I and others can see and answer them.
So as long as I always call my getInstance() class I can be certain that I will get a pointer to the already constructed object or that the static instance variable had been reset to NULL triggering new construction?
DH> If your process is killed, the next time you run a fresh process DH> must be created and re-initialized. There is no way to get a DH> "stale" pointer across this. If the pointer was stale, you'd DH> probably have a native crash because it would be completely DH> unrelated to the current process you are running.
DH> You can verify the behavior yourself just by using adb shell to DH> kill your process.
DH> On Wed, Jun 1, 2011 at 5:39 PM, Jake Colman <col...@ppllc.com> wrote:
>> >> I was pretty sure that I understood the Android lifecycle and how >> applications/processes might be start/stopped/removed and how that >> effects whether variables remain initialized. I am, however, seeing >> some funky behavior in my app such that I am afraid I have missed >> something. >> >> I use a singleton pattern for some of my objects. That is, the >> constructor is private and you call a static getInstance() method to >> construct the object. The static instance variable is, of course, >> initialized to NULL which is the trigger for getInstance to know whether >> the object needs to be constructed. >> >> I have learned that any code that calls getInstance() cannot assume that >> the object it has constructed continues to live in memory since my >> application might have been killed by Android. So throughout my code I >> always call getInstance() to ensure that I have a valid object. Am I >> correct in assuming that had I been killed that getInstance() will >> create a new object? In other words, will my static instance variable >> have been reset to NULL or might my factory method give me back a stale >> pointer? >> >> ...Jake >> >> >> -- >> Jake Colman -- Android Tinkerer >> >> -- >> You received this message because you are subscribed to the Google >> Groups "Android Developers" group. >> To post to this group, send email to android-developers@googlegroups.com >> To unsubscribe from this group, send email to >> android-developers+unsubscribe@googlegroups.com >> For more options, visit this group at >> http://groups.google.com/group/android-developers?hl=en >>
DH> Note: please don't send private questions to me, as I don't have time to DH> provide private support, and so won't reply to such e-mails. All such DH> questions should be posted on public forums, where I and others can see and DH> answer them.
DH> -- DH> You received this message because you are subscribed to the Google DH> Groups "Android Developers" group. DH> To post to this group, send email to android-developers@googlegroups.com DH> To unsubscribe from this group, send email to DH> android-developers+unsubscribe@googlegroups.com DH> For more options, visit this group at DH> http://groups.google.com/group/android-developers?hl=en
The code below will work fine for a singleton: ... private static MyObject myObject = null; public static MyObject getMyObject() { if (myObject == null { myObject = new MyObject(); } return myObject;
}
Note that variable won't be 'reset' to null. There is nothing to reset when your process is killed. It is gone, nohting left.
When a *new* process is started, i.e. the process that will run your app again, then the static fields are all properly initialized (in this case, 'myObject' to the value 'null').
> If your process is killed, the next time you run a fresh process must be
> created and re-initialized. There is no way to get a "stale" pointer across
> this. If the pointer was stale, you'd probably have a native crash because
> it would be completely unrelated to the current process you are running.
> You can verify the behavior yourself just by using adb shell to kill your
> process.
> On Wed, Jun 1, 2011 at 5:39 PM, Jake Colman <col...@ppllc.com> wrote:
> > I was pretty sure that I understood the Android lifecycle and how
> > applications/processes might be start/stopped/removed and how that
> > effects whether variables remain initialized. I am, however, seeing
> > some funky behavior in my app such that I am afraid I have missed
> > something.
> > I use a singleton pattern for some of my objects. That is, the
> > constructor is private and you call a static getInstance() method to
> > construct the object. The static instance variable is, of course,
> > initialized to NULL which is the trigger for getInstance to know whether
> > the object needs to be constructed.
> > I have learned that any code that calls getInstance() cannot assume that
> > the object it has constructed continues to live in memory since my
> > application might have been killed by Android. So throughout my code I
> > always call getInstance() to ensure that I have a valid object. Am I
> > correct in assuming that had I been killed that getInstance() will
> > create a new object? In other words, will my static instance variable
> > have been reset to NULL or might my factory method give me back a stale
> > pointer?
> > ...Jake
> > --
> > Jake Colman -- Android Tinkerer
> > --
> > You received this message because you are subscribed to the Google
> > Groups "Android Developers" group.
> > To post to this group, send email to android-developers@googlegroups.com
> > To unsubscribe from this group, send email to
> > android-developers+unsubscribe@googlegroups.com
> > For more options, visit this group at
> >http://groups.google.com/group/android-developers?hl=en
> Note: please don't send private questions to me, as I don't have time to
> provide private support, and so won't reply to such e-mails. All such
> questions should be posted on public forums, where I and others can see and
> answer them.
Note that this will only work single-threaded. Add the synchronized keyword to the method and make the static data member volatile to make it safe for multi-threaded access.
(a problem I ran into with WakefulIntentService, which is why this issue is fresh in my mind...)
On Wed, Jun 1, 2011 at 10:34 PM, Streets Of Boston
<flyingdutc...@gmail.com> wrote: > The code below will work fine for a singleton: > ... > private static MyObject myObject = null; > public static MyObject getMyObject() { > if (myObject == null { > myObject = new MyObject(); > } > return myObject; > }
> Note that variable won't be 'reset' to null. There is nothing to reset when > your process is killed. It is gone, nohting left.
> When a new process is started, i.e. the process that will run your app > again, then the static fields are all properly initialized (in this case, > 'myObject' to the value 'null').
> -- > You received this message because you are subscribed to the Google > Groups "Android Developers" group. > To post to this group, send email to android-developers@googlegroups.com > To unsubscribe from this group, send email to > android-developers+unsubscribe@googlegroups.com > For more options, visit this group at > http://groups.google.com/group/android-developers?hl=en
Thanks for all the replies. My use of the singleton pattern was correct as was confirmed by these posts. I did find my issues - one of which _was_ due to not handling lifecyle correctly - and now all is well.
...Jake
>>>>> "MM" == Mark Murphy <mmur...@commonsware.com> writes:
MM> Note that this will only work single-threaded. Add the MM> synchronized keyword to the method and make the static data MM> member volatile to make it safe for multi-threaded access.
MM> (a problem I ran into with WakefulIntentService, which is why MM> this issue is fresh in my mind...)
MM> On Wed, Jun 1, 2011 at 10:34 PM, Streets Of Boston MM> <flyingdutc...@gmail.com> wrote: >> The code below will work fine for a singleton: >> ... >> private static MyObject myObject = null; >> public static MyObject getMyObject() { >> if (myObject == null { >> myObject = new MyObject(); >> } >> return myObject; >> } >> >> Note that variable won't be 'reset' to null. There is nothing to reset when >> your process is killed. It is gone, nohting left. >> >> When a new process is started, i.e. the process that will run your app >> again, then the static fields are all properly initialized (in this case, >> 'myObject' to the value 'null'). >> >> >> -- >> You received this message because you are subscribed to the Google >> Groups "Android Developers" group. >> To post to this group, send email to android-developers@googlegroups.com >> To unsubscribe from this group, send email to >> android-developers+unsubscribe@googlegroups.com >> For more options, visit this group at >> http://groups.google.com/group/android-developers?hl=en
MM> -- MM> You received this message because you are subscribed to the Google MM> Groups "Android Developers" group. MM> To post to this group, send email to android-developers@googlegroups.com MM> To unsubscribe from this group, send email to MM> android-developers+unsubscribe@googlegroups.com MM> For more options, visit this group at MM> http://groups.google.com/group/android-developers?hl=en
On Fri, Jun 3, 2011 at 10:38 AM, Chris <crehb...@gmail.com> wrote: > While thread-safety is on your mind, you should know that double-checked > locking doesn't always fix that problem either.
It did according to my sources. Got a better pattern?
Using a static helper inner-class that constructs and holds your singleton eliminates the synchronization overhead and is safe and works across JVM versions. This is the initialization on-demand holder pattern.
I believe they fixed DCL in Java 5 as long as you use volatile (by the way, I completely overlooked you mentioning setting the static instance to volatile in your previous post - so for Android your suggestion would work assuming you're ok with synchronization overhead) but since DCL was an anti-pattern years ago I've stopped using it anyway.
On Fri, Jun 3, 2011 at 11:33 AM, Chris <crehb...@gmail.com> wrote: > Using a static helper inner-class that constructs and holds your singleton > eliminates the synchronization overhead and is safe and works across JVM > versions. This is the initialization on-demand holder pattern.
public class MySingleton { *private* static mySingleton = null;
public static *synchronized* getMySingleton() { if (mySingleton == null) { mySingleton = new MySingleton(); } return mySingleton; }
does not fix this?
The construction and returning of mySingleton is synchronized *statically*, on the MySIngleton class. It's true that *every* getXXXX (even when mySingleton was already constructed) is now synchronized, but it is thread-safe, i think.
If you are responding to Streets of Boston, that code is not doing double check locking. It is doing the only null check with the lock held.
Personally I prefer to explicitly use synchronized() instead of synchronized methods for legibility and to avoid leaking locking semantics out of a class, but that is a separate issue. :)
> The pattern, while common, was broken prior to Java 5 and was fixed after > but only if mySingleton is declared volatile.
> -- > You received this message because you are subscribed to the Google > Groups "Android Developers" group. > To post to this group, send email to android-developers@googlegroups.com > To unsubscribe from this group, send email to > android-developers+unsubscribe@googlegroups.com > For more options, visit this group at > http://groups.google.com/group/android-developers?hl=en
Note: please don't send private questions to me, as I don't have time to provide private support, and so won't reply to such e-mails. All such questions should be posted on public forums, where I and others can see and answer them.
The above article is what reminded me that I had forgotten about the volatile keyword in my original WakefulIntentService. See the "Fixing Double-Checked Locking using Volatile" section.
The "static helper inner-class" and similar static initialization approaches work well when there's no configuration necessary, and I use those in many places for managing API level-dependent stuff in Android. In my WakefulIntentService case, though, I am initializing a WakeLock, and so I need a Context to get the PowerManager system service. Hence, double-checked locking with volatile is, AFAIK, as good as it gets for that scenario, modulo perhaps using synchronized() per Ms. Hackborn.
The locking is very explicit and controlled. I think that using double null checks for this kind of stuff is very premature optimization, you'd be better off calling the method once when you need the instance and keeping the pointer around.
The method takes an Application object instead of a Context to prevent mistakes where the developer hands in a transient Context such as for an Activity. Another way to deal with that though is to explicitly retrieve the application context from any given context:
static final Object sLock = new Object(); static MySingleton sInstance;
On Fri, Jun 3, 2011 at 10:14 AM, Chris <crehb...@gmail.com> wrote: > Not really, but to the thread in general.
> Since this has gotten off topic of Android development, I'd like to try to > pull it back...
> Mark if you're still paying attention, how would this come up within the > context of .. a Context? I'm having trouble seeing a useful example.
> -- > You received this message because you are subscribed to the Google > Groups "Android Developers" group. > To post to this group, send email to android-developers@googlegroups.com > To unsubscribe from this group, send email to > android-developers+unsubscribe@googlegroups.com > For more options, visit this group at > http://groups.google.com/group/android-developers?hl=en
Note: please don't send private questions to me, as I don't have time to provide private support, and so won't reply to such e-mails. All such questions should be posted on public forums, where I and others can see and answer them.
I think this stuff would deserve an Android Dev blog enttry. While I
understand what you are saying, remembering it in 6 months time is a
different story. If there were a blog post to refer back to, it would
be a lot easier.
On Jun 4, 2:29 am, Dianne Hackborn <hack...@android.com> wrote:
> The locking is very explicit and controlled. I think that using double null
> checks for this kind of stuff is very premature optimization, you'd be
> better off calling the method once when you need the instance and keeping
> the pointer around.
> The method takes an Application object instead of a Context to prevent
> mistakes where the developer hands in a transient Context such as for an
> Activity. Another way to deal with that though is to explicitly retrieve
> the application context from any given context:
> static final Object sLock = new Object();
> static MySingleton sInstance;
> On Fri, Jun 3, 2011 at 10:14 AM, Chris <crehb...@gmail.com> wrote:
> > Not really, but to the thread in general.
> > Since this has gotten off topic of Android development, I'd like to try to
> > pull it back...
> > Mark if you're still paying attention, how would this come up within the
> > context of .. a Context? I'm having trouble seeing a useful example.
> > --
> > You received this message because you are subscribed to the Google
> > Groups "Android Developers" group.
> > To post to this group, send email to android-developers@googlegroups.com
> > To unsubscribe from this group, send email to
> > android-developers+unsubscribe@googlegroups.com
> > For more options, visit this group at
> >http://groups.google.com/group/android-developers?hl=en
> Note: please don't send private questions to me, as I don't have time to
> provide private support, and so won't reply to such e-mails. All such
> questions should be posted on public forums, where I and others can see and
> answer them.- Hide quoted text -
Its really not android specific, just best practices when dealing with synchronized access to singleton objects.
Dianne, thanks for the example but maybe I misunderstood Mark's post about static holders and Contexts. I thought he meant, and I was seeking an example where the Context itself is the singleton which, except for eg. an Application context doesn't seem to make much sense. But I don't do much with services so maybe I'm missing some common patterns in Android.
Good thread nonetheless. New programmers thrust into a multithreaded framework should at least know that synchronization is critical.
> Its really not android specific, just best practices when dealing with > synchronized access to singleton objects.
> Dianne, thanks for the example but maybe I misunderstood Mark's post about > static holders and Contexts. I thought he meant, and I was seeking an > example where the Context itself is the singleton which, except for eg. an > Application context doesn't seem to make much sense. But I don't do much > with services so maybe I'm missing some common patterns in Android.
> Good thread nonetheless. New programmers thrust into a multithreaded > framework should at least know that synchronization is critical.
Except it's not multithreaded, for the most part...
I feel that the lifecycle (of apps and activities) is not well
documented. I wish the Javadocs were somehow wikified so the vague
portions could be collaboratively identified and remedied. I only
feel I am able to identify places where questions arise, so having the
ability through OSP (I suppose) to edit these is not such a boon.
My app has longstanding bugs that some phones see and others do not,
and they tie into lifecycle issues.
Here are some of the questions I find I still have.
The Activity documentation does not clearly indicate the difference
between an activity being paused versus being stopped. Is a paused
activity one which has 1 or more pixels obscured by another, and a
stopped activity one that has zero visible pixels?
I have never found just where the the interrelationships between
finish() and state diagram paths toward pause/stop and the use of the
back button or home button are detailed.
Shouldn't "finish()" have been called "destroy()" so it is consistent
with the state diagram? If so, the documentation should spell out how
finish()ing an activity will start it through the path to onDestroy().
There seems to be no direct means by which my app (or do I mean my
task?) can know when one of its activities is active versus not. This
has confused me for one and a half years (it matters when you make an
app that has a voice user interface and want the recognition to be
stopped when the user presses home, but not when he presses "back" on
an activity that returns another of your app's activities to the
fore). It gets worse in that starting a new activity does not deliver
you an Activity instance immediately, making instance counting
difficult.
I have resisted using the manifest flags for "singletop" et al
entirely, as several readings of their description have not conveyed a
clear understanding.
On Sat, Jun 4, 2011 at 1:20 PM, DulcetTone <dulcett...@gmail.com> wrote: > The Activity documentation does not clearly indicate the difference > between an activity being paused versus being stopped. Is a paused > activity one which has 1 or more pixels obscured by another, and a > stopped activity one that has zero visible pixels?
"Paused: Another activity is in the foreground and has focus, but this one is still visible. That is, another activity is visible on top of this one and that activity is partially transparent or doesn't cover the entire screen. A paused activity is completely alive (the Activity object is retained in memory, it maintains all state and member information, and remains attached to the window manager), but can be killed by the system in extremely low memory situations."
"Stopped: The activity is completely obscured by another activity (the activity is now in the "background"). A stopped activity is also still alive (the Activity object is retained in memory, it maintains all state and member information, but is not attached to the window manager). However, it is no longer visible to the user and it can be killed by the system when memory is needed elsewhere."
> I have never found just where the the interrelationships between > finish() and state diagram paths toward pause/stop and the use of the > back button or home button are detailed.
BACK calls finish() by default, though an activity can override onBackPressed() to change this behavior.
HOME does not -- it merely brings the home activity to the foreground.
> There seems to be no direct means by which my app (or do I mean my > task?) can know when one of its activities is active versus not.
Correct, no more than a Web server knows if a Web page is "active" or not.
It is if you want to do anything smartly with remote content. Five seconds or so before an ANR isn't much time to get stuff over HTTP with even a 3g connection.
Regardless of ANRs, doing networking on the same thread as UI is unavoidably going to result in a janky and crummy UI experience. Heck, even doing disk IO will result in some amount of UI jerkiness and significant IO like database operations should always be done on another thread.
On Sat, Jun 4, 2011 at 12:33 PM, Chris <crehb...@gmail.com> wrote: > It is if you want to do anything smartly with remote content. Five seconds > or so before an ANR isn't much time to get stuff over HTTP with even a 3g > connection.
> -- > You received this message because you are subscribed to the Google > Groups "Android Developers" group. > To post to this group, send email to android-developers@googlegroups.com > To unsubscribe from this group, send email to > android-developers+unsubscribe@googlegroups.com > For more options, visit this group at > http://groups.google.com/group/android-developers?hl=en
Note: please don't send private questions to me, as I don't have time to provide private support, and so won't reply to such e-mails. All such questions should be posted on public forums, where I and others can see and answer them.