I have a class Crop that isn't Serializable when it comes to passing her trough an intent to another Activity. But if it is from an Activity to a Fragment (trough Bundles), there's no error and I can see the drawable object in the other side with no errors whatsoever.
How can it be serializable in one case but not in the other ?
public class Crop implements Serializable {
private String specieHarvest;
private String specieLocation;
private String specieName;
private Drawable img;
}
First, let's answer your question.
The reason of the behavior you are seeing is that
Passing objects as extras to an activity, will always cause that object to be marshaled/un-marshaled by Android, it is thought of as some kind of IPC.
Passing objects as arguments to a fragment will not always cause your object to be marshaled.
Then when is your object taken apart and reconstructed by Android when sent to a fragment?
It will not be serialized/de-serialized unless the fragment is destroyed and recreated for some reason, otherwise, that object is kept in a map and the same object is returned to you when you call getArguments().
If you even change that object in the Fragment, it will change the original one that was sent, because it is in fact the same reference. CRAZY, right? :)
If you take a look at Drawable class, you will find that it is not Parcelable or Serializable, so don't pass it along in intents/arguments.
Instead, pass an int which can be the resource ID if the Drawable object is in your resources, or you can pass a String path if that is an image that is stored locally somewhere.
Also, passing int or string is much better, because Drawables tend to be large is size, which might cause your app to crash due to size exceeded for an intent extras.
Here's an article where you can read about that as well.
Related
I need to pass objects to my fragments in order to initialize them.
Currently I am doing this with ((MyActivity)getActivity()).getX(). (direct access to the activity)
However, I would like to pass the required objects as parameter.
I definitely do not want to add parcelable objects to the bundle, since they require an excessive amount of useless boilerplate code. My goal is to reduce complexity, not increasing it.
And I do not want to add serializable objects to the bundle, since they are slow and cause an unnecessary overhead.
What is the best way to pass objects to fragments?
Any ideas to solve the problem in a more convenient way?
I definitely do not want to add parcelable objects to the bundle, since they require an excessive amount of useless boilerplate code. My goal is to reduce complexity, not increasing it.
You write this code in your model classes which is separated from your activities and fragments. There is no complexity in implementing Parcelable. And it is a common way to pass objects to a Fragment.
Any other solutions? Well, you still can do this ((MyActivity)getActivity()).getX() as long as your fragment is attached to your activity. In this case it is even faster than Parcelable because there is no serialization at all.
Other ways would be to write objects to database, pass their ids to a Fragment and then use a query to retrieve objects.
You can also use SharedPreferences, but that's rarely used. For this you will need to convert your object to String.
You can do the Android way: Parcelable.
You can serialize then.
You can do the poor way : static
You can do the retained way: Create a Fragment with setRetainInstance(true) and save your objects references.
I understand you don't want to use parcelable / serializable objects to a Bundle. I also agree with you since I got lazy, and my phone app is getting complicated.
Here's what you can do, and it works reliably.
Make a public method in your Fragment class, sample below.
Have the Activity, preferably no other place, call that public method. Remember Activity is always present, Fragments and Adapters may not due to its lifecycle.
The timing of the call is crucial if you're not using Bundles. I have used it without any problems.
The advantage of this technique is that it is fast, especially compared to Bundles. Many developers do not consider this however.
Note: If you are using simple fundamental Java types, do use Bundles! As suggested by Google.
Sample code:
public class MyFragment extends Fragment {
...
public void setList(final ArrayList<String> arrayList) {
...
}
In the Activity:
MyFragment fragment1 = MyFragment.newInstance(<parameters>);
fragment1.setList( arrayList );
Do you need to change the properties once they have been set on the fragment? If not, you can use setArguments(Bundle). If it is a fairly light object you can even skip implementing Parcelable and just set each property individually. The advantage is that the arguments are preserved upon orientation change. The disadvantage is that you need to call this method before attaching your fragment, hence it is not very useful once the fragment is in use.
It's way too late for my answer, but if someone else is wondering. The recommended way is to use Parcelable or Serializable, but you can always do something like this:
public class ObjectManager {
private static final String TAG = "ObjectManager";
private static ObjectManager instance;
private Object currentObject;
public static ObjectManager getInstance() {
if (instance == null)
instance = new ObjectManager();
return instance;
}
public Object getCurrentObject() {
return currentObject;
}
public void setCurrentObject(Object object) {
this.currentObject = object;
}
}
And then use it: where you needed as long as your app is running
//Use on the object you would like to save
ObjectManager.getInstance().setCurrentObject(object);
//Get the instance from pretty much everywhere
Object = ObjectManager.getInstance().getCurrentObject();
You can use it always, but it will be most likely to be useful, if you pass objects bigger than the Bundle max size.
I have an object which has the following class fields:
int, int, String, MyDatabaseType (custom object), List < MyDatabaseDetail > (array list of custom objects)
Is it possible for me to pass this through an intent/bundle?
I've played around a little bit with serializable and parcelable, but I couldn't get it working. Would I need to make all of custom object types parcelable, instead of just the main one that I want to pass?
Surely there is a better way?
When you pass data in an intent, it must be Parcelable. That is because the intent may passed to a different application, and thus a different VM. As your object may be crossing process boundaries, it needs a mechanism that will allow it to be saved/restored. This is analogous to passing data via a web service call (in this case, the object is "flattened" to something like either XML or JSON).
Even if the intent stays within your application, parcelability allows the object to survive even if Android chooses to kill/re-launch your application, which can happen if memory is running low.
Serialization can be used instead of parcelization, however parcelization is more efficient.
You can make your class Parcelable if it contains variables that are not serializable. If all variables inside your class are primitive or serializable or you can make them serializable, it's easy enough to do it.
You can follow this guide: http://developer.android.com/reference/java/io/Serializable.html
In most cases, all you have to do is to make your class implement Serializable interface and add the following line in your class:
private static final long serialVersionUID = 0L;
(read more in the guide above).
Is something wrong with this construct in Android?
class A extends Activity {
private Object myObject = new Object();
#Override
protected void onCreate(Bundle savedInstanceState) {
//myObject = new Object();
}
}
Because at some point(s) later I get (sometimes, not reproducible yet) exceptions because myObject is null. I don't know if it's because I have to initialize in onCreate.
Edit: Additional details:
The actual class of myObject is List<Object> (Where Object is a domain specific type)
At some point later in the activity I'm storing myObject as a static field of a "Parameter passer" class and starting other Activity (because I'm avoiding to implement Parcelable. If this is good or bad practice should not be discussed here, unless that's causing my error). In the other Activity I pick up myObject. There it's (sometimes) null.
Edit 2: I don't understand why this object becomes null if I'm storing a reference to it as static field of my parameter passer class (a standalone, dedicated class). That's how garbage collection works, right, it just removes when the objects are not referenced anymore. So since I have a static reference this object should not be removed. According to this thoughts, if they are correct, the problem should be somewhere else.
When you start a new activity your old one goes on the block for possible garbage collection (including any classes instantiated in it, including your parameter passer class), so your object is not necessarily going to be available (which is why you see an intermittent failure.).
I see two option:
1) Pass it along in the bundle with your intent that starts the new activity. As you were trying to avoid this, probably not your best choice.
2) Extend the Application class and store the object in there.
EDIT
I think the accepted answer to this SO Question might fix your issue (and explain what is actually happening).
No. That code is just fine. You can create objects in the constructor.
You may want to check a previous question about it Instance variable initialization in java and the section 3.2.4. Field Defaults and Initializers which basically states that the first case:
private Object myObject = new Object();
is identical to an initialization in the class constructor. (NOTICE onCreate is NOT the constructor).
So, myObject should never be null, except in the case the "new Object()" instruction failed, generating an exception.
Isn't this possible your code is changing the contents of myObject later on the code?
I have an Object that I need to be able to pass between Activites. It implements Parcelable and I've written all the code related to that.
The problem is that one of the properties of the Object is a Drawable - and really needs to be. Unfortunately, Drawable is neither Parcelable or Serializable. I don't understand how to pass it.
The reason for having the Drawable is that I need to cache an Image that I've downloaded from the internet at runtime. I don't want to cache the images on the filesystem, since this would potentially end up using up a lot of space over time.
I'm putting the image into a Drawable so that I can easily put it into an ImageView.
Any Ideas?
in your Application:
HashMap<String,Object> tempObjects = new H....
public Object getTempObject(String key) {
Object o = null;
o = tempObjects.get(key);
tempObjects.remove(key);
return o;
}
public void addTempObject(String key, Object object) {
tempObjects.put(key, object);
}
and cast the Object to Drawable on the way back. You may also add a boolean param in the get(), and remove the object from the map if it is true, that way you can access a certain temp object more than once, or remove it immediately if you are sure that you won't need it anymore in there
EDIT: sorry for the Exception catch, I pasted the code from a function where I have a HashMap<Class<?>, HashMap<String, Object>> for more detailed temp objects getter, where I am getting one hashMap as a value, and then getting the Object from it, that's why there was an NPE check in the code that I pasted first
You can store your unParcelable data in a custom ContentProvider,then pass the uri references to it.
You can't pass a complex object that isn't Serializable or Parcelable between activities. One option would be to cache the images in your custom Application class, and access them from there in your activity.
MyApplication application = (MyApplication)getAppliction();
Drawable drawable = application.getCachedDrawable();
This question already has answers here:
How to pass an object from one activity to another on Android
(35 answers)
Closed 9 years ago.
I need to be able to use one object in multiple activities within my app, and it needs to be the same object. What is the best way to do this?
I have tried making the object "public static" so it can be accessed by other activities, but for some reason this just isn't cutting it. Is there another way of doing this?
When you are creating an object of intent, you can take advantage of following two methods
for passing objects between two activities.
putParcelable
putSerializable
You can have your class implement either Parcelable or Serializable. Then you can pass around your custom classes across activities. I have found this very useful.
Here is a small snippet of code I am using
CustomListing currentListing = new CustomListing();
Intent i = new Intent();
Bundle b = new Bundle();
b.putParcelable(Constants.CUSTOM_LISTING, currentListing);
i.putExtras(b);
i.setClass(this, SearchDetailsActivity.class);
startActivity(i);
And in newly started activity code will be something like this...
Bundle b = this.getIntent().getExtras();
if (b != null)
mCurrentListing = b.getParcelable(Constants.CUSTOM_LISTING);
You can create a subclass of Application and store your shared object there. The Application object should exist for the lifetime of your app as long as there is some active component.
From your activities, you can access the application object via getApplication().
This answer is specific to situations where the objects to be passed has nested class structure. With nested class structure, making it Parcelable or Serializeable is a bit tedious. And, the process of serialising an object is not efficient on Android. Consider the example below,
class Myclass {
int a;
class SubClass {
int b;
}
}
With Google's GSON library, you can directly parse an object into a JSON formatted String and convert it back to the object format after usage. For example,
MyClass src = new MyClass();
Gson gS = new Gson();
String target = gS.toJson(src); // Converts the object to a JSON String
Now you can pass this String across activities as a StringExtra with the activity intent.
Intent i = new Intent(FromActivity.this, ToActivity.class);
i.putExtra("MyObjectAsString", target);
Then in the receiving activity, create the original object from the string representation.
String target = getIntent().getStringExtra("MyObjectAsString");
MyClass src = gS.fromJson(target, MyClass.class); // Converts the JSON String to an Object
It keeps the original classes clean and reusable. Above of all, if these class objects are created from the web as JSON objects, then this solution is very efficient and time saving.
UPDATE
While the above explained method works for most situations, for obvious performance reasons, do not rely on Android's bundled-extra system to pass objects around. There are a number of solutions makes this process flexible and efficient, here are a few. Each has its own pros and cons.
Eventbus
Otto
Maybe it's an unpopular answer, but in the past I've simply used a class that has a static reference to the object I want to persist through activities. So,
public class PersonHelper
{
public static Person person;
}
I tried going down the Parcelable interface path, but ran into a number of issues with it and the overhead in your code was unappealing to me.
It depends on the type of data you need access to. If you have some kind of data pool that needs to persist across Activitys then Erich's answer is the way to go. If you just need to pass a few objects from one activity to another then you can have them implement Serializable and pass them in the extras of the Intent to start the new Activity.
Your object can also implement the Parcelable interface. Then you can use the Bundle.putParcelable() method and pass your object between activities within intent.
The Photostream application uses this approach and may be used as a reference.