I need to pass a list of objects between activities, I am using a public static global variable in my Application class.
The problem is that this variable seems to be the first to be destroyed when the activity goes in the background for a while.
I never have problems with the activity going in the background but whenever I use a global variable like this, it always the first gets garbage collected or something to fee memory. This causes my application to crash.
How can I prevent this happening?
This is a bad approach of doing it storing it in a global variable.
You should either serialize your class or create a Singleton pattern and store that object in it.
Serializable Approach
public class ClassIntanceOne implements Serializable{
}
//In Activity
ClassIntanceOne class_instance_one = new ClassIntanceOne();
Bundle bundle = new Bundle();
bundle.putSerializable("object1", class_instance_one);
intent.putExtras(bundle);
startActivity(/*Your class*/);
The easiest solution is to include the objects in the intent, that is starting the other activity. This requires, that your objects implement Parcelable or Serializable.
Then just call:
Intent intent = getIntent();
intent.putExtra("myobjects", listOfObjects);
getContext().startActivity(intent);
It is bad practice in android to make use of globel static variables. The GC will always remove them when your activity goes to the background as the value is not used anymore. Also don't try to keep your object longer in memory as you need it. This may cause poor performance on devices with less memory.
Like SME_Dev said, you will need to serialzie your objects and pass them as an intent extra to the activitys.
When you can searialize your objects it's also easier to restore the current state of the app if it get's destroyed because you can make use of the recreating mechanism in android.
Related
What is the purpose of using an intent with a message instead of just declaring a static variable in java and calling it from the new activity? It seems easier to me this way because you can have the static variable be anything you want (i.e. ArrayList, Object, etc.).
public class FirstActivity extends Activity {
public static String name;
...
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
name = "Robert";
startActivity(intent);
}
public class SecondActivity extends Activity {
...
textView.setText(FirstActivity.name);
}
By using extras to start SecondActivity, you make it more reusable. Many of the stock activities work this way, that's why you can reuse for example the camera activity to take and save photos, because it does not make assumptions about who is calling it.
In your case SecondActivity depends on FirstActivity having been loaded in the JVM. I would not count on that, and it's certainly not a recommended practice to have such dependency between activities. Don't do this. Use extras to pass values between activities, as recommended by the SDK.
To clarify, the OP's strategy won't work if another app outside of his/hers wants to handle the Intent. Because of this, it's not a "best practice".
There are roughly 30 different putExtra variations for an Intent, each representing a different data type you can add. They include general purpose data types such as Bundle, Parcelable, Serializable, and so forth. I can't think offhand of anything that these don't cover.
I don't use statics or variables defined by overriding Application or other similar ways of assuming that some data is floating about in storage. It's much more robust to assume that my Activity or Fragment is totally independent.
Using Intent make you slower than static
For example if you use mvp or mvvm
At least you have to pass like id through layer by later
Suppose I have an activity MyMainActivity, let's say complex enough with a bunch of code.
From another activity, to access a public variable or a method I instanciate :
MyMainActivity ma = new MyMainActivity();
ma.editVariableMethod();
String example_variable = ma.public_examplevariable;
When I instanciate MyMainActivity ma, is it like creating the hole activity again and storing everything from MyMainActivity to memory, and that way it takes the same amount of memory it would take if I was starting MyMainActivity, or is it just a link which permits to edit variables from MyMainActivity?
You can't instantiate an Activity. The framework has to take care of it. If you want to use public methods either make them static OR get a reference to a valid instance of the activity object.
Edit:
As Squonk pointed out, depending on your use case, it might be a better idea to just extract the shared logic to another new class, at least until you know what you're doing. Giving "full access" to internal variables or even methods in an Activity might seem to work, but it's very likely NOT the right approach.
It is a bad practice to share memory-resident objects between objects in Android, no matter what the objects are. Android won't ensure that it will work. There are alternatives available for most use cases. In the particular case of "accessing a public variable" in another Activity, you can call startActivityForResult(), or ensure that your Activities store data they want to "share" in SharedPreferences, etc.
If you have two or more Activities that use the same method, you should first consider if the class needs to be abstracted into a separate object. Ideally, Activities should be frameworks that delegate to POJOs.
I'm taking an android class now, so I am somewhat new to android app development.
My first assumption for a Base Activity is that it's Global Variables and it's values would be available to all activities. I have found that it is available to my Main Activity, but not any activities after that.
In the Base Activity I am storing an ArrayList of Objects. I also load data from an xml in there that adds objects to the arrayList. Once in the Main Activity I still have access to that arrayList and it's values. I use it to fill a list. But when I go to the next activity, it knows about the arrayList but thinks it is empty.
Do I need to create methods in the base activity to retrieve the arrayList and to add objects to the array list?
Any help would be appreciated.
Thank you,
Michelle
Global variables need to be declared static. Then they would be accessible from any class. Example:
public class Globals {
public static String myString;
}
Any class can read/write the myString like this:
Globals.myString = "foo";
or
String bar = Globals.myString;
From experience I believe the variables of one activity is only avaliable to the other while the activity is active, which means between onCreate and onDestroy, other then that you will probably get a null pointer exception, what you really should be doing is sending the data, or arrays, along with the intent to the other activity.
I dont think you should be calling on other activities variables, although it is possible as stated above. I believe when the activity has had it's onDestroy method called the objects in the activity are destroyed to and are removed from memory. Destroying anything that they held.
What is this base activity? Does it just extend activity? And then MainActivity is extending Activity as well? Only one activity is usable at any one time, if your doing what I think your doing you should have a service which can provide you with everything over the cycle of the application, just remember to stop it when your done with it.
This is a pretty simple question, but I have been unable to find anyway to accomplish what I am trying to do...
I want to launch a new Activity to display some complex information. Because of the complexity, it is undesirable to serialize the information into the intent's parameters. Is it possible for the the new Activity to get a reference to the launching activity, so it can call its methods?
If you use a custom application class, you can store information that will be kept between the activities.
See a tutorial here for instance.
The lifetime of an Activity cannot be depended upon. In this case, one way of sharing data is to have a singleton which holds the data to be shared between the two activities.
You can add a public static field to the first activity containing this (the first activity).
But beware that the first activity could be destroyed by Android while you are using the second activity, so you will have to implement a fallback method if the first activity is destroyed.
And don’t forget to unset the public static variable in the onDestroy() callback of the first activity or you will leak memory.
Is it possible for the the new Activity to get a reference to the launching activity, so it can call its methods?
Please do not do that. Android can and will destroy activities to free up memory.
Complex information like you describe should not be owned by an activity. It should be held in a central data model, like you would in any other application. Whether that central data model is mediated by a Service or a singleton or a custom Application object depends a bit on the type of data, caching models, risks of memory leaks, and so on.
You can make your complex objects public and static in ActivityA, and access them in ActivityB like this:
MyCustomObjectType complexFromA = ActivityA.complexObject;
this will work, however while in ActivityB, you can't always be sure that static objects from ActivityA will exist(they may be null) since Android may terminate your application.
so then maybe add some null checking:
if(null == ActivityA.complexObject) {
//go back to ActivityA, or do something else since the object isn't there
}
else {
//business as usual, access the object
MyCustomObjectType complexFromA = ActivityA.complexObject;
}
You could also use a Singleton object which extends Application. You would have the same problem when Android terminates your application. always need to check if the object actually exists. Using the Singleton extending Application approach seems to be the more organized way - but adds more complexity to implementation. just depends what you need to do and whatever works for your implementation.
You should create a separate class that both the activities can use.
public class HelperClass{
public void sharedFunction(){
//implement function here
}
}
I would recommend staying away from static variable in android. It can cause some unexpected behavior.
Use getParent() from new activity and call parent's method
Android Activity call another Activity method
I was looking at the way Android handles orientation change for my application (I discovered that it restarts the mainactivity on orientation change. I've seen that you can override the method
protected void onSaveInstanceState(Bundle outState)
To save stuff, then have the in onStart. The problem is that I've my view with custom objects and a listview using a custom adapter. Everything is in a ArrayList of these objects, but I've noticed that you can't put arbitrary objects in the bundle! So how do I save the state?
Have you tried using: its work around ,
<activity name= ".YourActivity" android:configChanges="orientation|screenSize"/>
in Manifest file?
It does not work by default because , when you change the orientation onCreate will be called again and it redraws your view.
If you write this parameter no need to handle in Activity , the framework will take care of rest of things.
It will retain the state of the screen or layout if orientation is changed.
NOTE If you are using a different layout for landscape mode , by adding these parameters the layout for landscape mode will not be called.
Other way and Another way
EDIT: On newer versions of Android and with the compatibility library, retained fragments are usually the best way to handle keeping expensive-to-recreate data alive across activity destruction/creation. And as Dianne pointed out, retaining nonconfiguration data was for optimizing things like thumbnail generation that are nice to save for performance reasons but not critical to your activity functioning if they need to be redone - it's not a substitute for properly saving and restoring activity state.
But back when I first answered this in 2010:
If you want to retain your own (non view-state) data, you can actually pass an arbitrary object specifically for orientation changes using onRetainNonConfigurationInstance(). See this Android Developers blog post. Just be careful not to put any Views or other references to the pre-rotation Context/Activity in the object you pass, or you'll prevent those objects from being garbage collected and may eventually run out of memory (this is called a context leak).
First, you need to determine what is actually the "state" in your app. You haven't said what you are actually doing, but let me assume that the ArrayList of objects is the state the user is working with.
Second, decide what the lifecycle of this state actually is. Is it really tied to that activity? Or should the user not lose it if say their battery runs low, the device turns off, and they later return to your app? If the former, onSaveInstanceState() is correct; if the latter, you'll want to save to persistent storage in onPause().
For onSaveInstanceState() with custom objects, the key is to implement the Parcelable interface. This involves implementing the methods on Parcelable, as well as making a static CREATOR object in your class. Here's the code for a typical simple Parcelable class:
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/content/ComponentName.java
The key functions are the Parcelable implementation:
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/content/ComponentName.java#317
and the CREATOR static class:
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/content/ComponentName.java#355
(The static writeToParcel() and readFromParcel() are just conveniences that were done for that class and not required.)
Now that you have that, you can put your entire ArrayList of objects into the saved state Bundle with Bundle.putParcelableArrayList:
http://developer.android.com/reference/android/os/Bundle.html#putParcelableArrayList(java.lang.String, java.util.ArrayList)
In Activity.onCreate(), check to see if you have a savedState Bundle, and if so try to retrieve the ArrayList from that and use it if found, creating a new adapter and list view for the new activity that are used to display it.
but I've noticed that you can't put arbitrary objects in the bundle!
First make your custom objects Parcelable.
Then you can put them in to a bundle.
Everything is in a ArrayList of these objects
You can use putParcelableArrayList method in bundle to store an ArrayList of custom "parcelable" objects.
write your objects to JSON Strings using Google's Gson, then save them as a String. Then, build them back from the saved JSON Strings when rebuilding the Activity/Fragment
import com.google.gson.Gson;
public class MyClass {
private int a;
private String s;
private OtherSerializableClass other;
private List<String> list;
public String toJson() {
return new Gson().toJson(this);
}
public static ChatAPI_MessagesArray fromJson(String json){
return new Gson().fromJson(json, YourClass.class);
}
// Getters
...
// Setters
...
}