How should I forward Intent parameters through chains of Activities? - android

I have lots of Activities chained together with Intents and some Intents require parameters passed in the extras Bundle. When I have to forward parameters through multiple Activities, should I copy each one explicitly or is there a best-practice way of doing it? For instance, I could clone-copy the current Intent as a starting point for calling other subtask Intents, and this would (presumably) copy all previous Bundle parameters.
As an illustration, say you have a file explorer Activity that is in one of two modes: Expert and Novice. You want to pass this state to some subtask Activity like a file properties page, which you could do by calling putExtra("skillLevel", "Expert") on the Intent before you launch it. Now if the property page also has a subtask Activity, compression options for instance, how should you forward on the "skillLevel" parameter?

I dont know why you would want to copy all the other properties using a constructor.
newIntent.putExtras(oldIntent);
Should do the trick.

I think the best and cleaner way to do it is to initialize the next Intent using the received Intent through the Intent(Intent) constructor.
Intent newIntent = new Intent(receivedIntent);

If a parameter is system wide, it may be easier to store it in Shared Preferences until it is changed (such as difficulty of a game). It would have the side effect of remembering the set difficulty when the user leaves the app.

Since we don't have Global variables in Android you can create a class with your application wide informations and use the Singleton pattern . Since it will be changed for all the system, this way you can always get the same instance of this object, hence always the same information.
An example:
public class Object {
private static Object instance;
private Object objectcall;
private Object(){
}
public void setObject(Object newObject){
this.objectcall = newObject;
}
public Object getObject(){
return this.objectcall;
}
public static synchronized Object getInstance(){
if(instance==null){
instance=new Object();
}
return instance;
}
}
when you want to retrieve it in a Activity just call
Object objectSingleton = Object.getInstance();

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
}
}

Best practice for sharing instances between Objects

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.

General Android Advice: Global Variables

I was wondering what is the best way to handle global variables for android apps. For example, I'm just trying to create a basic login/register system. I've created a user class (that has various attributes like username, password, etc..), so that when we go to the register activity, the User class constructor is called to create a unique user object once all the fields are filled out. I was then thinking of simply having a global arrayList of type User so that I could just loop through all the users on a login attempt.
So far (due to a combined lack of experience in java, and being very new to this android stuff), I haven't been able to implement this successfully. I have a class which I call "globalStuff" which has a bunch of public static variables (i.e. the list of users and current user), which I thought could be accessed from any activity the user navigates to.
There must be a better way to go about this. I've been reading through a few tutorials and a few posts on here, but none address this very basic idea. So what would be the best way to approach something like this?
Thanks for any help!
It's called a static singleton and it looks like this:
public class Global {
private static Global instance = null;
public static Global getInstance() {
if( instance == null )
instance = new Global();
return instance;
}
// additional methods, members, etc...
}
Then you just refer to it in your code as:
Global.getInstance().getUserList();
etc.

What is the best way to store a non serializable object instance in android?

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);

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