The question is simple, if I make an object parcelable, and put it into a bundle and create a fragment using the bundle. Does the object get cloned or is it referenced.
The context.
I have got an object stored/referenced in an ArrayList. Depending on the type of object in the ArrayList (polymorphism is used). I create a fragment suitable for dealing with it.
I need to also pass this object to the fragment. The fragment is used within a custom view pager. I do not wish to have duplicate objects and it seems to me parcelable clones objects.
Another method is to pass the index of the object in the ArrayList. and then get a reference to the arraylist from the fragment using getActivity().myList.get(Integer passed to ). But it doesn't seem very safe to me (ArrayList contents may change, although I simply delete everything and start again). I have also read, that you should avoid passing arguments to a fragments constructor as it may be recreated using the default no-args constructor.
(Although I'm currently destroying any recreated fragments as there are some strange problem with reattaching to the correct view, another post).
New sub-question: is there a way to pass a value by reference to a fragment?
The question is simple, if I make an object parcelable, and put it into a bundle and create a fragment using the bundle. Does the object get cloned or is it referenced.
It may get cloned, if not immediately, at other points in time (e.g., when the fragment's arguments Bundle is included in the instance state).
I have also read, that you should avoid passing arguments to a fragments constructor as it may be recreated using the default no-args constructor.
Correct.
Another method is to pass the index of the object in the ArrayList. and then get a reference to the arraylist from the fragment using getActivity().myList.get(Integer passed to ). But it doesnt seem very safe to me (ArrayList contents may change, although I simply delete everything and start again).
Don't use an ArrayList. Use a HashMap with a durable key. Pass the key to the fragment. Have the fragment get the data via the key. Make sure anyone deleting this object (and thereby removing it from the HashMap) does so only when this fragment does not exist, or notifies this fragment so it knows how to handle this scenario.
Related
I was looking at the google samples for MVP and I saw this as the last statement in onCreate of the activity:
new TaskDetailPresenter(
taskId,
Injection.provideTasksRepository(getApplicationContext()),
taskDetailFragment);
This code seems weird to me.
It instantiates an object (TaskDetailPresenter) that is local and not assigned anywhere and in it associates the fragment with the presenter.
Classes defined here
Is this really how it is supposed to be done? Because it seems not a good practice to me
In TaskDetailPresenter constructor they have:
mTaskDetailView.setPresenter(this);
So the presenter object is passed to the fragment ("View" in MVP). And that fragment stores a reference to the presenter. This is ok - View can easily communicate with its presenter with this configuration.
But the trick they use here - calling setPresenter(this) before the constructor finishes is a bad thing. Here you can find an explanation of this problem: http://www.javapractices.com/topic/TopicAction.do?Id=252
It may be ok if that reference is just saved to a property and everything happens in one thread. But something may change in the future and we can get into trouble.
I use the putExtra and getSerializable Methods to pass my object to a second activity. It works fine, however, am I required to return this object in order to maintain the changes made in the second activity?
When I run my app, and launch my second activity then call finish() after makimg a change to the object passed to it, if I relaunch that second activity the old object data previous to the change is displayed, does this mean that using the put/get serializable methods are passing a clone of the object, and that in order to keep the changes made on the second activity I must repass the object back to the main activity ?!
I am not sure why would you require such behaviour.However you can try the following methods.
You can make that object as global static variable(preferably in the application class of the app) so that the object is retained between different instances of the activities.
Also if the state of the object is important across app restarts you must plan to write the state of the object in some persistent storage like db/file/shared preference.Refer this link for storing object,
Is it possible to save a reference to an object in a bundle?
Not the whole object, only the reference?
For example, Bundle class has method to get an object but I can't find the put or set method.
I need it because in an activity, I create an instance of my timer class, and then I start the timer: myTimer.start()
If I have to restart the activity, I want to restore the timer to it's previous value.
Hopefully you cannot do this. You can only store primitives in bundle. As Marcin Orlowski mentioned "storing" the whole object is achieveable through implementing Parcelable interface. By "storing" I meant storing object state. Implementing this interface helps you persisting your object state in different code sections without putting its all attributes to Bundle object over and over again.
When activity goes to pause state sooner or later all objects used by your activity will be removed by garbage collector, so storing references to them would be silly.
The official docs recommend using fragments for storing references during "configuration changes" (no, I don't think this means you need to repose your activity as a fragment, but to use a fragment as a storage medium (clarification needed)):
http://developer.android.com/guide/topics/resources/runtime-changes.html#RetainingAnObject
Retaining an Object During a Configuration Change
If restarting your activity requires that you recover large sets of data, re-establish a
network connection, or perform other intensive operations, then a full
restart due to a configuration change might be a slow user experience.
Also, it might not be possible for you to completely restore your
activity state with the Bundle that the system saves for you with the
onSaveInstanceState() callback—it is not designed to carry large
objects (such as bitmaps) and the data within it must be serialized
then deserialized, which can consume a lot of memory and make the
configuration change slow. In such a situation, you can alleviate the
burden of reinitializing your activity by retaining a Fragment when
your activity is restarted due to a configuration change. This
fragment can contain references to stateful objects that you want to
retain.
When the Android system shuts down your activity due to a
configuration change, the fragments of your activity that you have
marked to retain are not destroyed. You can add such fragments to your
activity to preserve stateful objects.
To retain stateful objects in a fragment during a runtime
configuration change:
Extend the Fragment class and declare references to your stateful
objects. Call setRetainInstance(boolean) when the fragment is created.
Add the fragment to your activity. Use FragmentManager to retrieve the
fragment when the activity is restarted. For example, define your
fragment as follows:
public class RetainedFragment extends Fragment {
// data object we want to retain
private MyDataObject data;
// this method is only called once for this fragment
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// retain this fragment
setRetainInstance(true);
}
public void setData(MyDataObject data) {
this.data = data;
}
public MyDataObject getData() {
return data;
}
}
Caution: While you can store any object, you should never pass an
object that is tied to the Activity, such as a Drawable, an Adapter, a
View or any other object that's associated with a Context. If you do,
it will leak all the views and resources of the original activity
instance. (Leaking resources means that your application maintains a
hold on them and they cannot be garbage-collected, so lots of memory
can be lost.)
You can't. You can only pass limited set of data types as you can see by looking at method lists in the docs and if you want more than single primitives then your class needs to implement Parcelable interface (or use helper like Parceler). Once you got that done you will be able to pass your object data via the Bundle (but still, not object itself)
One alternate approach is to convert the POJO to json and then to string the GSON library and save in bundle as string. Then retrieve it back from bundle as string and convert it back to object using the same GSON library. Example code below.
Convert JSON to String and save it in bundle
JsonElement json = new JsonElement();
String result = gson.toJson(json);
bunlde.putString("key",result);
Convert String from bundle to object again
JsonElement json = gson.fromJson(bundle.getString("key"), JsonElement.class);
I want to pass an ArrayList of custom Objects to a FragmentActivity. Each Fragment will have information populated by an individual Object.
My question is, when I use the set methods (in the custom Object) in each Fragment, will that correctly update my ArrayList of these custom Objects so that I can pass this updated ArrayList of Objects to another Activity?
Yes, as long as the reference to the object is the same the changes are made.
I have an app that consists mostly of one instance that is a tree of objects. Every time my phone sleeps or I switch app my app resets and all the data within this tree of objects is lost.
I first tried using onSaveInstanceState() but the savedinstancestate bundle would not allow me to pass on the instance of the object I needed. I tried to pass the object off as a parcel but the objects within my object could not be saved to the parcel.
Lastly I tried using getNonConfigurationInstance which didn't appears to work and I abandoned getting it to work when I looked up that it was depreciated. The alternative to it is for API11, I am using API7.
Any advice appreciated, I've read the documentation through but cannot find anything short of going through all my objects and saving each variables one by one to save my instance state.
Thanks
You should use getNonConfigurationInstance only in conjunction with onRetainNonConfigurationInstance, because the former retains the object and the latter saves it. Also, they work only for configuration changes — i.e., when activity is immediately recreated in the same process.
If objects on your tree are simple, you can try putting it into a JSON format and then serializing/deserializing it. I have once done that to persist state of my controller hierarchy, worked great.
You can put Bundles inside Bundles with putBundle(String key, Bundle value).
You can put Parcelables inside Bundles with putParcelable(String key, Parcelable value).
Options 2, 3 and 4 will require you to write a recursive converter of your tree into/from either JSONObjects, Bundles or Parcelables.