Android - Context in Parcelable Class - android

I have the following in a class that is implementing Parcelable:
private static final long serialVersionUID = 66;
private HashMap<Integer, Bitmap> mBitmaps;
private HashMap<Integer, Drawable> mDrawables;
private Context mContext;
And:
#Override
public void writeToParcel(Parcel dest, int flags) {
// TODO Auto-generated method stub
dest.writeValue(mBitmaps);
dest.writeValue(mDrawables);
dest.writeValue(mContext);
dest.writeByte((byte) (mActive ? 0x01 : 0x00));
}
I get the error:
java.lang.RuntimeException: Parcel: unable to marshal value android.app.Application#4051cfe0
at android.os.Parcel.writeValue(Parcel.java:1132)
at com.example.example.ImageManager.writeToParcel(ImageManager.java:82)
dest.writeValue(mContext); is on line 82.

As stated in this answer: https://stackoverflow.com/a/6112919/7721253
You are trying to add to the parcel objects which are not serializable. Objects within the objects you are serializing have to be serializable as well.

You are writing to Parcel.
Did you implemeted Parcelable to do so ?
You need to implement the Serializable or Parcelable interface. Otherwise the value will not be marsheled as the error suggests.
Also,
Classes implementing the Parcelable interface must also have a static
field called CREATOR, which is an object implementing the
Parcelable.Creator interface.
Check about android Parcelable.

Just write a little Class thats holding your Context-Object like this:
import android.content.Context;
public class ContextHolder {
public static Context context = null;
public static void setContext(Context context){
ContextHolder.context = context;
}
public static Context getContext(){
return context;
}
}
then whenever you need the Context, you can just call the ContextHolder.getContext() function from everywhere you need it.
Cheers

Related

How do I access Android SharedPreferences from a static method in a class without passing in a context reference?

I have just one class where I need to access SharedPreferences:
public class MyUtils {
public static String packageMe(Object input){
// do stuff here
// need SharedPreferences here
}
public static Object unpackageMe(String input){
// do stuff here
// need SharedPreferences here
}
}
I tried this:
public class MyUtils extends Activity
But, as you know, I cannot access SharedPreferences from a static method.
I thought about passing in the context to the static methods, but that extends the number of classes out to four that I will need to modify, and the classes are already extending AsyncTask:
public class SomeClass01 extends AsyncTask {
#Override
protected Object doInBackground(Object[] params){
MyUtils.packageMe(abc_123_object);
// do stuff here
}
}
So, I thought that maybe I could pass the context into those four classes. However, there are a couple dozen classes that I would need to modify that use those four classes, that in turn use that single class.
public class SomeTopClass extends FragmentActivity implements x, y, z {
new SomeClass01.execute(abc_123_object);
// do stuff here
}
I don't know if I want to be passing a context reference that deep into my code.
I saw here on StackOverflow about putting a reference to the SharedPreferences in my abc_123_object model, but there are quite a few objects I use (other than abc_123_object) and I don't want to have to jerry-rig so many classes.
So, is there a way for me to do this without modifying dozens of classes and passing context references all around my code, or am I stuck?
Thanks
Create static variable in your Application class.
public class MyApp extends Application{
public static Context context;
#Override
public void onCreate() {
context = this;
}
}
Then use it when you need.
public static String packageMe(Object input){
// do stuff here
// need SharedPreferences here
// context = MyApp.context
}
As Dusan mentioned, using an application class is an easy way to do this:
In your application class:
private static MyApplication sInstance = null;
private SharedPreferences mPrefs
public static MyApplication getApp()
{
return sInstance;
}
public SharedPreferences getSharePreferences()
{
return mPrefs;
}
in onCreate():
sInstance = this;
mPrefs = getSharedPreferences(PREF_FILE, MODE_PRIVATE);
Then in your code simply do:
MyApplication.getApp().getSharePreferences();
Your Application's onCreate() is guaranteed to be executed before any activity is created, so unless you are doing something really weird, it should be safe.

readFromParcel() method in Parcelable interface

I have a simple doubt in marshaling during service creation. When there is a writeToParcel() method declared in Parcelable interface which is invoked in stub generated (if aidl method parameters are declared as in), why there is no readFromParcel() declaration in Parcelable interface(for out parameters)?
I can create my own readFromParcel() but as per my understanding there should be a overridden readFromParcel() declaration in Parcelable interface if the generated stub wants to invoke it. But the documentation for Parcelable interface does not show any sign of readFromParcel() method. Why is it so? Was it included in previous API version and later got removed? Please explain !
And how different is createFromParcel() from readFromParcel() if both tries to read a parcelable object and populate member fields with the data out of it?
This is because you have declared a parameter of that type as "inout" in your AIDL.
When returned from the method, generated AIDL proxy will call readFromParcel() to update the parameter value (as defined by the "inout" qualifier).
createFromParcel is exactly what it sounds like. A NEW Intance of the parcelable Object/Class that has been written to parcel : Parcelable.writeToParcel() is created. This is a good thing, as it helps prevent memory leaks, as you are not holding on to a reference to the object from another class that may or may not have been destroyed
From the documentation of Parcelable interface :
public class MyParcelable implements Parcelable {
private int mData;
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel out, int flags) {
out.writeInt(mData);
}
public static final Parcelable.Creator<MyParcelable> CREATOR
= new Parcelable.Creator<MyParcelable>() {
public MyParcelable createFromParcel(Parcel in) {
return new MyParcelable(in);
}
public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};
private MyParcelable(Parcel in) {
mData = in.readInt();
}
}
When an object of a class which implements the Parcelable interface is to be written to a Parcel, writeToParcel(...) is called.
When an object of this class is to be created using a Parcel, CREATOR.createFromParcel(Parcel in) is called. From there onwards, how the class creates its instance from the Parcel is upto the developer of the class. In the above example, a constructor private MyParcelable(Parcel in) is called from the createFromParcel(...) method.
Conventionally, many developers define a readFromParcel(Parcel in) method in their implementations and call it from the constructor:
private MyParcelable(Parcel in) {
readFromParcel(in);
}
private void readFromParcel(Parcel in) {
mData = in.readInt();
}

Parcelable error sending object from one to another activity

I cant resolve problem when i sending my object "filmovi" to another activity i got a error. when i was tried to send another object "korisnik" it works without any problem.
Error
FATAL EXCEPTION: main
java.lang.ClassCastException: ba.fit.kino.model.filmovi cannot be cast to android.os.Parcelable
Sending from activity
filmovi Film = ((filmovi)lstView.getItemAtPosition(position));
Intent intent = new Intent(getApplicationContext(), RezervacijaActivity.class)
intent.putExtra("Rezervacija", Film);
startActivity(intent);
Reciving in activity
filmovi filmoviRezervacija;
Bundle bundle = getIntent().getExtras();
if(bundle != null){
filmoviRezervacija = bundle.getParcelable.("Rezervacija");
}
I RESOLVE PROBLEM WITHT THIS:
public class filmovi implements Parcelable{......
public filmovi (Parcel source)
{
this.setFilmID(source.readInt());
this.setNaziv(source.readString());
this.setCijenaKarte(source.readFloat());
this.setSalaID(source.readInt());
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest,int flags)
{
dest.writeInt(this.filmID);
dest.writeString(this.naziv);
dest.writeFloat(this.cijenaKarte);
dest.writeInt(this.salaID);
}
public static final Parcelable.Creator<filmovi> CREATOR = new Parcelable.Creator<filmovi>() {
#Override
public filmovi createFromParcel(Parcel source) {
return new filmovi(source);
}
#Override
public filmovi[] newArray(int size) {
return new filmovi[size];
}
};
}
The reason is that your filmovi class is not parcelable
To make filmovi, or any class for that matter, parcelable, the class and all of its inner members should
implement the parcelable interface, and implement a writeToParcel method which loosely speaking
streams the class' content.
Here, for example
class MyClass implements parcelable {
private MyMemberDataClass data; <----- must also implement parcelable
void writeToParcel(Parcel dest, int flags) {...}
}
It is not enough that MyClass will implement parcelable.
MyMemberDataClass (i.e. the inner member class) must do so as well.
This may bet complicated. And in many cases it is also not really necessary...
instead, consider using an activity-parameters static object to which you will pass all of
your activity's required params without the need to parcel them!:
filmovi Film = ((filmovi)lstView.getItemAtPosition(position));
Intent intent = new Intent(getApplicationContext(), RezervacijaActivity.class)
RezervacijaActivityParams.setValue(Film); <--------------- instead of putExtra()
startActivity(intent);
Where:
class RezervacijaActivityParams {
private static filmovi Film;
public static void getValue(filmovi f) { Film = f; }
public static filmovi getValue() { return Film; }
}
and in RezervacijaActivity's onCreate:
class RezervacijaActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
filmovi = RezervacijaActivityParams.getValue();
RezervacijaActivityParams.setValue(null); <---------- clear static data
}
}
Note, and this is also an answer to kcoppock's comment, that it is a good practice for
your activity to clear the static data immediately after retrieving it.
EDIT: As kcoppock mentioned, you can't place Objects into Intents as extras unless they're serializable or parcelable. Therefore, your Film class will need to implement one of those. I've only ever added native types (int, etc.) to Intents, so I did not know this. Something to watch out for!
As an aside, it's good practice to extract your key strings to static final values. That ensures that the same string ends up in each location you use it.

Extending a class that implements Parcelable

I have a class, we'll call it class A, that implements Parcelable.
I have a second class, we'll call it class B, that extends class A.
My question is:
How do I write class B's member variables to the Parcel and then write it's parent class's (ie: class A's) member variables to the Parcel (and, subsequently, read them in)?
Is there some nifty trick to not needing to rewrite class A's Parcel code? Or do I just need to rewrite the Parcel code in class A and add additional code for class B's member variables?
How do I write class B's member variables to the Parcel and then write it's parent class's (ie: class A's) member variables to the Parcel
Class B overrides writeToParcel() from Class A, chaining to the superclass and also adding its own objects to the Parcel.
(and, subsequently, read them in)?
Class B implements public static final Parcelable.Creator<MyParcelable> CREATOR in such a way that it can let both classes read their stuff in. If you take the approach of creating a constructor on Class B that takes a Parcel as a constructor parameter, just chain to the superclass constructor (to let Class A do its work), then read Class B's data.
The key will be to do them both in the same order. If you intend to let Class A read its data first, Class A must write its data first.
Is there some nifty trick to not needing to rewrite class A's Parcel code?
Inheritance and chaining to the superclass.
Adding an example, the marked answer is indeed correct, but something more visual seems more suitable for this situation:
This would be the supper class:
public class BasePojo implements Parcelable {
private String something;
//what ever other constructor
//getters and setters
protected BasePojo(Parcel in) {
something = in.readString();
}
public static final Creator<BasePojo> CREATOR = new Creator<BasePojo>() {
#Override
public BasePojo createFromParcel(Parcel in) {
return new BasePojo(in);
}
#Override
public BasePojo[] newArray(int size) {
return new BasePojo[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(something);
}
}
And then this would be the child class:
public class ChildPojo extends BasePojo implements Parcelable {
private int somethingElse;
//what ever other constructor
//getters and setters
protected ChildPojo(Parcel in) {
super(in);
somethingElse = in.readInt();
}
public static final Creator<ChildPojo> CREATOR = new Creator<ChildPojo>() {
#Override
public ChildPojo createFromParcel(Parcel in) {
return new ChildPojo(in);
}
#Override
public ChildPojo[] newArray(int size) {
return new ChildPojo[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
super.writeToParcel(parcel, i);
parcel.writeInt(somethingElse);
}
}
The marked answer provides a very good explanation, calling super is the key.
It is a little complex, but the trick is to use Reflection to get the types of subclass's members and to sort the members so that you can read and write the data back in the same exact order using the proper types.
I have implemented the solution for class A here: https://github.com/awadalaa/Android-Global-Parcelable
so now you can make any class parcelable by simply extending this class.

Implementing Parcelable Class That Requires Context

I'd like for my data class to implement Parcelable so it can be shared between Activities, however it also needs reference to Context so the fields can be saved to SQLiteDatabase.
This however is a problem since Parcelable.Creator method createFromParcel only has one parameter Parcel.
public abstract class Record implements Parcelable {
protected Context context;
protected String value;
public Record(Context context) {
this.context = context;
}
public Record(Parcel parcel) {
this.value = parcel.readString();
}
public void save() {
//save to SQLiteDatabase which requires Context
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int flag) {
parcel.writeString(value);
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public Record createFromParcel(Parcel parcel) {
return new Record(in);
}
public Record[] newArray(int size) {
return new Record[size];
}
};
}
How can a class that implements Parcelable also reference Context so it save to SQLiteDatabase?
The Parcelable interface is like the Java interface Serializable. Objects which implement this interface should be serializable. This means it should be possible to transform the object to a representation which could be saved in a file e.g.
It is easily possible for a string, int, float or double etc, because they all have a string representation. The Context class is clearly not serializable and not parcelable, because it can be an Activity for example.
If you want to save the state of your activity to a database, you should find another way to do that.
Your Record class probably doesn't really need access to the SQL database. The reason for it is exactly the problem you have now: it's very difficult to inject the Context back into each Record.
Perhaps a better solution would be to implement a static RecordSQLService, that has method save(Record r). Your app could start RecordSQLService when the app launches, so it will remain alive as long as your app does, and it takes the responsibility of saving away from the Record class, which makes it so you don't need Context anymore and can Parcel it.

Categories

Resources