Passing arbitrary object in Android Intent - java

I wish to pass an arbitary object via an Android intent, I have the following code:
In the activity I wish to send an intent from:
Intent intent = new Intent(containerListActivity, com.uk.jacob.containerdroid.activities.ContainerDetailsActivity.class);
intent.putExtra("container", containers.get(i));
containerListActivity.startActivity(intent);
The activity I wish to recieve an intent:
System.out.println(getIntent().getExtras("container"));
I seem to get the error "cannot resolve method putExtra.
containers is essentially a Jackson JSON mapped POJO.

Unfortunately, you can't put an arbitrary object in the Intent. It needs to be Parcelable or Serializable.
http://developer.android.com/reference/android/content/Intent.html
You can do it a few ways:
Have your custom class implement Serializable. This is quick to code, but may not be performant.
Have your custom class implement Parcelable. This could be a bit of work.
Convert your container to a string using Jackson, then convert it back after using getStringExtra.

Serialize your JSON to a string and then push it to the intent. In the receiving activity try getStringExtra() to extract it from the intent object.

Related

Sending serializable object between applications with broadcast receiver

Im trying to send an Object that implements "Serializable" from one application to another via a Broadcast receiver.
I want to send a class like:
public class MyObject implements Serializable{
//basic properties
}
In application A i do this:
Intent i = new Intent("myApplication.string");
Bundle b = new Bundle();
b.putSerializable("myclass",obj);
i.putExtras(b);
sendBroadcast(i);
When i debug this I can verify that the object is properly stored in the bundle in the intent.
In application B i do this:
Manifest
<receiver
android:name="com.example.myapplication.myreceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="myApplication.string"/>
</intent-filter>
</receiver>
Broadcast Receiver
#Override
public void onReceive(Context context, Intent intent) {
try{
Bundle b = intent.getExtras();
MyObject s = (MyObject)b.getSerializable("myclass");
}catch (Exception e){
Log.d(TAG,e.getMessage());
}
}
In application B the intent does not hold the data that it did in application A.
When i try to cast the data it throws an:
Parcelable encountered ClassNotFoundException reading a Serializable object (name = com.example.myApplication.MyObject)
I've copied the class implementation in both application so they match.
In application A all the data is with in the mMap property of the Intent.extras - but in app B it is empty.
Can anyone help me understand this?
Thanks.
Im trying to send an Object that implements "Serializable" from one application to another via a Broadcast receiver.
That is not a good idea. It requires both apps to have the same class definition for that Java class, or at least one that is compatible. Since the apps may not be updated at the same time, you can run into cases where the sender has a newer edition of the Java class than does the recipient, which can cause a deserialization exception.
In application A i do this:
That is not a good idea. You are not sending the data to the other app. You are sending the data to any app that cares to listen for this broadcast, including any that may want to spy on your output.
In application B i do this:
That is not a good idea. Any app can send a broadcast to this app, either to spoof messages or attempt to cause the app to crash. And on Android 8.0+, you will not receive the broadcast in any case.
I've copied the class implementation in both application so they match.
Perhaps there is a problem in how you did this, as the error message would seem to disagree with your assessment.
I would start by getting rid of the Serializable. Only put things into extras that are guaranteed to be recognized correctly by all parties accessing those extras. So, use simple primitives, or Bundle, or other framework classes, not custom Serializable or Parcelable implementations. Then, see if your more ordinary extras are making it from app to app.
Then, do a better job of inter-process communication:
Use an explicit Intent (one with a ComponentName), not an implicit Intent (one with an action string), so that the "broadcast" only goes between the two parties and works around the Android 8.0+ implicit broadcast restrictions
Either implement permissions (e.g., android:permission on the <receiver>) or perform signature checks to ensure that the two parties are who you think they are
MyObject should implement Parcelable, not Serializable...

Using callbacks to pass back data in a DialogFragment (android)

I have a DialogFragment that allows users to filter and search in the same Fragment. The filter works by passing back data to the hosting activity/fragment using a callback interface. This seemed to work great until I added a SearchView to the DialogFragment as when I enter text and click search it works but then throws the following expception and crashes:
Parcelable encountered IOException writing serializable object (name = com.braden.android.fragments.ListItemFragment$6)
...
Caused by: java.io.NotSerializableException: com.braden.android.fragments.ListItemFragment
To do the callback I used a fairly standard callback interface pattern. The interface extends Serializable. Here's the code for my callback:
private void displayFilter() {
FilterCategoryDialogFragment filterCategoryDialogFragment = new FilterCategoryDialogFragment();
Bundle bundle = new Bundle();
mOnFilterClickListener = new OnFilterClickListener() {
#Override
public void onCategoryClickListener(String filterName) {
updateVenues(mFilter);
}
};
bundle.putSerializable("listenerFilter",
mOnFilterClickListener);
filterCategoryDialogFragment.setArguments(bundle);
filterCategoryDialogFragment.show(getFragmentManager(), DIALOG_CATEGORY_FILTER);
}
This seems to have something to do with using an anonymous inner class that implements serializable so I'm wondering:
1) Why is it that I'm only receiving this exception when I use SearchView and not when I perform an action to send back data via callback or simply click out of the dialog.
2) Is there a workaround here or is this just a bad pattern for me to use.
I found the answer to this question here: Callback to a Fragment from a DialogFragment
They key is the "setTargetFragment" method which allows you to tell a fragment which fragment to send its result to. This allows you to avoid having to serialize an interface reference for the callback.
All fields of class must be Serialized, otherwise you should get NotSerializableException.
if you check Exception stack you will be able to find that object which not serialized.

How to use a Serializable class

I'm using Backendless as a backend service, it offers a class called BackendlessUser for saving and retrieving users. I'm trying to pass a User between two activities on Android by passing it as Serializable extra:
Intent intent = new Intent(PeopleActivity.this, ConversationActivity.class);
intent.putExtra("withUser", contacts.get(position));
startActivity(intent);
Since the class BackendlessUser implements Serializable. However when I run it, it gives me this error:
java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = com.backendless.BackendlessUser)
at android.os.Parcel.writeSerializable(Parcel.java:1468)
at android.os.Parcel.writeValue(Parcel.java:1416)
....
Caused by: java.io.NotSerializableException: java.lang.Object
....
Due to, I think, this variable:
public final class BackendlessUser implements Serializable {
private final Map<String, Object> properties = new HashMap<String, Object>();
...
How can I solve this considering that I cannot modify the BackendlessUser class?
In Android you should use Parcelable which offers better performances compared to Serializable. For an explanation about how to implement it take a look at this answer
Also, if you need to use Parcelable on your map object, see this answer
Edit: since Object is not Parcelable though you might want to follow Alexander's answer, or, even better, use a database for data persistence
Instead of passing the object, you can save the reference to it in a singleton so that it's available between the activities.
You can extend the Application class and save there. The Application class exists all the time while your app is running and is a singleton.
public class MyApp extends Application {
public BackendUser currentUser;
}
Then:
((MyApp)getApplication()).currentUser
You should be using Parcelable to pass objects between activities. Parcelable is Android's version of Serializable, so you use that. You can find more information on Parcelable here.
If you can't modify the backend user, your best bet would be to use Alexanders suggestion and create a Singleton instance of User. This would allow you to create/update you user from any activity.

Android - JSON stored list contains objects that revert to superclass when app is exited, reopened

I have an ArrayList <GeneralTemplate> items
Throughout my program, I am adding Routines which are subclasses of GeneralTemplate i.e items.add(new Routine("Test")); and all is well.
Most importantly, I can do the following.. Routine myRoutine = items.get(position);
I am saving this big list of items in a special data object in JSON using Google's GSON library. I believe this may be the problem.
This data object contains the ArrayList <GeneralTemplate> items. During my program, I can see that the routines stored in the items list are indeed Routine objects. I then save it using the code below. I have followed this process with the debugger and when I setRoutineList, the Routine objects are maintained without problem.
// Global save String method
public static void save()
{
Editor editor = sharedPreferences.edit();
RoutineData tempSaveObject = new RoutineData();
tempSaveObject.setRoutineList(routineList);
String routineListInJSON = gson.toJson(tempSaveObject);
editor.putString(ROUTINE_LIST, routineListInJSON).commit();
}
The problem occurs when I restart the app and retreive the data. All of the items in the list revert to GeneralTemplate objects and cannot be cast back to Routine via Routine routine = (Routine) items.get(position) -> ClassCastException (Code for loading below)
// Get a global sharedPreferences object that can be used amongst Activities
sharedPreferences = this.getSharedPreferences(SHARED_PREFS, Context.MODE_PRIVATE);
if (sharedPreferences.contains(ROUTINE_LIST))
{
String routineListJSON = sharedPreferences.getString(ROUTINE_LIST, null);
routineDataObject = gson.fromJson(routineListJSON, RoutineData.class);
routineList = routineDataObject.getRoutineList();
}
else
{
routineList = new ArrayList<GeneralTemplate>();
}
Therefore, I can't access specific methods and variables because I cant regain the subclass context. There are several other instances of this problem, so if there is any good solution to this that you good folks knowledge, it would help a lot.
Thanks!
SORTED:
Genson JSON library.
https://code.google.com/p/genson/downloads/detail?name=genson-0.94.jar&can=2&q=
Made things so much easier, no need for custom serializers/deserializers. Took care of all the in depth polymorphism stuff by default.
Implemented as shown in Eugen's answer
This is due to the fact that you have a list of GeneralTemplate, while serializing Gson knows the concrete type of each element in the list but during deserialization Gson doesn't know into which type to deserialize (as it is a list of GeneralTemplate).
I am not sure but it looks like they have some contrib (not part of Gson) that allows to add type information in the serialized stream, this allows Gson to deserialize back into the right type.
You could also try out Genson library, handling polymorphic types is supported out of the box. It has the features provided by Gson and some others too. Here is how you can achieve it:
// first configure your Genson instance to enable polymorphic types support and
// serialization based on concrete types
Genson genson = new Genson.Builder()
.setWithClassMetadata(true)
.setUseRuntimeTypeForSerialization(true)
.create();
// and now just use it to serialize/deser
String json = genson.serialize(routineData);
RoutineData data = genson.deserialize(json, RoutineData.class);
EDIT
Problem solved. The Routine class had no default constructor. Putting #JsonProperty("name") on the ctr parameter and using gensons previous configuration solved the problem.

Java Class classes

I have an array of Classes, like this:
private static final Class[] testerClasses={
Tester1.class
};
I need it to be like this, so I can start Android intents, like this:
intent =new Intent(this,testerClasses[0]);
startActivity(intent);
I'm trying to figure out how to do the following, and I'm really struggling to figure out how to do it...
I need to call functions from an instance of the class. For the most part, these are static or static like functions, so merely getting an instance of the function is sufficient.
I need to make sure I can pass the class as the Intent structure requires.
Why do I want to do this? Sometimes I want to call the function like an intent, other times I just want to get data about the intent, without having to call it. My abstractTester class (Of which all concrete tester classes inherit from) contains a few things like the name of the test, which is handy to have before the Intent has even started.
Thanks for the help!
You will not be able to pass the class object with the Intent . Instead, pass the class name in the intent; the receiver can get the class via loadClass()
Once the receiver has the class object, it can use reflection to call the intended method.
The correct solution is:
private static final abstractTester[] testerClasses={
new Tester1()
};
intent =new Intent(this,testerClasses[0].getClass());
startActivity(intent);
Then if I need to reference the class, I can just do it. Works like a charm!
Hi you can try to use code below ,
ArrayList<Class<?>> a = new ArrayList<Class<?>>();
a.add(MainActivity2.class);
a.add(MainActivity.class);
Intent intent =new Intent(this,a.get(0));
startActivity(intent);

Categories