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();
Related
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.
Let's assume I have a class MainActivity.
This contains a number of objects stored in fields, such as instances of Player, Enemy, Level, etc. Each of these objects needs to be able to refer to every other object.
What is the best way to go about this?
Make these fields static, and refer to them accordingly, i.e.
MainActivity.player.setHealth(0);
Create getter methods for each field, and simply pass each object a reference to MainActivity, so that they can call these getter methods, i.e.
mainActivity.getPlayer().setHealth(0);
Pass each object a reference to every other object, and store these references in fields within each object, so that they can be referred to directly, i.e.
player.setHealth(0);
Not a real answer but just to give you some tips.
Your Player should be like so:
public class Player
{
private static Player _player = null;
int _health;
...
public static Player getInstance()
{
if (_player == null)
_player = new Player(...);
return _player;
}
public void increaseHealth(int amount)
{
_health += amount;
}
}
Then in any part of your application when you need a Player you can do:
Player p = Player.getInstance();
and you will get the same player all the time. You can do a similar thing with your level class as only 1 level will be active at any one time.
However the Enemy class will need a different approach. I would make a List inside the Level class and get at them like so:
Level l = Level.getInstance();
List<Enemy> enemiesOnLevel = l.getEnemies();
// do something with them
Have a look in the Android docs here: http://developer.android.com/guide/faq/framework.html#3. There is also the possibility to serialize your object into primitive datatypes and pass those within your Intent to the new Activity.
A couple more options to share objects between activities are to use parcable, which I think is probably the highest performance method, and shared preferences.
In my app I used to learn (the little I know about android programming), I used gson to serialize the object to json, then stored it in shared preferences in activity A , then recreated it from shared preferences in activity B, and then stored it again.
I got an static hashmap in order to be able to instantiate multiple times the same class, and the hashmap will hold all the key-value reference.
class A {
public static final Map<String,Bitmap> map = new HashMap<String,Bitmap>();
// methods
}
So on every instance of the class A, when you call map.get(KEY), you'll always get the same bitmap. I'm getting an "Released unknown bitmap reference" IllegalStateException What am I doing wrong?
Sometimes, if Android needs memory, it deletes some variables.
So if your app is having a lot of big vars like Bitmaps in static variable, they can be deleted sometimes by the system. I had this problem with an app. The only solution I found is that you have to check on your getter if the object exists, if it doesn't, you'll have to recreate it...
Are you calling .recycle() on the bitmap?
If so when you come to get the bitmap from the hashmap it will no longer exist and throw that exception
i'm developing an app that, when i press a button, downloads a XML file, put the xml data in a custom object and passes it to a second activity.
The problem is that something is wrong: when a call the startActivity() function the app crashes with a Runtime error.
My code is:
public void onClickBtn1(View view)
{
final ProgressDialog dlg = ProgressDialog.show( this, "Data wait", "Waiting data from the site ..");
// Thread to wait data
Thread th = new Thread() {
public void run() {
// Download and parse xml data
final DatiSport dati = new DatiSport();
boolean ret = dati.download();
dlg.dismiss();
// check result
if (ret==true)
{
// -- Ok
handlerUI.post( new Runnable() {
public void run() {
Intent intSec = new Intent(AICSActivity.this, SportActivity.class);
intSec.putExtra("datiSport", dati);
startActivity(intSec);
}
});
}
else
{
The app crashes on the startActivity() call. When i break on the startActivity() line i'm not able to look the variable called 'dati' and i guess this is not well defined.
If i substitute dati with 12345, there is not problem.
Which is the problem with dati ?
--- Changed here cause I'm not enabled to reply myself ---
Ok guys. Thanks for replies!
My guess is that i need to re-design the app data.
My first attempt was: download the XML text and accommodate the data into a (rather) complex object. This object contain a list of championships, each of them contains a list of categories, each of them contains a list of teams.
The problem is that, since the Serializable is not working, the implementation of Parcelable is too complex and it should generate almost the same data as the xml file.
I'm wondering if it should be easier passing directly the xml text to other activities (they have to show in turn the list of championships, then the categories of a selected championship, then the list of teams for a selected category...)
Any other idea?
Extract from this Answer :
Serializable is a standard Java interface. You simply mark a class Serializable by implenting the interface, and Java will automatically serialize it in certain situations.
Parcelable is an Android specific interface where you implement the serialization yourself. It was created to be far more efficient that Serializable, and to get around some problems with the default Java serialization scheme.
Extract from this answer :
Seeing Parcelable might have triggered the question, why is Android
not using the built-in Java serialization mechanism? It turns out that
the Android team came to the conclusion that the serialization in Java
is far too slow to satisfy Android’s interprocess-communication
requirements. So the team built the Parcelable solution. The
Parcelable approach requires that you explicitly serialize the members
of your class, but in the end, you get a much faster serialization of
your objects.
After seeing some answer on StackOverFlow, i come to conclusion that Parcelable is optimized than Serialization in android.
How to make class to Parcelable ?? (Check out this, this & this tutorials)
Use a Serializable or Parcelable when passing objects
You need a class to implement the Serializable class
//to pass :
intent.putExtra("MyClass", obj);
// to retrieve object in second Activity
getIntent().getSerializableExtra("MyClass");
Your class would look something like this;
import java.io.Serializable;
#SuppressWarnings("serial") //with this annotation we are going to hide compiler warning
public class MyClass implements Serializable {
public Deneme(Object obj){
this.obj= obj;
}
private Object obj;
}
The Intent class has a method as
putExtra(String name, int value)
thats why it works when you put 12345 at the place of "value", but there is no overloaded version of putExtra that takes "DatiSport" object.
You must ensure that "DatiSport" is Serializable or Parcelable.
See below for more info-
http://developer.android.com/reference/android/content/Intent.html#putExtra%28java.lang.String,%20java.io.Serializable%29
How to send an object from one Android Activity to another using Intents?
How to pass an object from one activity to another on Android
Make your class implement Serializable interface and then pass object instances in intent extra.
To pass data from one Activity to another :
intent.putExtra("ClassName", obj);
To retrieve data in the Second Activity from the First Activity :
getIntent().getSerializableExtra("ClassName");
I found the problem !!!
An internal class were not implementing Serializable!
In the dump window i saw the internal object 'ioe' that said that there was a NotSerializable error and the name of the class!!
Now i checked each internal class and the data is passed to the next activity.
Thanks a lot
i have a class that inherits from BroadcastReceiver and is bound to listen for PHONE_STATE events. inside of the onReceive method, i need an object instance that has to be always the exact same (at least between the state ringing and the next occurrence of ide / offhook). that means i need to store the object somewhere. it can not be serialized nor anyhow be stored in a database or in the SharedPreferences.
i thought about 2 different approaches:
using a static variable. downside: no one knows at which point android is going to delete it.
using a service. downside: the service needs to be started at the first call and then bound. this is an async call and i might have to wait for an uncertain time. also it seems kinda wrong to use a service just to store one single object.
any other, better ideas?
Don't know if it will work in your situation, but I'm usually storing an object's string representation in SharedPreferences. You can override the toString() method, which will create the string representation, and implement a parse() method that will parse the saved string and initialize an object based on its saved state. Hope this helps.
third 3) Use a singleton instance of a custom class, then you may get variable from call to call , but not persistant (if application stop).. But useful from a time to another time in the runtime application life. To avoid as much as possible to have wiped data by android framework you may tie your singleton to a service that is "foreground" see http://developer.android.com/reference/android/app/Service.html#startForeground(int,%20android.app.Notification) by this way you get a higher memory detruction protection .. That is the way I currently use singleton in service.. made long time execution (~2 weeks with normal and heavy load activity) without any trouble ...
here an singleton example
class MyData {
private static MyData mMydata= null; // unique reference ( singleton objet container)
private MyObject myobject = null; // inside the unique object container we have the unique working object to be use by the application
// can't make instance from outside... we want to have single instance
// we want that outside use method "getInstance" to be able to use the object
private MyData() {
}
// retrieve and/or create new unique instance
public static MyData getInstance() {
if (mMydata == null) mMyData = new MyData();
return mMyData;
}
// Works with your memory stored object
// get...
public myObject getMyObject() {
return myobject;
}
// set ...
public void setMyObject(MyObject obj) {
myobject = obj;
}
}
in your application to handle your "working" object your may access it like
// get object
MyObject obj = MyData.getInstance().getMyObject();
// or set a object
MyData.getInstance().setMyObject(obj);