I want to add a HashMap to a singelton (which will be the application instance object) to pass data between activites. I cant use a Bundle as the objects are too big to be passed through an intent.
I'm currently using this implementation:
public class MyApp extends Application {
private static MyApp singelton;
private static Map<String, Object> tempDataStorage;
#Override
public void onCreate() {
super.onCreate();
singelton = this;
tempDataStorage = new HashMap<String, Object>();
}
// puts the object in the Map and returns the key
public static String putDataInStorage(Object data) {
String key = UUID.randomUUID().toString() // generate a key
tempDataStorage.put(key, data);
}
// gets the object from the Map and deletes it to save memory
public static Object getDataFromStorage(String key) {
Object o = tempDataStorage.get(key);
tempDataStorage.remove(key);
}
}
putDataInStorage() is called from activity A to pass the data to activity B which then calls getDataFromStorage() with the key passed through the intent. If activity B gets destroyed by android, it also calles putDataInStorage() in its onSaveInstanceState() method to reclaim the data later.
Im still having a problem with android destroying my application after some time if the app is not used. If the user then comes back, it seems to recreate Activity B with the Bundle from saveInstanceState, while the new HashMap is empty.
I first thought about writing the Data from the Map into an SQL-Database when the app is destroyed and recreate the objects on recreation. But this would lead into an endlessly growing Map (and therfore endlessly growing memory usage) as the objects would never disappear. Not a good solution though.
As the data is fetched from a webserver, my second idea was to also save a reference to the data in the saveInstanceState() of Activity B (eg. the parameters of the GET-request). If activity B is then confronted with a NullPointerException, it can refetch the data.
Is that a good solution? If not, whats a better one?
Thanks for your help!
You should not count on saving dynamic data. Application can be killed at any time when other apps need memory. Use SharedPreferences to save your data permanently.
Related
In my app , at a particular screen there is Arraylist which is a source of recycler view . There are many buttons on that screen which takes you to next screen , next screen may be a single plain activity or activity with view pager and tablayout and that fragment may contain buttons which takes you to next screen .In some screen i can edit the Song class field too . My problem is that i am confused whether the send the list to next screen and further next fragment or next screens through intent or should i make that static and access it anywhere . Again and again i have to parcel wrap and then unwrap then send it to fragment then wrap for the fragment then unwarp it then send it to adpater attached to fragment , this is long process and i am afraid that anyone can change that list in any screen and secondly this whole process is cumbersome every time sending intent and receiving intent .
Passing the Values from Intent have chances of data loss so do not pass the multiple Values with the Intent. So it will be better to access the values from a Static class if the values are not changing. If sometimes values are changing then pass these with Intent.
You can also go with the SharedPreferences, it will be more feasible in your case.
You can shift to flux architecture. Redux store kind of state management.
Who ever needs data queries to store. And data changes automatically dispatched to listeners.
SharedPreferences are NOT made to pass data between Activities/Fragments. They are here to store data that need to persist when the app is closed.
An option could be to use some kind of "cache" class that will store your data. So let's say you display the list of whatever data you want on the first screen, then the user selects one of the items to see the details/modify it. So you give the position of this data (in the array stored in the cache) to your next fragment and this next fragment asks the cache to give to it the data, based on the position it has received.
Example
Cache class
public class Cache{
List<Object> data;
// ... Implementation
public List<Object> getData(){
return this.data;
}
public setData(List<Object> data){
this.data = data;
}
public Object getObject(int position){
return data.get(position);
}
}
List Activity
public class ListDataActivity extends ListActivity{
public void onCreate(...){
// get the data
...
// Set the data to the cache
Cache.getInstance().setData(data);
// Display the list
...
}
public void onItemClicked(...){
Intent intent =....
intent.put(ITEM_POSITION, pos);
startActivity(intent);
}
}
Details Activity
public class DetailsActivity extends Activity{
public void onCreate(...){
//...
// get data from the cache
int pos = getIntent.getInt(ITEM_POSITION);
Object obj = Cache.getInstance().getObject(pos);
// Display the details
...
}
}
I've recently tried to simplify some data structures by posting them into a simple key, value map. I push a log to verify the value has been associated to the key during the .put method.
When I later call a .get, the value is no longer available. Important code snippets:
MainActivity.class
public final HashMap<String, Integer> resumeMap = new HashMap<String, Integer>();
if (resumeMap.containsKey(url)) {
Log.i("Value is there!", url);
resumeTime = resumeMap.get(url);
Log.i("Value set to:", "" + resumeTime);
} else {
resumeTime = 0;
Log.i("Value is not found!", "" + url);
}
public void setHashmap(String url, Integer time) {
resumeMap.put(url, time);
int newTime = resumeMap.get(url);
Log.i("Setting:", "URL: " + url);
Log.i("Setting:", "TIME:" + newTime);
}
VideoPlayer.class
MainActivity setter = new MainActivity();
setter.setHashmap(urlString, player.getCurrentPosition());
In the setHashmap method, the log is correctly outputting both url and time as expected. However resumeMap.containsKey(url) is remaining false, even while the debugger is confirming an expected matching key via "Value is not found!" output.
To make clear, during first pass in MainActivity, I expect a not found result, the VideoPlayer class is then called with the resumeTime of 0 with proper results. I verify the setting of the key and value, and when I open the same link I am still receiving a 0.
I originally built the Hashmap in it's own class, with the same results. I moved the map to the MainActivity just to debug. Thank you for any assistance you may be able to provide.
Activity instances are quickly destroyed and re-created all the time, for example, when you rotate the screen (but for other reasons as well). It often appears that the Activity is "still there", but the actual underlying object instance has been destroyed and recreated.
A quick way to verify that that is really the problem before going down the instance state bundle path is to make the HashMap static to prevent instance destruction from destroying it.
Sometimes, static maps like this are OK, but proceed with caution, as structures like this open up avenues for leaking memory all over the place. A better approach is to use some sort of persistence, or if you only need the information while the Activity is being used, pass the information around using onSaveInstanceState and onRestoreInstanceState (see http://developer.android.com/training/basics/activity-lifecycle/recreating.html)
I have created an application that takes in user name and other details for a transaction and then fills them in a database. At times the application shows odd behavior by filling the SAME details in the database twice as two transactions. Even though the new values are read but not STORED in the static variables.
Therefore I needed help in flushing the values of all my static variables at the end of each activity to avoid overriding of the previous values in a fresh transaction.
EDIT :
public class One
{
static String var;
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
var="blah";
}
}
public class Two
{
static String variable = One.var;
// This is where i am accessing the value of the variables from previous activities.
//CODE
}
May these help you..
Using Static Variables is like a nightmare in any activity as it stores memory through out the activity..
I think you can try some other memory store to overcome your problem of passing value from one activity to another..
In my opinion u can store values in SharedPreference or either you can pass value through intent to other activity where ever it is required..
Hope these will help you..
EDIT:
Intent in = new Intent(MainActivity.this,SecondActivity.class);
You can use more than one putExtra() method to put several values and can fetch then in Second Activity
in.putStringArrayListExtra(String name, ArrayList<String> value);
StartActivity(in);
In Second Activity:
Intent in = getIntent();
ArrayList<String> Roleids = new ArrayList<String>;
RoleId = in.getStringArrayListExtra(String name, ArrayList<String> value)
I have several Activities in a program. Lets say, activities A, B and C.
Activity A is the main activity in this context. It contains object X, that must be accessible for all other activities (Activities: B and C).
Activity A will start activity B and then B will start ะก. After that both activities A and B are in the background and can be killed by the OS. How should I pass object X to activities B and C in order to be sure that object X will not be killed when A&B are killed?
Why don't you can create your X Object with SingleTon ? You can keep it alive as long as you want and you can get the same Instance from where ever you want.
public class TestObject {
private static TestObject testObjectInstance;
/* put you data here */
private TestObject() {
}
public TestObject getTestObjectInstance() {
if (testObjectInstance != null) {
return testObjectInstance;
} else {
testObjectInstance = new TestObject();
return testObjectInstance;
}
}
public TestObject createNewTestObjectInstance() {
testObjectInstance = new TestObject();
return testObjectInstance;
}
}
The best way will be to save X value in Shared Preferences.The Value of X will be retained even your A and B activities are killed.
Check this Link for How to use Shared Preferences:
How to use SharedPreferences in Android to store, fetch and edit values
There are three possible ways:
Two of them were mentioned here.
Via SharedPreferences. But remember that the SharedPreferences retain the value when the application is closed. This is the best solution if you want the value to be "permanent" on your application.
Creating a singleton object. This is the best solution if you want to manipulate the object in all activities but don't want to save it for another runs.
Sending the data via extras. This is the best solution if you only want the VALUE of the object and don't want to manipulate it.
try the putExtra() function in Intent
You can only use it for primitives.
If your X is contains only primitives, you may write a function in X,public Intent fillIntentWithX(Intent intent) which take the intent object as argument, and fill the intent object with the primitives in X, and return the intent object.
similarly, write another function in X, public X getXFromIntent(Intent intent) which takes an intent as an argument, extracts the primitives residing in it to form a new object X, and return it.
use fillIntentWithX() to fill the intent, which launches B, with the
object X's properties.
use getXFromIntent() to extract X out in
activity B, and so on.
I have two activities, Activity A and Activity B. I pass objects from Activity A to Activity B using intents. When i make changes to the Object in Activity B the data changes does not get reflected in Activity A. Have i missed out on something?
You are missing the fact that when you pass Object O from Activity A to Activity B via intents, activity B receives a COPY of object O. The way things work is that The object O gets serialized (converted to a sequence of bytes) and that sequence of bytes is then passed to Activity B. Then activity B recreates a copy of object O at the moment it was serialized. Any changes to the original object after it was serialized are not reflected in it's copy.
If both activities are part of the same application then just use a static field (or singleton) to communicate between them.
If you are passing a String, then it will not change since they are immutable.
Edit: See below for an alternative to Intent extras.
If you wish to use the architecture of passing immutable objects in messages you can create an immutable serializable data class. Pass an immutable instance of the data class in the intent using startActivityForResult. When the second activity is completed, send a different instance of the same immutable data class back to the first activity where it is trapped in onActivityResult. So in code, given an immutable class PasswordState.java with public final fields.
public final class PasswordState implements Serializable {
Create an instance of this immutable class and send it to the second activity as in:
private void launchManagePassword() {
Intent i= new Intent(this, ManagePassword.class); // no param constructor
PasswordState outState= new PasswordState(lengthKey,
timeExpire,
isValidKey,
timeoutType,
password,
model.getIsHashPassword(),
model.getMinimumPasswordLength()); // NOT minimumHashedPasswordLength
Bundle b= new Bundle();
b.putSerializable("jalcomputing.confusetext.PasswordState", outState);
i.putExtras(b);
startActivityForResult(i,REQUEST_MANAGE_PASSWORD); // used for callback
}
The second activity returns a new object when it is done.
PasswordState outPasswordState= new PasswordState(lengthKey,
timeExpire,
isValidKey,
timeoutType,
password,
isHashPassword,
minimumPasswordLength);
Bundle b= new Bundle();
b.putSerializable("jalcomputing.confusetext.PasswordState", outPasswordState);
getIntent().putExtras(b);
setResult(RESULT_OK,getIntent()); // call home with data on success only
finish(); // go back <=== EXITS Here
Where it is trapped in Activity one.
// ON_ACTIVITY_RESULT used for callback from ManageKeys.java
protected void onActivityResult(int request, int result, Intent data)
{
switch (request){
case REQUEST_MANAGE_PASSWORD:
if (result == RESULT_OK) { // Success. New password.
try {
PasswordState inMessage= (PasswordState)data.getSerializableExtra("jalcomputing.confusetext.PasswordState");
password= inMessage.password;
timeExpire= inMessage.timeExpire;
isValidKey= true;
writeToPrefs(); // support first launch and incoming tagged sms, save pw
}
catch(Exception e){ // data == null, extras == null
password= "";
isValidKey= false;
timeExpire= PasswordState.LONG_YEAR_MILLIS;
Log.d(Utilities.TAG,"FailedToGetResult",e); // will be stripped at runtime
}
...
break;
}
}
When you are done prototyping and the data objects are stable, you can refactor the code to use parcels instead of serializing objects. Since a copy is being sent between activities using serialization, it could be argued that the use of an immutable object is overkill. Using a mutable object and serializing a mutable object to and from the second activity would simplify the implementation.
Hope that helps.
JAL