How to use data in different activities? - android

I have two activities A & B. In A i have three ArrayLists. I want to access these ArrayLists in activity B . How can I do that? These two activities are in same package.

To answer your question - which you could have easily found the answer to by searching stackoverflow - if you need to pass an ArrayList you can do like this:
ArrayList<String> list = new ArrayList<String>();
list.add("hello");
Bundle b = new Bundle();
b.putSerializable("myList", list);
Intent myIntent = new Intent(this, ActivityB.class);
myIntent.putExtras(b);
startActivity(myIntent);
And in ActivityB:
Intent myIntent = this.getIntent();
ArrayList<String> list = (ArrayList<String>) myIntent.getSerializableExtra("myList");

You probably can't access them directly. Usually only 1 Activity can be in the foreground. Trying to access elements in a background Activity (so like you lists in A from the acitve B) is a bad design choice.
I think you need to store the data from those lists in some shared location:
make them parcelable and store them in you app's preferences
store them in a SQLite db
pass them to Activity B via the starting Intent
Also, maybe you don't need to pass the whole lists to B, maybe you need only a part of them, consider that too.

* make them parcelable and store them in you app's preferences
* store them in a SQLite db
* pass them to Activity B via the starting Intent
It's wrong. Wrong I mean in a sense that no need to store Parcelable object in persistent storage, since as soon as you make object Parcelable - another Activity can get access to object without serialization (moreover serialization is not recommended). Android docs read:
Container for a message (data and object references) that can be sent through an IBinder. A Parcel can contain both flattened data that will be unflattened on the other side of the IPC (using the various methods here for writing specific types, or the general Parcelable interface), and references to live IBinder objects that will result in the other side receiving a proxy IBinder connected with the original IBinder in the Parcel.
Parcel is not a general-purpose serialization mechanism. This class (and the corresponding Parcelable API for placing arbitrary objects into a Parcel) is designed as a high-performance IPC transport. As such, it is not appropriate to place any Parcel data in to persistent storage: changes in the underlying implementation of any of the data in the Parcel can render older data unreadable.

Related

What are the problems in using Intent to pass objects from one activity to another

UPDATE: Please see "accepted" solution below
What I have observed is one way is Intent.
There is no other proper recommended way.
For me serialization is required when you want to transfer data over network/ or when we need to retrieve objects after a while. Only used in some specific scenarios. But here what I saw is to use intent together with serialization to simply share/pass some data.
According to spec, intent will act as the glue between activities.
I would also assume that we can pass instructions /small amount of data to next activity.
My question more specifically is about passing data/big data using intents.
Considering that serialization is required when using intents. Is this a good way?
Note: Please consider that ,won't be able to use Parcelable in this specific scenario, since developing a framework independent of android.
Intents should only pass small packets of data. If you need to pass something big, save it to storage or a database, pass an uri through the Intent and then read the data in the receiving Activity.
Passing big data in the intent will cause drastic problems, up to the point of killing your app process (which is very annoying to the user).
There is a process-scope limit of 1MB of data being passed between components. Please keep in mind that this does not mean that you can pass 1MB of data safely, as there may be multiple Intents being processed at a time.
You could also consider using an event bus library, like greenrobot EventBus, but these require a big amount of discipline, as they basically let you pass everything everywhere.
Intents' extras are indeed the common way. It depends on your data type - primitive types do not require any special work on your size, and custom models should be bundled in a Parcelable object (can't think of why it can't be an option). If you're caching some large data (for example, large pictures), you should consider storing them temporarily on the SD card (as files or in a local SQLite), but this is still your way to go. Try to avoid extra network use and don't cache this data on a remote server.
Another method, especially good for communicating with other types of contexts (services, broadcast receivers) is EventBus.
An approach that I have used is to use a Singleton Holder Class for the Data Object. And access it between components of your process. pseudo code is here below. May have compilation errors. Also mind that you would need a purging mechanism and add a way to keep the data fresh.
class DataSet{
DataSet(String data){
this.data = data;
}
public String data;
}
class Holder{
private Holder(){
}
private static Holder holder = null;
DataSet object = null;
private Holder static getInstance(){
if(null == holder){
holder = new Holder();
}
return holder
}
public void setData(DataSet arg){
object = arg;
}
public DataSet getData(){
return object;
}
}
class Activity A implements View.OnClickListener{
public void onClick(){
Holder.getInstance().setData(new DataSet("this is a big object"));
// At this point the data has been set and has process scope.
startActivity(new Intent(A.this, B.class))
}
}
class Activity B{
DataSet data;
public void onCreate(){
data = Holder.getInstance().getData(); // This point the data is accessible to Class B
}
}

LinkedList put into Intent extra gets recast to ArrayList when retrieving in next activity

A behaviour i'm observing w.r.t passing serializable data as intent extra is quite strange, and I just wanted to clarify whether there's something I'm not missing out on.
So the thing I was trying to do is that in ActivtyA I put a LinkedList instance into the intent I created for starting the next activity - ActivityB.
LinkedList<Item> items = (some operation);
Intent intent = new Intent(this, ActivityB.class);
intent.putExtra(AppConstants.KEY_ITEMS, items);
In the onCreate of ActivityB, I tried to retrieve the LinkedList extra as follows -
LinkedList<Item> items = (LinkedList<Item>) getIntent()
.getSerializableExtra(AppConstants.KEY_ITEMS);
On running this, I repeatedly got a ClassCastException in ActivityB, at the line above. Basically, the exception said that I was receiving an ArrayList. Once I changed the code above to receive an ArrayList instead, everything worked just fine.
Now I can't just figure out from the existing documentation whether this is the expected behaviour on Android when passing serializable List implementations. Or perhaps, there's something fundamentally wrong w/ what I'm doing.
Thanks.
I can tell you why this is happening, but you aren't going to like it ;-)
First a bit of background information:
Extras in an Intent are basically an Android Bundle which is basically a HashMap of key/value pairs. So when you do something like
intent.putExtra(AppConstants.KEY_ITEMS, items);
Android creates a new Bundle for the extras and adds a map entry to the Bundle where the key is AppConstants.KEY_ITEMS and the value is items (which is your LinkedList object).
This is all fine and good, and if you were to look at the extras bundle after your code executes you will find that it contains a LinkedList. Now comes the interesting part...
When you call startActivity() with the extras-containing Intent, Android needs to convert the extras from a map of key/value pairs into a byte stream. Basically it needs to serialize the Bundle. It needs to do that because it may start the activity in another process and in order to do that it needs to serialize/deserialize the objects in the Bundle so that it can recreate them in the new process. It also needs to do this because Android saves the contents of the Intent in some system tables so that it can regenerate the Intent if it needs to later.
In order to serialize the Bundle into a byte stream, it goes through the map in the bundle and gets each key/value pair. Then it takes each "value" (which is some kind of object) and tries to determine what kind of object it is so that it can serialize it in the most efficient way. To do this, it checks the object type against a list of known object types. The list of "known object types" contains things like Integer, Long, String, Map, Bundle and unfortunately also List. So if the object is a List (of which there are many different kinds, including LinkedList) it serializes it and marks it as an object of type List.
When the Bundle is deserialized, ie: when you do this:
LinkedList<Item> items = (LinkedList<Item>)
getIntent().getSerializableExtra(AppConstants.KEY_ITEMS);
it produces an ArrayList for all objects in the Bundle of type List.
There isn't really anything you can do to change this behaviour of Android. At least now you know why it does this.
Just so that you know: I actually wrote a small test program to verify this behaviour and I have looked at the source code for Parcel.writeValue(Object v) which is the method that gets called from Bundle when it converts the map into a byte stream.
Important Note: Since List is an interface this means that any class that implements List that you put into a Bundle will come out as an ArrayList.
It is also interesting that Map is also in the list of "known object types" which means that no matter what kind of Map object you put into a Bundle (for example TreeMap, SortedMap, or any class that implements the Map interface), you will always get a HashMap out of it.
The answer by #David Wasser is right on in terms of diagnosing the problem. This post is to share how I handled it.
The problem with any List object coming out as an ArrayList isn't horrible, because you can always do something like
LinkedList<String> items = new LinkedList<>(
(List<String>) intent.getSerializableExtra(KEY));
which will add all the elements of the deserialized list to a new LinkedList.
The problem is much worse when it comes to Map, because you may have tried to serialize a LinkedHashMap and have now lost the element ordering.
Fortunately, there's a (relatively) painless way around this: define your own serializable wrapper class. You can do it for specific types or do it generically:
public class Wrapper <T extends Serializable> implements Serializable {
private T wrapped;
public Wrapper(T wrapped) {
this.wrapped = wrapped;
}
public T get() {
return wrapped;
}
}
Then you can use this to hide your List, Map, or other data type from Android's type checking:
intent.putExtra(KEY, new Wrapper<>(items));
and later:
items = ((Wrapper<LinkedList<String>>) intent.getSerializableExtra(KEY)).get();
If you are using IcePick library and are having this problem you can use Ted Hoop's technique with a custom bundler to avoid having to deal with Wrapper instances in your code.
public class LinkedHashmapBundler implements Bundler<LinkedHashMap> {
#Override
public void put(String s, LinkedHashMap val, Bundle bundle) {
bundle.putSerializable(s, new Wrapper<>(val));
}
#SuppressWarnings("unchecked")
#Override
public LinkedHashMap get(String s, Bundle bundle) {
return ((Wrapper<LinkedHashMap>) bundle.getSerializable(s)).get();
}
}
// Use it like this
#State(LinkedHashmapBundler.class) LinkedHasMap map

How to store data between activities in android?, shopping cart like

I want to make some data available between some activities, just like a shopping cart on a website would do.
This data would probably be a collection of strings maybe a list, a map or something like that. Each item should have associated a id, quantity, type, and a text note (about last one isn't sure yet)
The point is that it doesn't need to be persistent after session ends, and this data will be deleted and recreated completely many times in a whole session.
The question is :
Is the best choice to use a SharedPreferences?, a database?
Thanks!
Even better choice would be some singleton java collection ( map or list ) located via factory object. Just store your cart there and do not bother with database or preferences at all
In case you decide to use preferences, I can recomment my small databinding library:
https://github.com/ko5tik/andject
the simplest way is to use a class with public static variables declared and jus set them from any activity and the retrieve that saved value in any other activity just by calling through a static refrence i.e MyContantsClass.StaticVar1 like
class MySessionVars
{
public static int MyVar1;
}
In first Activity
{
MySessionVars.NyVar1=10;
}
and from any other activity
{
Var = MySessionVars.NyVar1;
}
this is easiest way and will retain vars untill app is closed
You can use Gson (by Google) to send and receive any data btw activities, even class object.
Send: (FirstActivity)
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
Gson gson = new Gson();
intent.putExtra("CustomClassObject", gson.toJson(object));
startActivity(intent);
Receive: (SecondActivity)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Bundle extras = getIntent().getExtras();
if (extras != null)
{
Gson gson = new Gson();
CustomClass object = gson.fromJson(extras.getString("CustomClassObject"), CustomClass.class);
}
//...
}
SharedPreferences is great for storing simple key/value pairs and small amounts of data, however, I imagine your shopping cart object(s) might be a bit more complicated than that. For that reason I would probably use a SQLite database. If you don't have complicated data though, and you just want to store some simple stuff then Shared Preferences should do.
Use Bundle or Intent.
I wouldn't recommend using database for this purpose. Database needs to be properly opened and closed. Where data is passed around activities, it can get messy which activity should be the last to close the database, and may cause a leak. If you are just passing these data from one Activity to another, create a Bundle, attach it to Intent as you start the next activity.
SharedPreferences are key-value pairs whereas database in Android is SQLite both persists after session. You can do any number of operations on SQLite database and use the same file during next session also.
You can put up comments to my answers if you need more clarifications for your particular use case.
People may talk about making global data or storing values in sharedpreference but according to my experience, the simplest way is to just create a separate class with your variables or arraylists declared as public static then use them throughout your project by just referring to the classname.variable name.
class global
{
public static arrayList<String> my;
}
in any of your activity
global.my
This is the simplest method i found so far!

how to communicate between Activity(s) using non-primitive object

Is it possible for Activity(s) to communicate using user defined object?
p.s.
So far as I know, when I want Activity(s) to communicate to each other, I have to use primitive type of objects, such as int, String, boolean,...etc.
We don't use Serializable, Parcelable and static class.
If talkin about extras when caling intents, you can implement Serializable or Parcelable interface in your objects to pass them through.
You can also put that object into own implementation of Application class and access it in Activity or Service class as described in my other answer. But please keep in mind, that sharing state in that manner may be a sign of more general problem in your design.
You have a few options:
1.You could wrap the more complex structure in a class that implements the Parcelable interface, which can be stored in an extra.
2.You could wrap the more complex structure in a class that implements the Serializable interface, which can be stored in an extra
3.You use static data members to pass stuff around, since they are all in the same process
4.You use external storage (file, database, SharedPreferences)
5.As the person who just posted noted, use a common component, such as a custom Application or a local Service
What you do not want to do is pass big stuff via extras. For example, if you are creating an application that grabs pictures off the camera, you do not want to pass those in extras -- use a static data member (icky as that sounds). Intents are designed to work cross-process, which means there is some amount of data copying that goes on, which you want to avoid when it is not necessary for big stuff.
Answer copy from here
Intent myintent = new Intent(Info.this, GraphDiag.class).putExtra("<StringName>", value);
startActivity(myintent);
use the above code in parent activity
and in child activity
int s= getIntent().getIntExtra("<StringName>");
in the same u retrive the float,char,String values

How do I pass an object from one activity to another on Android? [duplicate]

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.

Categories

Resources