I have an application which has some static variables.
These variables are stored in an independent Class named DataContext.
These variables are initialized from raw files at the application start (a method named DataContext.initConstant() is called in the onCreate() of MyApplication which extends Application).
(EDIT : the initConstant method use an AsyncTask to load this data from files).
When my application comes to the background for a certain time or when my application used to much memory, these static variables become null.
How can it be prevented?
If not what should I do with my static variables?
I have other data which are stored in static variables to be used across different activities, but I clear them or pass them to null in the onLowMemory() of MyApplication.
What is the best way to keep some data accessible between activities if these data are too big to be serialized in an Intent, a database can't be used (for whatever reason), and can't be stored in files through serialization either?
You can't. Android needs to free up memory from time to time. Imagine if all applications had a ton of static data that is supposed to be resident forever - how would you fit that in memory? It's a mobile phone. It doesn't have virtual memory.
(and 3): Anything that is intended to be persistent needs to be stored, either via SharedPreferences, a Sqlite database, or a file.
Most likely the issue is that your application is being killed while it is in the background, and then recreated when you come back to it. Check out the Activity Lifecycle documentation on when this might occur for a single activity. You need to make sure that you move anything stored in memory to more permanent storage at the correct point in time to avoid losing that information if the app gets killed.
I'm not sure what exactly you are storing, but it sounds like using Shared Preferences might work well. This page on Data Storage explains a number of different ways of more permanently storing data, including Shared Preferences.
If you weren't using raw files, I'd advise initializing when the class is loaded.
For instance,
public static Map<?,?> myStaticMap = new HashMap<?,?>();
static { //fill myStaticMap }
You do have some bigger concerns to worry about if you are loading files that way. For instance, what about I/O errors, or latency issues? You will get warnings in gingerbread (if you enable them) for doing I/O in your main thread. Perhaps you should have an object to retrieve these values instead of a class with static fields. (perhaps with a static cache, although you should synchronize on it before checking/changing it)
I assume this is a data cache problem.
Storing data in static class is not guaranteed to work when user swap apps often. Android system will reclaim any background activity when memory is low. Static class is definitely among this category.
The proper way to do it is to use sharedPreference to persist cache data.
You can create your own getter and setter of the data you want and wrap it around sharedPreference object. When you access using getter, you should always check if the value is empty or expired. You can store an update_time when using setter.
For activity specific data, you can just use getPreference(permission), if you want to share data across activities and other applications components, you can use getSharedPreference(name, permission).
Normally, the permission will be MODE_PRIVATE such that the data can only be accessed within your application.
You should group data and store in difference sharedPreference object. This is good practice because when you want to invalidate that group of data, it is just a matter of one liner.
editor.clear(); editor.commit()
If you want to cache complex object, you should serialize it. I prefer JSON format. So you need some conversion mechanism in place. To do this, I will create my data object class extending JSONable class. JSONable class will have toJSON() method and readFromJSON(). This is convenient when restore and serialize data.
I store a User object and a Client object in my static scope. I have noticed from time to time the reference becomes null. So now in my getters I check to see if this value is null and if so I restart the app.
Intent i = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName());
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(i);
I could have also chosen to reload the Client because I store the Access Token in prefs however I do so much initialization that I decided restarting the app would the best idea.
In your onResume() method you could query the static data to see if it is present and if not, load it back in again.
Instead of using the static variable u can use the shared preference for storing the value.
Note: for shared preference also you should not give heavy load.
I have solved this problem by having the super class with getter and setter function for storing and retrieving shared preference variable.
All class in my application extended the super class instead of activity.
Related
Suppose we have a list of complex objects (primitives and other objects inside) requested from a server to show them inside a RecycleView. In adapter we need only some data from each object, let's say 70%.
I get from server list with full data objects, and each object implements Parcelable so when I select a item I pass object via intent to MyDetailsActivity like so:
Intent intent = new Intent(context, MyDetailsActivity.class);
intent.putExtra("foo", myComplexObject);
startActivity(intent);
This is working as expected but on some low memory devices I get out of memory errors. So my question is how to achieve this type of data flow?
One possible solution is to implement get/set for MyObj in Applicattion class, and pass it like so but I'n not sure if it's stable.
And of course I can pass just an id from MyObject and do another request call inside DetailsActivity's onCreate(), but this requires user to wait some more seconds to load data.
Any advices or references are apreciated
As you have experienced, sending data through the bundle/intent extras has its memory limits.
Your options are to either keep those objects in memory and access them via some static/singleton instance or to do the data query from your server in the desired activity that will show them in your list.
Another viable options is to store the data in a database for ex and read it once its required but this ofcourse depends on your current architecture... you can checkout Realm, Room, GreenDao database options etc...
Second approach is more suitable for mobile applications since you only load data in list via API which is visible inside the recycler view, you can pass that data to activity and then in activity load the remaining data from another call for that specific item.
I'm assuming that each object of the data coming from the server is not of same size, leading to out of memory on some but not all the objects. So the better approach is to also modify server apt (if possible) to send only info which is required for the list in your call's response and have separate resource url to access full information for that object.
This may result in a minimal delay but it will not cause unexpected out of memory errors which is a big let down for the end user.
You can actually use Application class for this purpose, to make it even better
Set the data in Application class when you intend to pass
Retrieve the data at destination into Local variable from Application
After retrieving data into local variable, set object in Application to null
Using above approach will make sure your objects memory is released as soon you consumed it without holding it all the time throughout Application Lifecycle
I am building an app which allows the user to define multiple objects of a specific type, lets call them "Person"'s. The "Person" object is defined as such:
class Person {
public String name;
public int age;
}
These "Person" objects will be serialised and stored within the app, either in SharedPreferences or via a Cloud storage mechanism. This can be considered handled and working.
My problem is that I need an editor interface to allow the user to change the "name" and "age" of any particular "Person" instance in their collection. In order to make my UI feel as much like stock as possible, I would like my editor interface to resemble the "Preference" interface which Android implements. As such I need a way to make a "PreferenceActivity" load and save it's preferences from/to a POJO.
The reason I am looking to do this is so that I have a UI that feels like something the user is used to using. My other choice is to mimic the style and create all the handling code myself, which will take a lot of time.
I imagined the process would be to override the "load" and "save" functions of a "PreferenceActivity" to pull/push the values from a POJO provided "onCreate" via an "Intent" - and the return this POJO as an activity result to the caller.
Is this achievable?
What is the purpose of having it this way? I fail to see why this would be useful nor how it would be feasible with regards to the reference of the pojo containing the data. If it was possible then the saving object would most likely be a generic key-object map, where you would need to extract the data from, which is exactly how the Preferences already work.
If the point of this is just to have the information in a Person object why not just make a method that creates one based on the saved preferences.
----- Additions
If you add a static/singleton data handler(repository) in the App that will contain all the persons while the App is executing, and that it has some kind of identifier for each person. Then you can pass the ID in the intent to the PreferenceActivity which will in turn fetch the person object from the data handler and fill in the values of the PreferenceActivity based on it.
Add another Preference to the PreferenceActivity named "Save" or similar, which you resolve and bind in the activity. When clicked this will fetch the currently entered information (which will be saved in the SharedPreferences) and create a Person instance out of it. It should then pass this object to the data handler which will add it (or update it if the ID is already there) to the repository of Persons. At this time you should probably consider serializing the whole repository and save it, one easy way is just to JSON it all and put it into the SharedPreferences. Don't forget that you need to load this data the first time you access the data handler so that the previously saved persons are accessible.
I would also recommend you create Interfaces for the data handling action in case you want to add new or replace the implementation to for example database operations instead.
This way you can use the PreferenceActivity for add/editing Persons. Even though I would prefer create your own UI for it.
The application passes large number of objects (about 150 objects after parsing JSON format) via intent.putExtra();
Among them are serialized objects.
And the process of opening a new activity takes about 2 seconds...
Is there a way to speed up this process?
If you just want to pass data from one activity to another you can just use a static variable that is accessible from both activities. This eliminates the need to serialize and deserialize all the objects. Example:
public class Globals {
public static List<MyObject> myObjects;
}
In one activity you can set the data you want to pass in Globals.myObjects and the receiving activity can get it from there.
Be aware that this mechanism does have some drawbacks (like when Android kills your process and restarts it later). However, this can be the least troublesome way to simply hand a lot of objects from one activity to another.
One suggestion could be:
Use parceable where you are using serializable
Another suggestion could be:
Use something else to save/restore the data. e.g. a database
I think using a Singleton class for sharing large amount of temporary data between activities is a great way to go. Keeps it really quick and simple.
Although it can be done through Android Parcelable but it has storage limitation which can cause this error "!!! FAILED BINDER TRANSACTION !!!"
Is there any disadvantage in transferring values from Activity A to Activity B with static fields of a third class instead of the ExtraBundle?
In my application, sometimes I have like 15 - 20 values that I need to transfer between two Activitys. In my oppinion, it is more lucid solving this with static fields from a sort of TransferHandler.
At the moment, I have one disadvantage in mind: When a value is not put into the Extras before starting Activity B, I will get an Exception. Solving it with static fields, it it possible that I forget to assign a value, and if that value was assigned before from somewhere else, it might be that a wrong value is used in Activity B. Nonetheless, I think this is a "programmer problem" and not a "program problem". So are there any further minusses or am I free to choice a way? How's with the performance of the two variants?
First of all, if you plan to use static values, you should use your Application class to do this (because Android system assures you that it is a true singleton)
Thus, you can store your datas in attributes of your custom Application class, and use specific methods to store and get these values.
This will ensure you can't "forget" any values.
Also, with 15-20, I will strongly advice you to make a specialized POJO class to store all this...
I think the biggest disadvantage with using static classes for passing information in android is that static fields and objects can be cleared by the system at any time. That means that any static non final value can ALWAYS be null.
So it will probably work fine most of the time, but if you don't make sure to handle these null situations and your users start using your app they'll get a null pointer exception crash once in a while because the system decided it needed that memory stored in those static fields.
The best way for passing data between activities is by my opinion by using Intents, see here for a good example. Alternatively use a database or the the sharedpreferences.
Google also have a good read about pass data between Activities/Services here.
You cannot use a third class to transferring values form one activity to other. Here is the problem with it. You create one object in the activity-a then you store some values into it. Then after for using the values you need to create one more object in the activity-b then the object created in activity b will not be having the values you assigned in activity-a.
You can use SharedPreferences class to store valuo of variables:
SharedPreferences settings = getSharedPreferences("shared_pref", MODE_WORLD_READABLE);
SharedPreferences.Editor editor = settings.edit();
// With editor you put data
editor.putString(firstName, "John");
editor.putString(lastName, "Smith");
editor.commit();
You can access this data in all activities:
// With settings you access to data in different activities
SharedPreferences settings = getSharedPreferences("shared_pref", MODE_WORLD_READABLE);
String firstName = settings.getString(firstName, null);
String lastName = settings.getString(lastName, null);
I need to pass a List of my objects between activities. I do not want to use parcelable or serialize the data each time. I also do not want to save it in a local file or database. That probably leaves me with using static objects.
Lets say I to use ListA between activities Activity1 to Activity2. I ca
Approach1: Create a static ListA in one of those activities and do all my stuff of that static ListA.
Approach2: Create a static list in another class which I use just for storing this List and doing all my stuff on this list. But this means that this stays as long as my process is running and I have to manually set it to null when I do not need it.
Approach3. I am extending the above class to implement it using a static HashMap.
I have two methods one to store the list in a static HashMap using a unique key and another method to retrieve the list and remove it each time data is retrieved so that the List is no longer present in the static HashMap. So we essentially have to pass only the random key generated to store data between activities which I can pass as an extra using Intents.
Will there be any issues when I use any of the above approaches and which will be the best approach.
I'd consider creating an Application object and using it like a singleton to access your data. I've described the approach here: http://chrisrisner.com/31-Days-of-Android--Day-7%E2%80%93Sharing-Data-Between-Activities. Some people don't seem to like using the Application object in this manner but it makes more sense to me than putting a static object on an Activity.
Uggg statics! Man I wish all developers understood global variables are bad and how they make your program more brittle and your life hell. We only been talking about how bad they are for 30+ years, but unfortunately no one figures this out until they've utterly hung themselves on them.
First I'll say serializing your data is fast. There are great tools out there that will serialize your objects quickly that you can use I prefer http://flexjson.sourceforge.net for this.
So if you are just outright opposed to this you can pass this object through the Application by subclassing it, declaring your implementation in your Android Manifest, and each activity has access to the Application instance:
public class MyActivity extends Activity {
public void onCreate( Bundle bundle ) {
MyApplication application = (MyApplication)getApplication();
Object anInstanceFromAnotherActivity = application.getSomeInput();
}
}
The downside to this is when your application is reclaimed if the user returns to your application the memory is gone, and you can't get that input you might need of your screen. Android framework is trying to make you serializing things in the bundles because if it decides to destroy your application you can always rebuild yourself from the bundle. Now there are short cuts you can take like redirecting people to start over if the Application has been reclaimed, but those depend upon your program and what its doing if they make sense.
That's where using serialization wins out over all other forms of persistence (parcelables, files, databases) because it can be done in one line of code.