Accessing adapter and other data in an activity from main activity - java

I would like to know what is the proper way to access non-primitive data between activities. Although there are many questions and answers on the topic, I still think that my question hasn't been asked yet.
1) The main activity shows a custom adapter ToDoListAdapter. ToDoManagerActivity loads data for entries in its methods loadItems() and saveItems() in onPause() and onResume():
public class ToDoManagerActivity extends ListActivity {
toDoListAdapter mAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mAdapter = new ToDoListAdapter(ToDoManagerActivity.this);
.....
}
}
2) Another activity TodoChartActivity is fired from ToDoListAdapter view and is an activity that shows all entries, but differently. In order to show the entries, I need the data that is in adapter variable in main activity.
According to sources in the internet, there are different ways to make adapter variable (or any other) accessible in the second activity:
Make mAdapter public and static or make public getter&setter for it. BAD APPROACH
Access it from Application Singleton. BAD APPROACH
Copy loadItems() and saveItems() from ToDoManagerActivity and load them again into memory. Since I use memory much more that I actually need -- BAD APPROACH
Passing all data of one list entry through intent extras. TEDIOUS APPROACH, I was told. Also, let's imagine, that we need all entries, for example. So, it's a general question.
Do you have other suggestions, how to access complex objects and lists between activities?

ToDoManagerActivity is your first Activity, from here you are initiating ToDoListAdapter and from ToDoListAdapter you are inflating TodoChartActivity.
This is a common scenario. Since, your data is same, just share it across the adapters.
From ToDoManagerActivity pass the list of object in your ToDoListAdapter. Now, you have your data in this adapter. Now, pass the same data to TodoChartActivity.

A singleton is not necessarily a bad approach unless the lifetime of its contents is being held longer than they're used by the two activities. You could even put the container object in a WeakReference so that it's garbage collected after the two activities using the data are no longer around and holding references to the data. The major downside to this approach is that your activities can't save and retain they state if they need to be destroyed and recreated.
You could also persist your data to a sqlite database. If you don't want to break out all the fields from all your objects into sqlite fields, you could "cheat" and just serialize them as JSON with something like Gson that will automate this process if your model data can be represented as Java primitive types and basic collections. If you do this, then your activities can also rebuild themselves from the data persisted in sqlite in case they are destroyed by Android. The downside is that you now have to figure out when to remove this data from the DB when it's no longer being used. And if you need to use this scheme more than once at a time in your app, you need to be able to identify each collection of data using some id and make sure that id is shared between activities.

Related

ViewModel Data Lost When BackPress

firstly I want to say I am sorry. I am newbie in MVVM. I want to know how to retain the data in viewmodel ??
For example I have followed this codelab tutorial https://codelabs.developers.google.com/codelabs/android-lifecycles/#0.
I try to kill the apps then go back into the apps but the data is not saved .Why?
I tried to make new activity by intent it. I ln new activity I implement the same code as statelifecyle. But why when I backpressed and try to enter back the newactivity the data is not saved ?
To answer your questions:
Data in ViewModel is only persisted throughout the lifecycle of your activity. So if your app dies, your data is not saved. If you want it to persist, consider integrating an off-line data persistence library like Room or you can also use SharedPreferences depending on your use case.
According to this post: Android LiveData - how to reuse the same ViewModel on different activities?
When you call ViewModelProviders.of(this), you actually create/retain
a ViewModelStore which is bound to this, so different Activities have
different ViewModelStore and each ViewModelStore creates a different
instance of a ViewModel using a given factory, so you can not have the
same instance of a ViewModel in different ViewModelStores
In other words, different activities cannot share a single ViewModel. So if you want to switch pages while retaining data in your ViewModel, consider using fragments inside your activity instead.

Storing shared values in SQLite database vs passing through intents

My app initially makes a request for a list of objects from a server. These objects are currently kept in memory as an ArrayList<MyObject>. However, I want these objects to be passed through multiple activities before the user terminates the flow by pressing a button. I could make the ArrayList serializable and pass it through Intent extras. But I could also store MyObject(s) in a SQLite database and access/modify them in any Activity without having to go though intents. I was wondering what the norm is to accomplish this.
EDIT: forgot to mention that all the values would be deleted once the user terminates the flow.
SQLite is not the best way to go in your case since you don't need the data to be persistent after you close the app. It will just slow your app having to store and retrieve all entries on every activity transition. You can do one of the following instead:
Pass Serializable the way you described. Might be slower than the other alternatives though
Make MyObject implement Parcelable and use [intent.putParcelableArrayListExtra()](https://developer.android.com/reference/android/content/Intent.html#putParcelableArrayListExtra(java.lang.String, java.util.ArrayList))
Extend Application and load the list from the network in your Application.onCreate() and call getList() from activities that need it. That way you load it once and you don't need to pass it between different activities.

Android - Is using static fields a good practice for surviving activity restart?

Recently I have found a way to survive configuration changes. What I do is I declare the objects I want to protect as static fields. Is this a good practice?
It's never been a good practice. In my own experience I made a music player app with full of static variables and it's ram usage rocketed to more than 75 mb which is far more than any other of it's kind. The reason is, it stores the value of variable while activity is destroyed. if you have static variable on bitmaps or any other heavy file, it makes memory leaks which is not pleasant to see by user as not all devices got enough resources (ram) to keep up with heavy usage app.
Also static variables often make NullPointerExceptions as these are used by many other activities too and having a variable null may result in total failure of app...
I advise you to store the data in a SharedPreferences and just make one static field like integer and always use that to retrieve values from SharedPrefrences, its very clean and reduces the NullPointers. Moreover just one static makes u have more control over your app... For me it saves time to change 100's of static field rather than changing 1 as it's a lot easy and memory efficient...
I hope, I may help u a little !
Not really. You can cause a memory leak, because one of your static fields can hold a reference to the current Context, which could be destroyed without the reference.
The best way to survive configuration changes is to use the recomended method - Bundles. If you have to store something bigger and/or more persistant - use files, SharedPreferences or a database.
Use static may lead to nullpointer exception. Because when configuration change the current context is destroyed and if you access those context then it throws nullPointer exception.
So sharedPreferences or database is the best option for store the data.
Hope this help :)
One of the approach to retain Activity State, especially during configuration change is to make use of Fragments.
Instead of reinitializing your activity, you can alleviate the burden by retaining a Fragment when your activity is restarted due to a configuration change.
The fragments of your activity that you have marked to retain are not destroyed by Android system, when it destroyed your activity during configuration change.
You can add such fragments to your activity to preserve stateful objects.
Compare to using "static", this Approach is what being advocated at below link:
http://developer.android.com/guide/topics/resources/runtime-changes.html
I assume you can't just put in the bundle in onSaveInstanceState for some (very good) reason. So I think you have to compare with retain fragment.
If you have a static field in your class, like:
static Object myStaticReference;
you will no more worry about all the lifecycle jungle mess with onDestroy, onCreate, onSaveInstanceState of the Activity or Fragment. This will reduce considerably the code rows and make everything just easier to debug and understand. In any case you shall not have any reference in your object to a View, Activity, Fragment, Context and the like.
If for example you have something like this:
public class MyObject {
ArrayList metaData;
Context cntx;
public MyObject(Context cntx) {
this.cntx=cntx;
}
}
Then you still are in the lifecycle jungle, and you will have to pass (and repass) the reference of the context (and any other View relations you have) any time your Activity gets recreated in onCreate and still manage with onSaveInstanceState if there is any need. So in this case it is just useless.
With retain Fragment (which I suppose should be the other way) things are different too. The retain Fragment will last even when the Activity gets recreated, but you have to manage this additional fragment (the good part is that you do not need an UI for this).

Is it ok to use static properties to move data around the app?

I need to access a few of my custom objects in different activities of my application. For this purpose and for the sake of accessibility I have been using static properties for moving data from an activity to another.
For example I have the following class:
public class TrackItem {
public String title, imageUrl, mediaUrl, type, artist, desc;
public static TrackItem track;
}
And for starting an activity:
TrackItem.track = items.get(i); // 'items' is an arraylist defined elsewhere
Intent trackActivity = new Intent(c, TrackActivity.class);
startActivity(trackActivity);
And now inside the TrackActivity I can easily access TrackItem.track and use it's properties.
I just need to know if I'm making a mistake or not? Is there any better way to do this?
The android way of dealing with that problem is to make your class
Parcelable
and pass it with the intent from one activity to another.
If you are initializing your static variables in an activity be aware of loosing data, because in android activity can be destroyed at any point after its state changed to pause. Moreover, your static variables can be erased if the entire application is killed by the system, that is happening rather frequently. Then you'll get the
NullPointerException
trying to access your data.
If you really want to use static members handle their initialization in the
Application
class constructor, so they will be recreated on the start of your application, being killed.
But in general it is not a good practice in android.
I would say it is OK in certain cases, but there might be other more suitable solutions.
You could have a central data store class that uses the singleton principle and therefore would be accessible from everywhere. You would add the item id to the Intent for the new Activity. Then, with the id, you could get the item from the data store.
You could also make the item serializable and just add it to the Intent.
One thing to keep in mind when using static members is that it could lead to a memory leak. Static members are related to the class and are therefore only garbage collected if you either set them to null, or the whole app gets killed and the classloader unloads this specific class.
In general, this is an unsafe practice because it is difficult to keep track of who is manipulating its data. It is much safer to use static variables for bookkeeping information, such as an ID which you can use to go look up the appropriate TrackItem (e.g. in an SQLite database), which is its own object and does not have the chance of something else editing it when it shouldn't be. It terms of OOP, using static variables as shared data breaks encapsulation.
If you are looking to send data around the app, it would be much better to do so either with intents, as others are saying, or with SharedPreferences. Both have the advantage that you are dealing with only one instance of the object at any given time, SharedPreferences have the added advantage of keeping the data around after the app has been killed, so that users can resume with the same track that was playing when they closed the app. And both of these are safer than using static members as shared data fields.

Save object reference to Bundle in Android

Is it possible to save a reference to an object in a bundle?
Not the whole object, only the reference?
For example, Bundle class has method to get an object but I can't find the put or set method.
I need it because in an activity, I create an instance of my timer class, and then I start the timer: myTimer.start()
If I have to restart the activity, I want to restore the timer to it's previous value.
Hopefully you cannot do this. You can only store primitives in bundle. As Marcin Orlowski mentioned "storing" the whole object is achieveable through implementing Parcelable interface. By "storing" I meant storing object state. Implementing this interface helps you persisting your object state in different code sections without putting its all attributes to Bundle object over and over again.
When activity goes to pause state sooner or later all objects used by your activity will be removed by garbage collector, so storing references to them would be silly.
The official docs recommend using fragments for storing references during "configuration changes" (no, I don't think this means you need to repose your activity as a fragment, but to use a fragment as a storage medium (clarification needed)):
http://developer.android.com/guide/topics/resources/runtime-changes.html#RetainingAnObject
Retaining an Object During a Configuration Change
If restarting your activity requires that you recover large sets of data, re-establish a
network connection, or perform other intensive operations, then a full
restart due to a configuration change might be a slow user experience.
Also, it might not be possible for you to completely restore your
activity state with the Bundle that the system saves for you with the
onSaveInstanceState() callback—it is not designed to carry large
objects (such as bitmaps) and the data within it must be serialized
then deserialized, which can consume a lot of memory and make the
configuration change slow. In such a situation, you can alleviate the
burden of reinitializing your activity by retaining a Fragment when
your activity is restarted due to a configuration change. This
fragment can contain references to stateful objects that you want to
retain.
When the Android system shuts down your activity due to a
configuration change, the fragments of your activity that you have
marked to retain are not destroyed. You can add such fragments to your
activity to preserve stateful objects.
To retain stateful objects in a fragment during a runtime
configuration change:
Extend the Fragment class and declare references to your stateful
objects. Call setRetainInstance(boolean) when the fragment is created.
Add the fragment to your activity. Use FragmentManager to retrieve the
fragment when the activity is restarted. For example, define your
fragment as follows:
public class RetainedFragment extends Fragment {
// data object we want to retain
private MyDataObject data;
// this method is only called once for this fragment
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// retain this fragment
setRetainInstance(true);
}
public void setData(MyDataObject data) {
this.data = data;
}
public MyDataObject getData() {
return data;
}
}
Caution: While you can store any object, you should never pass an
object that is tied to the Activity, such as a Drawable, an Adapter, a
View or any other object that's associated with a Context. If you do,
it will leak all the views and resources of the original activity
instance. (Leaking resources means that your application maintains a
hold on them and they cannot be garbage-collected, so lots of memory
can be lost.)
You can't. You can only pass limited set of data types as you can see by looking at method lists in the docs and if you want more than single primitives then your class needs to implement Parcelable interface (or use helper like Parceler). Once you got that done you will be able to pass your object data via the Bundle (but still, not object itself)
One alternate approach is to convert the POJO to json and then to string the GSON library and save in bundle as string. Then retrieve it back from bundle as string and convert it back to object using the same GSON library. Example code below.
Convert JSON to String and save it in bundle
JsonElement json = new JsonElement();
String result = gson.toJson(json);
bunlde.putString("key",result);
Convert String from bundle to object again
JsonElement json = gson.fromJson(bundle.getString("key"), JsonElement.class);

Categories

Resources