Runtime exception : ClassCastException android.os.Bundle cannot be cast to Parcelable class - android

I get this crash when I installed the new code and ran it, below I have attached the old and the new code. Please let me know how can we fix things like these?
Old Code:
// data class
public class X {
int val;
....
....
}
public final HelperClass {
static Bundle toBundle(X obj) {
toDataMap(obj).toBundle();
}
static DataMap toDataMap(X obj) {
DataMap dataMap = new DataMap();
dataMap.putInt("id", obj.val);
....
....
return dataMap;
}
}
// used in an activity on a onClick
context.startActivity(new Intent(ACTION_VIEW)
.putExtra(X_INSTANCE_EXTRA, HelperClass.toBundle(new X())));
// received in the activity started by the intent
X obj = bundle.getBundle(X_INSTANCE_EXTRA);
New Code :
// data class
public class X implements Parcelable {
int val;
....
....
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(val);
...
...
}
public static final Parcelable.Creator<X> CREATOR =
new Parcelable.Creator<X>() {
#Override
public X createFromParcel(Parcel in) {
X obj = new X();
x.val = in.readInt();
...}
};
}
// used in an activity on a onClick
context.startActivity(new Intent(ACTION_VIEW)
.putExtra(X_INSTANCE_EXTRA, new X());
// received in the activity started by the intent
X obj = intent.getParcelableExtra(X_INSTANCE_EXTRA);
I get the error at the last line of the new code stating
java.lang.ClassCastException: android.os.Bundle cannot be cast to X
Is it because of some intent fired on old code and received in the new code?

If you want to make changes like this to code that is already in the field (installed on user's devices) then you need to make your changes backwards compatible. One example would be to use a different key for X_INSTANCE_EXTRA (ie: change the name) so that there are no conflicts with older code. Another way would be to expect the exception and verify the type of object returned here:
X obj = intent.getParcelableExtra(X_INSTANCE_EXTRA);
like this:
try {
X obj = intent.getParcelableExtra(X_INSTANCE_EXTRA);
// Extra is the correct type, continue processing...
} catch (ClassCastException e) {
// Not of the correct type, so this is old stuff, do something appropriate...
}
This is kinda ugly and I wouldn't normally do this. You need to think about backwards compatibility when you make changes to software that is already out in the field. Try to make your changes so that the old stuff doesn't break.

Related

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.

Error with implementing Parcelable object

I'm kind of new to Android development.
I don't understand why the following code gives me a stackoverflowerror
Intent intent = new Intent(view.getContext(), MakeCall.class);
SipParcelable sipp = new SipParcelable(_sip);
intent.putExtra("sip", (Parcelable) sipp);
startActivity(intent);
Basically as soon as the startActivity(intent) fires, I get the following error:
I can get rid of the error by commenting out the third line with the putExtra() function.
I'm trying to pass my _sip object over to the MakeCall.class activity on another screen that's about to load up. I tried to follow the tutorial on how to implement a Parcelable class/object. Here's what my SipParcelable code looks like:
import com.myproject.library.SipService;
import android.os.Parcel;
import android.os.Parcelable;
public class SipParcelable implements Parcelable{
public SipService mData;
/* everything below here is for implementing Parcelable */
// 99.9% of the time you can just ignore this
public int describeContents() {
return 0;
}
// write your object's data to the passed-in Parcel
public void writeToParcel(Parcel out, int flags) {
out.writeValue(mData);
}
public SipParcelable(SipService sip)
{
mData = sip;
}
// Parcelling part
public SipParcelable(Parcel in){
mData = (SipService) in.readValue(SipService.class.getClassLoader());
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public SipParcelable createFromParcel(Parcel in) {
return new SipParcelable(in);
}
public SipParcelable[] newArray(int size) {
return new SipParcelable[size];
}
};
}
What am I doing wrong?
Your SipService class must implement parcelabe and modify how SipService object is read and written from/to pracel.
check this tutorial it might help you
http://shri.blog.kraya.co.uk/2010/04/26/android-parcel-data-to-pass-between-activities-using-parcelable-classes/
You can use serialisable too… But parcelable is faster and better
NOTE: all properties of an object (if the properties are objects) that implements parcelable, must also be parcelable as well.

Class not found when unmarshalling when passing Parcelable through Messenger to remote service

I have a Parcelable object which I use to pass it from Activity to remote service. When I pass it using AIDL interface, everything sounds fine.
Recently, I try to pass it through Messenger from Activity.
// TEST TEST TEST!
StockInfo stockInfo0 = new StockInfo(Code.newInstance("code0"), Symbol.newInstance("symbol0"));
StockInfo stockInfo1 = new StockInfo(Code.newInstance("code1"), Symbol.newInstance("symbol1"));
StockInfo stockInfo2 = new StockInfo(Code.newInstance("code2"), Symbol.newInstance("symbol2"));
List<StockInfo> stockInfos = new ArrayList<StockInfo>();
stockInfos.add(stockInfo0);
stockInfos.add(stockInfo1);
stockInfos.add(stockInfo2);
StockInfosEx stockInfosEx = new StockInfosEx(stockInfos, "abc");
msg.obj = stockInfosEx;
try {
mService.send(msg);
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
I'm getting the following exception in remote service.
02-21 22:55:16.546: E/Parcel(8365): Class not found when
unmarshalling: com.example.testonmessenger.StockInfosEx, e:
java.lang.ClassNotFoundException:
com.example.testonmessenger.StockInfosEx
I was wondering, what can get wrong in between? Here is my Parcelable object.
public class StockInfosEx implements Parcelable {
public final List<StockInfo> stockInfos;
public final String searchedString;
public StockInfosEx(List<StockInfo> stockInfos, String searchedString) {
this.stockInfos = stockInfos;
this.searchedString = searchedString;
}
////////////////////////////////////////////////////////////////////////////
// Handling Parcelable nicely.
public static final Parcelable.Creator<StockInfosEx> CREATOR = new Parcelable.Creator<StockInfosEx>() {
public StockInfosEx createFromParcel(Parcel in) {
return new StockInfosEx(in);
}
public StockInfosEx[] newArray(int size) {
return new StockInfosEx[size];
}
};
private StockInfosEx(Parcel in) {
stockInfos = new ArrayList<StockInfo>();
in.readTypedList(stockInfos, StockInfo.CREATOR);
searchedString = in.readString();
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeTypedList(stockInfos);
parcel.writeString(searchedString);
}
// Handling Parcelable nicely.
////////////////////////////////////////////////////////////////////////////
}
To get complete source code, kindly download from https://www.dropbox.com/s/n69yuhddpb8vedz/testonmessenger.zip
Not Workable Approach (Because our Parcelable is custom, not part of Framework like Rect)
Activity
msg.obj = stockInfosEx;
Remote Service
StockInfosEx stockInfosEx = (StockInfosEx)msg.obj;
Workable Approach
Activity
msg.getData().putParcelable("data", stockInfosEx);
Remote Service
msg.getData().setClassLoader(StockInfosEx.class.getClassLoader());
StockInfosEx stockInfosEx = (StockInfosEx)msg.getData().getParcelable("data");
Now, after I read back the documentation of msg.obj (http://developer.android.com/reference/android/os/Message.html#obj) again, only I understand what it really mean by Parcelable of a framework class
An arbitrary object to send to the recipient. When using Messenger to
send the message across processes this can only be non-null if it
contains a Parcelable of a framework class (not one implemented by the
application). For other data transfer use setData(Bundle).
Note that Parcelable objects here are not supported prior to the FROYO
release.
You're probably not using the right ClassLoader. You need to keep track of the ClassLoader that is marshalling the class in the first place, and use THAT ClassLoader to unmarshall it.
When unmarshalling, you're using current thread's ClassLoader, which is not your UIThread but Android system thread, and as such, has no info about your custom classes.
I used a static class that contained my ClassLoader to solve this (similar approaches can be used without having it to be static).
Something like:
ClassLoaderHelper.setClassLoader(Thread.currentThread().getContextClassLoader());
Then when unmarshalling:
public final void readFromParcel(final Parcel in) {
id = in.readString();
appInfo = in.readParcelable(ClassLoaderHelper.getClassLoader());
...
}
See this other question for more detailed information (probably a duplicate btw).
I'm getting the following exception in remote service.
If you are truly getting this from the remote service, it is because the remote service app does not contain that class. If you are going to use custom Parcelable classes, both the client and the server must have the same class definition.
If, however, your stack trace feels like your Parcelable is being accessed from a core OS process, then you cannot pass Parcelable objects via obj reliably. I have only ever used obj on Message for in-process object passing, never for cross-process messages.

Android:Passing a hash map between Activities

I have a map between string and a class object. I populate this map in Activity1 and i would like to pass it to activity2.
public class NEW extends Activity {
public class data {
String name;
float value;
.... etc }
......
static Map<String, data> data_map = new HashMap<String, data>();
..... }
The best way to do this is if you can express your data in the primitives supported by Bundle, so it can be placed in the Intent you are sending through the Intent.putExtra() methods. (EXCEPT for the use of Serializable, which is extremely slow and inefficient.)
However you can't do this because (a) you are using a Map and (b) your map contains a custom data type.
The formally correct solution to this exact problem is to write a custom Parcellable class that takes care of marshalling/unmarshalling your data structure. I'll sketch out the code here, though it may not be exactly correct:
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Parcelable.Creator;
public class MyData implements Parcelable {
HashMap<String, data> data_map = new HashMap<String, data>();
public MyData() {
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int parcelableFlags) {
final int N = data_map.size();
dest.writeInt(N);
if (N > 0) {
for (Map.Entry<String, data> entry : data_map.entrySet()) {
dest.writeString(entry.getKey());
data dat = entry.getValue();
dest.writeString(dat.name);
dest.writeFloat(dat.value);
// etc...
}
}
}
public static final Creator<MyData> CREATOR = new Creator<MyData>() {
public MyData createFromParcel(Parcel source) {
return new MyData(source);
}
public MyData[] newArray(int size) {
return new MyData[size];
}
};
private MyData(Parcel source) {
final int N = source.readInt();
for (int i=0; i<N; i++) {
String key = source.readString();
data dat = new data();
dat.name = source.readString();
dat.value = source.readFloat();
// etc...
data_map.put(key, dat);
}
}
}
Note that when you have a custom data structure like your "data" class, it can be cleaner to also make that Parcellable, so it knows how to read/write its contents in a Parcel, and the code here would just call .writeToParcel(...) and a .readFromParcel(...) method on it instead of knowing the details of its contents. That way when you add new fields to "data" you don't forget to also update this other marshalling code to know about them.
I am assuming you own both Activities (call them A and B). In which case just put the map in a public static variable and access it from B via A.data_map.
[update]
For all of the downvotes take a peek at the Android Application Framework FAQ section "How do I pass data between Activities/Services within a single application?". The solution I recommend is exactly the same as...
A public static field/method
An alternate way to make data
accessible across Activities/Services
is to use public static fields and/or
methods. You can access these static
fields from any other class in your
application. To share an object, the
activity which creates your object
sets a static field to point to this
object and any other activity that
wants to use this object just accesses
this static field.
Yes, there are caveats to this solution but with the limited info presented by the OP we can not assume this method will not work.
I would suggest using Intents, which work for both static and arbitary objects (as long as they implement Serializable). Create a custom Intent for your application and then pass on your HashMap (not a Map, which doesn't implement Serializable!) as extra data:
Intent act2 = new Intent(Activity2.SHOW_ME);
act2.putExtra("data", data_map);
Then in Activity2, you can call getIntent() and check via Intent.getAction().equals(Activity2.SHOW_ME) whether you were the one calling your Activity. If so, you can access your extra data by
Intent caller = getIntent();
if (caller.getAction().equals(Activity2.SHOW_ME)) {
Map<String, NEW.data> data_map = (Map<String, NEW.data>)caller.getExtras().get("data");
}
Hope I typed everything correctly ;) This assumes, that your Intent action-string is stored as static final string SHOW_ME = "yourpackage.yourname"; in Activity2.
If you need further clarification, add a comment.
You can encapsulate your data in a Bound Service as described in this document:
http://developer.android.com/guide/topics/fundamentals/bound-services.html
Don't forget to add your server to the manifest file.
this solution has the advantage of decoupling your data from your UI, which will ultimately lead to a more maintainable design.
If the need arrises you can add a messaging wrapper to your service's API to allow it to be called from other processes / applications
Create a new java file which will be global for whole application.
Step1:
public class GlobalClass extends android.app.Application
{
public static Map<String, NEW.data> data_map = new Map<String, NEW.data>();
}
Step2:
After doing that, Register this "GlobalClass" in AndroidManifest.xml
<application
android:name=".GlobalClass"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name" >
</application>
Step3:
now you can use this Map anywhere in your application.
#Override
protected void onCreate(Bundle icicle)
{
GlobalClass global = (GlobalClass)getApplication();
Map<String, NEW.data> my_map_data = global.data_map;
}
These steps maybe helpful for you...

Passing enum or object through an intent (the best solution)

I have an activity that when started needs access to two different ArrayLists. Both Lists are different Objects I have created myself.
Basically I need a way to pass these objects to the activity from an Intent. I can use addExtras() but this requires a Parceable compatible class. I could make my classes to be passed serializable but as I understand this slows down the program.
What are my options?
Can I pass an Enum?
As an aside: is there a way to pass parameters to an Activity Constructor from an Intent?
This is an old question, but everybody fails to mention that Enums are actually Serializable and therefore can perfectly be added to an Intent as an extra. Like this:
public enum AwesomeEnum {
SOMETHING, OTHER;
}
intent.putExtra("AwesomeEnum", AwesomeEnum.SOMETHING);
AwesomeEnum result = (AwesomeEnum) intent.getSerializableExtra("AwesomeEnum");
The suggestion to use static or application-wide variables is a really bad idea. This really couples your activities to a state managing system, and it is hard to maintain, debug and problem bound.
ALTERNATIVES:
A good point was noted by tedzyc about the fact that the solution provided by Oderik gives you an error. However, the alternative offered is a bit cumbersome to use (even using generics).
If you are really worried about the performance of adding the enum to an Intent I propose these alternatives instead:
OPTION 1:
public enum AwesomeEnum {
SOMETHING, OTHER;
private static final String name = AwesomeEnum.class.getName();
public void attachTo(Intent intent) {
intent.putExtra(name, ordinal());
}
public static AwesomeEnum detachFrom(Intent intent) {
if(!intent.hasExtra(name)) throw new IllegalStateException();
return values()[intent.getIntExtra(name, -1)];
}
}
Usage:
// Sender usage
AwesomeEnum.SOMETHING.attachTo(intent);
// Receiver usage
AwesomeEnum result = AwesomeEnum.detachFrom(intent);
OPTION 2:
(generic, reusable and decoupled from the enum)
public final class EnumUtil {
public static class Serializer<T extends Enum<T>> extends Deserializer<T> {
private T victim;
#SuppressWarnings("unchecked")
public Serializer(T victim) {
super((Class<T>) victim.getClass());
this.victim = victim;
}
public void to(Intent intent) {
intent.putExtra(name, victim.ordinal());
}
}
public static class Deserializer<T extends Enum<T>> {
protected Class<T> victimType;
protected String name;
public Deserializer(Class<T> victimType) {
this.victimType = victimType;
this.name = victimType.getName();
}
public T from(Intent intent) {
if (!intent.hasExtra(name)) throw new IllegalStateException();
return victimType.getEnumConstants()[intent.getIntExtra(name, -1)];
}
}
public static <T extends Enum<T>> Deserializer<T> deserialize(Class<T> victim) {
return new Deserializer<T>(victim);
}
public static <T extends Enum<T>> Serializer<T> serialize(T victim) {
return new Serializer<T>(victim);
}
}
Usage:
// Sender usage
EnumUtil.serialize(AwesomeEnum.Something).to(intent);
// Receiver usage
AwesomeEnum result =
EnumUtil.deserialize(AwesomeEnum.class).from(intent);
OPTION 3 (with Kotlin):
It's been a while, but since now we have Kotlin, I thought I would add another option for the new paradigm. Here we can make use of extension functions and reified types (which retains the type when compiling).
inline fun <reified T : Enum<T>> Intent.putExtra(victim: T): Intent =
putExtra(T::class.java.name, victim.ordinal)
inline fun <reified T: Enum<T>> Intent.getEnumExtra(): T? =
getIntExtra(T::class.java.name, -1)
.takeUnless { it == -1 }
?.let { T::class.java.enumConstants[it] }
There are a few benefits of doing it this way.
We don't require the "overhead" of an intermediary object to do the serialization as it's all done in place thanks to inline which will replace the calls with the code inside the function.
The functions are more familiar as they are similar to the SDK ones.
The IDE will autocomplete these functions which means there is no need to have previous knowledge of the utility class.
One of the downsides is that, if we change the order of the Emums, then any old reference will not work. This can be an issue with things like Intents inside pending intents as they may survive updates. However, for the rest of the time, it should be ok.
It's important to note that other solutions, like using the name instead of the position, will also fail if we rename any of the values. Although, in those cases, we get an exception instead of the incorrect Enum value.
Usage:
// Sender usage
intent.putExtra(AwesomeEnum.SOMETHING)
// Receiver usage
val result = intent.getEnumExtra<AwesomeEnum>()
You can make your enum implement Parcelable which is quite easy for enums:
public enum MyEnum implements Parcelable {
VALUE;
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(final Parcel dest, final int flags) {
dest.writeInt(ordinal());
}
public static final Creator<MyEnum> CREATOR = new Creator<MyEnum>() {
#Override
public MyEnum createFromParcel(final Parcel source) {
return MyEnum.values()[source.readInt()];
}
#Override
public MyEnum[] newArray(final int size) {
return new MyEnum[size];
}
};
}
You can then use Intent.putExtra(String, Parcelable).
UPDATE: Please note wreckgar's comment that enum.values() allocates a new array at each call.
UPDATE: Android Studio features a live template ParcelableEnum that implements this solution. (On Windows, use Ctrl+J)
You can pass an enum through as a string.
public enum CountType {
ONE,
TWO,
THREE
}
private CountType count;
count = ONE;
String countString = count.name();
CountType countToo = CountType.valueOf(countString);
Given strings are supported you should be able to pass the value of the enum around with no problem.
For passing an enum by intent, you can convert enum into integer.
Ex:
public enum Num{A ,B}
Sending(enum to integer):
Num send = Num.A;
intent.putExtra("TEST", send.ordinal());
Receiving(integer to enum):
Num rev;
int temp = intent.getIntExtra("TEST", -1);
if(temp >= 0 && temp < Num.values().length)
rev = Num.values()[temp];
Best regards.
:)
If you really need to, you could serialize an enum as a String, using name() and valueOf(String), as follows:
class Example implements Parcelable {
public enum Foo { BAR, BAZ }
public Foo fooValue;
public void writeToParcel(Parcel dest, int flags) {
parcel.writeString(fooValue == null ? null : fooValue.name());
}
public static final Creator<Example> CREATOR = new Creator<Example>() {
public Example createFromParcel(Parcel source) {
Example e = new Example();
String s = source.readString();
if (s != null) e.fooValue = Foo.valueOf(s);
return e;
}
}
}
This obviously doesn't work if your enums have mutable state (which they shouldn't, really).
It may be possible to make your Enum implement Serializable then you can pass it via the Intent, as there is a method for passing it as a serializable. The advice to use int instead of enum is bogus. Enums are used to make your code easier to read and easier to maintain. It would a large step backwards into the dark ages to not be able to use Enums.
Most of the answers that are using Parcelable concept here are in Java code. It is easier to do it in Kotlin.
Just annotate your enum class with #Parcelize and implement Parcelable interface.
#Parcelize
enum class ViewTypes : Parcelable {
TITLE, PRICES, COLORS, SIZES
}
about Oderik's post:
You can make your enum implement Parcelable which is quite easy for enums:
public enum MyEnum implements Parcelable {
...
}
You can than use Intent.putExtra(String, Parcelable).
If you define a MyEnum variable myEnum, then do intent.putExtra("Parcelable1", myEnum), you will get a "The method putExtra(String, Parcelable) is ambiguous for the type Intent" error message.
because there is also a Intent.putExtra(String, Parcelable) method, and original 'Enum' type itself implements the Serializable interface, so compiler does not know choose which method(intent.putExtra(String, Parcelable/or Serializable)).
Suggest that remove the Parcelable interface from MyEnum, and move the core code into wrap class' Parcelable implementation, like this(Father2 is a Parcelable and contain an enum field):
public class Father2 implements Parcelable {
AnotherEnum mAnotherEnum;
int mField;
public Father2(AnotherEnum myEnum, int field) {
mAnotherEnum = myEnum;
mField = field;
}
private Father2(Parcel in) {
mField = in.readInt();
mAnotherEnum = AnotherEnum.values()[in.readInt()];
}
public static final Parcelable.Creator<Father2> CREATOR = new Parcelable.Creator<Father2>() {
public Father2 createFromParcel(Parcel in) {
return new Father2(in);
}
#Override
public Father2[] newArray(int size) {
return new Father2[size];
}
};
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mField);
dest.writeInt(mAnotherEnum.ordinal());
}
}
then we can do:
AnotherEnum anotherEnum = AnotherEnum.Z;
intent.putExtra("Serializable2", AnotherEnum.X);
intent.putExtra("Parcelable2", new Father2(AnotherEnum.X, 7));
you can use enum constructor for enum to have primitive data type..
public enum DaysOfWeek {
MONDAY(1),
TUESDAY(2),
WEDNESDAY(3),
THURSDAY(4),
FRIDAY(5),
SATURDAY(6),
SUNDAY(7);
private int value;
private DaysOfWeek(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
private static final SparseArray<DaysOfWeek> map = new SparseArray<DaysOfWeek>();
static
{
for (DaysOfWeek daysOfWeek : DaysOfWeek.values())
map.put(daysOfWeek.value, daysOfWeek);
}
public static DaysOfWeek from(int value) {
return map.get(value);
}
}
you can use to pass int as extras then pull it from enum using its value.
I like simple.
The Fred activity has two modes -- HAPPY and SAD.
Create a static IntentFactory that creates your Intent for you. Pass it the Mode you want.
The IntentFactory uses the name of the Mode class as the name of the extra.
The IntentFactory converts the Mode to a String using name()
Upon entry into onCreate use this info to convert back to a Mode.
You could use ordinal() and Mode.values() as well. I like strings because I can see them in the debugger.
public class Fred extends Activity {
public static enum Mode {
HAPPY,
SAD,
;
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.betting);
Intent intent = getIntent();
Mode mode = Mode.valueOf(getIntent().getStringExtra(Mode.class.getName()));
Toast.makeText(this, "mode="+mode.toString(), Toast.LENGTH_LONG).show();
}
public static Intent IntentFactory(Context context, Mode mode){
Intent intent = new Intent();
intent.setClass(context,Fred.class);
intent.putExtra(Mode.class.getName(),mode.name());
return intent;
}
}
I think your best bet is going to be to convert those lists into something parcelable such as a string (or map?) to get it to the Activity. Then the Activity will have to convert it back to an array.
Implementing custom parcelables is a pain in the neck IMHO so I would avoid it if possible.
Consider Following enum ::
public static enum MyEnum {
ValueA,
ValueB
}
For Passing ::
Intent mainIntent = new Intent(this,MyActivity.class);
mainIntent.putExtra("ENUM_CONST", MyEnum.ValueA);
this.startActivity(mainIntent);
To retrieve back from the intent/bundle/arguments ::
MyEnum myEnum = (MyEnum) intent.getSerializableExtra("ENUM_CONST");
If you just want to send an enum you can do something like:
First declare an enum containing some value(which can be passed through intent):
public enum MyEnum {
ENUM_ZERO(0),
ENUM_ONE(1),
ENUM_TWO(2),
ENUM_THREE(3);
private int intValue;
MyEnum(int intValue) {
this.intValue = intValue;
}
public int getIntValue() {
return intValue;
}
public static MyEnum getEnumByValue(int intValue) {
switch (intValue) {
case 0:
return ENUM_ZERO;
case 1:
return ENUM_ONE;
case 2:
return ENUM_TWO;
case 3:
return ENUM_THREE;
default:
return null;
}
}
}
Then:
intent.putExtra("EnumValue", MyEnum.ENUM_THREE.getIntValue());
And when you want to get it:
NotificationController.MyEnum myEnum = NotificationController.MyEnum.getEnumByValue(intent.getIntExtra("EnumValue",-1);
Piece of cake!
Use Kotlin Extension Functions
inline fun <reified T : Enum<T>> Intent.putExtra(enumVal: T, key: String? = T::class.qualifiedName): Intent =
putExtra(key, enumVal.ordinal)
inline fun <reified T: Enum<T>> Intent.getEnumExtra(key: String? = T::class.qualifiedName): T? =
getIntExtra(key, -1)
.takeUnless { it == -1 }
?.let { T::class.java.enumConstants[it] }
This gives you the flexibility to pass multiple of the same enum type, or default to using the class name.
// Add to gradle
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
// Import the extension functions
import path.to.my.kotlin.script.putExtra
import path.to.my.kotlin.script.getEnumExtra
// To Send
intent.putExtra(MyEnumClass.VALUE)
// To Receive
val result = intent.getEnumExtra<MyEnumClass>()
Don't use enums. Reason #78 to not use enums. :) Use integers, which can easily be remoted through Bundle and Parcelable.

Categories

Resources