References in Java. Considerations in Android.
References are handled differently across different programming languages. Given that I work with a number of different languages it is important that I have a clear understanding of how they work.
References in Java have caused me a few issues - issues that are mainly a result of their utilisation within the context of the Android framework.
This post seeks to clarify how references work in Java whilst also outlining some considerations when working with Android.
Jon Skeet outlines the most important points quite definitively:
- "Everything in Java is passed by value
- "The values of variables are always primitives or references, never objects."
int x = 0;
//We pass the value 0 to foo - 0 is an int (primitive)
foo(x);
String y = "This is a string";
//We pass a reference (by value) to the String object containing "This is a string"
bar(y);
Another useful explanatory quote that I found here is:
"Java modifies the meaning of 'pass by reference' from languages like C/C++. In C/C++, the term 'pass by reference' means passing the value of the variable's address (this is what a pointer is!)"
There is an analogy pertaining to balloons which states that passing an object variable to a function is akin to attaching another string to a balloon.
If in the bar
function mentioned above one were to state y = new Thing()
you would essentially be reattaching the string to a new balloon.
Significance in Android
The significance of this within Android is that often you might have a Thing
instance in your activity, a property of which you would like to update.
You want to also have access to this Thing
in a displayed Fragment
, and you want it to be the case that updating the variable in your Activity
will make the changes in your Fragment
too.
Example
In my Activity
I have an instance property var thing = new Thing();
.
Within my onCreate
method I create a Fragment
using a static newInstance
method (see here) and add it to my view. What exactly have I passed to my Fragments initializer?
When I pass the variable thing
to my Fragments initialiser I am attaching another string to the same instance of Thing
in the heap.
If I now change a property of the Thing
within my Fragment
e.g thing.key = "newKey";
, then that change will be reflected if I access it within the Activity
because both 'strings' point to the same object.
The problem
The problem that arises can be demonstrated in this simple demo that I have put together.
When the initial Activity
is created we instantiate a new Thing
instance. The constructor for Thing
sets the value of a key
property to the current timestamp.
We pass this Thing
when instantiating our Fragment
and display the values of the key
properties.
At this point we have two 'strings' pointing to the same balloon. The value outputted is the same because the 'strings' point to the same Thing
object in the heap.
If I now click the Button, we change the value of the key
property of the Thing
referenced by the Fragment
. This updates the key
property of the Thing
referenced by the activity as well because they both reference the same object.
If however we now rotate our screen, the Activity
is recreated by the Android framework. The Fragment
however has setRetainInstance(true)
specified and as such the Android framework maintains the Fragment
instance.
At this point the values displayed for the key
property of the Activity
and Fragment
differ. The Activity
has been recreated. A new balloon has been created, and a new string attached to it. The Fragment
however still has a 'string' pointing to the original Thing object on the heap.
If now we press the Button, the key
property of the Thing
referenced in the Fragment
increments. The key
property of the Thing
referenced by the Activity
does not - the respective 'strings' now point to different balloons.
The third line (output on screen) pertains to a second fragment that I have created for demonstrations purposes - a 'Data Fragment'. This is a viewless Fragment
which simply holds and retains data. You will notice that when following the steps above the value for the key
property of the MainFragment
and the DataFragment
stays in sync. This indicates that when retaining Fragments (on orientation change for example), the Android framework is not doing anything untoward with our variables - the 'strings' in both point to the same balloon.
savedInstanceState
As a matter of intrigue, I also wanted to investigate the situation whereby a Fragment
is completely destroyed, its state saved in savedInstanceState
and then restored.
The Fragment documentation clearly states that in some situations the Fragment
instance may still be completely destroyed:
"There are many situations where a fragment may be mostly torn down (such as when placed on the back stack with no UI showing), but its state will not be saved until its owning activity actually needs to save its state."
I was concerned that perhaps in the process of parcelling a Parcelable
object and then restoring it from a Bundle
our 'strings' might be manipulated by the Android framework. This turns out not to be the case ! Wahoo !
In the demo (linked above), I have saved the Thing
instance of our MainFragment
and restored it in onViewStateRestored
(e.g after an orientation change). The fact that pressing the Button after an orientation change updates the key
value for both MainFragment
and DataFragment
indicates that everything is how we would like.
Extras
At this point I was becoming increasingly intrigued, and as such wanted to see how 'extras' work in respect of the subject matter. Sadly in this case the results were less positive :(
You may have been wondering as to why there is a 'Start new activity' button in my demo. Well, the reason is so that we can see how the Android framework handles passing variables to new Activities by proxy of 'extras'.
When we click this button, we pass an extra containing our Thing
instance from MainFragment
.
If you look at the logcat output of the new Activity
you will see that the key
value of the Thing
passed to the new Activity
is one and the same as that from the MainFragment
(to be expected).
What i have then done is implemented an update to the Thing
key
in MainFragment
. This is done on a 5 second delay I.E After opening the second Activity, 5 seconds later we update thing
in MainFragment
.
In the onCreate
method of ExtraActivity
we have set a 6 second timer after which we will check the key
property of the Thing
contained within the ExtraActivity
.
We would hope (in an ideal world) that these values would be one and the same, and that the strings are pointing to the same balloons. Sadly they are not.
I have had a look through the Android source code to try and gain an insight into why this is the case. On the face of it, both saving instance state and passing extras transmit data by proxy of a Bundle
. I will of course follow up if I gain any further insight on this. If you have any ideas, perhaps you could fill me in in the comments?
Implications
The implications of this are pretty obvious - namely that when working with data that needs to be utilised and manipulated in multiple locations, you need to be very careful about where your variables point.
If you are not, you may end up with data integrity issues across areas of your application.
Hopefully this has provided a little clarity on references within Java, and provides insight into the appropriate patterns to utilise when developing Android applications.
If you have any questions or comments, please feel free to post them. I am always happy to help.