preserving Singleton DB data on rotation Without using ContentProvider (Android) - android

How do you preserve singleton database data in an activity after a screen rotation without using Content Provider or loaders?
This was an interview question I got stumped on.
ContentProvider with cursor loader was my first thought, but they said they didn't want that. Then I thought of saving the cursor returned from the singleton DB's query method, but I couldn't "put" a cursor in the out bundle in OnSavedInstance, so I have no idea.
I also asked them "isn't using a singleton database discouraged?" to which they said "yes, but this is just for interview purposes."
Maybe this can help someone in the future who encounters this question.

"In an activity" sounds like I'm not allowed to pass all the stuff to the application class (which will not be destroyed on screen rotation). But just in case this is an option:
You write your own class which extends from Application. The official documentation tells you how to do it but states that you basically don't need to. Having said that, this Stack Overflow post is a collection of possible exceptions from the rule.
In the Activity, you access it like this:
MyApplication app = (MyApplication)getApplication();
But personally I'd keep data for one Activity not in the Application class but inside a retained Fragment. They survive configuration changes but keep in mind that they are not part of the back stack. So if you have a savedInstanceState != null, the retained Fragment may nevertheless have been recreated in its initial state e.g. if the app has been paused for a while.
The guide on Handling Configuration Changes shows how to use retained Fragments

You can use fragments and use Fragment#setRetainInstance(true) for that. And all the data should be saved across configuration changes. Note that a retained fragment is not visual its placed along your Activity or Fragment This might help you: https://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html

Related

Best practices: Restore activity state [duplicate]

I'm a little confused about the Activity being destroyed and recreated when the user's device is rotated.
I've been reading around, and I understand the rationale for doing so (it basically 'forces' the developer to make sure they haven't 'missed' anything upon rotation/language/other changes)
I'm happy to respect best practice if it is seen as such, however it begs the question:
How do I 'remember' the state of the game/app, so that when the screen is rotated I have something from which to re-generate what the user was looking at?
From what I can see, absolutely everything is destroyed, the base class constructor runs and all variables in the Activity are 'null'.
I suspect the 'savedInstanceState' (Bundle class) is where I would collect that data, but reading around it only seems to be used for when the app is closed from lack of resources (and a few other extremely fringe cases)
Am I misinformed or misunderstanding the purpose of savedInstanceState? Is it wise to abandon best practice (letting the Activity be destroyed) if I'm mindful enough to not miss anything upon rotation? Thanks in advance for any advice.
I should note this question applies to game programming (I'm not using a layout XML)
Do you need your activity to be recreated? Is there work you want to do on rotation? savedInstanceState is where you would store data to be passed to the recreation of the Activity, but since you aren't using XML layouts you may consider just adding android:configChanges="orientation" to your activity in the manifest. This will give you manual control over what happens during rotation change.
In additional to my original comment, I would override config changes for keyboard being show as well:
android:configChanges="orientation|keyboardHidden"
If there is work you want to do on rotation change you can override onConfigChange and do anything you need there.
It's hard to say that there is a time when you should always override config changes, but I often do it when I have no dependency on resource folders that are size or orientation specific and I'm doing a lot of work that would take too much time to recreate.
There are other best practices to store data while activitiy config changes.1. Developer Doc Handling Runtime Changes2. Alex Lockwood : Handling Configuration Changes with Fragments, This will answer all your questions reagarding config change and storing data.
Impt:
Fragment file to store data. You must set setRetainInstace(true) in order to store data and retrieve while your activity config changes.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// retain this fragment
setRetainInstance(true);
}

Dynamic Fragment recreation and setRetainInstance(true)

I don't completely understand how an activity and its fragments are recreated.
Imagine the following scenario: you have a database, activity reading this DB and a fragment, which will be created, if the activity's query to DB has some data. If DB is empty a fragment's onCreateView will cause an exception.
Now imagine we moved the app to background for a while and activity was destroyed. Moreover, the DB changes and gets empty.
How this structure will be recreated? Activity starts and understands that fragment is not needed, so everything is ok, or the fragment is recreated then anyway, because it already was created? Does setRetainInstance(true/false) has an influence on this process?
Making it more complex: we have several fragments, where one decides, if the other should be created or not in a similar way. What is the order of fragments recreation? Is it some kind of a race condition or the order is predefined?
Finally, how can we prevent or control the recreation of a fragment? I guess I have a situation, when sometimes a fragment is recreated not in right time. How such an architecture could be fixed? Simply removing fragment with onStop()?
I don't completely understand how an activity and its fragments are
recreated.
First of all it's very important to have very clear how the lifecycle of the Activity/Fragments works in Android. Here I link what is IMHO the most complete diagram on the subject (even more than the official one):
http://staticfree.info/~steve/complete_android_fragment_lifecycle.svg
Imagine the following scenario: you have a database, activity reading
this DB and a fragment, which will be created, if the activity's query
to DB has some data. If DB is empty a fragment's onCreateView will
cause an exception. Now imagine we moved the app to background for a
while and activity was destroyed. Moreover, the DB changes and gets
empty.
This kind of operation, like reading or writing to a DB (or connecting to a web service or more) are considered I/O and should get managed properly in background. I don't know concrete details in your case but in your example, you can use a CursorLoader. Android docs says the following:
A loader that queries the ContentResolver and returns a Cursor. This
class implements the Loader protocol in a standard way for querying
cursors, building on AsyncTaskLoader to perform the cursor query on a
background thread so that it does not block the application's UI.
Moreover
If DB is empty a fragment's onCreateView will cause an exception.
If a DB is empty the fragment should not cause an exception (any), if you're using, for example, a CursorLoader it will eventually get a Cursor with 0 entries. Later on (and again this depends on your implementation), if you update your database you can do a new CursorLoader petition to get a refreshed Cursor with the new data (the same applies if you rotate your device).
Two good tutorials that talks more in deep about CursorLoader are How to use Loarders written by Wolfram Rittmeyer and Life before Loaders by Alex Lockwood
Activity starts and understands that fragment is not needed, so
everything is ok, or the fragment is recreated then anyway, because it
already was created? Does setRetainInstance(true/false) has an
influence on this process?
An activity is a "dumb" component, by itself it does not know nothing about what a fragment is (well that's not quite true because the implementation of the activity knows about fragments). But for good sake it really depends on what you try to do. If you have a fragment in your activity it will be eventually recreated. Two classes you have to keep an eye to understand how this work is:
FragmentActivity.java
FragmentManagerImpl.java
Finally, how can we prevent or control the recreation of a fragment? I
guess I have a situation, when sometimes a fragment is recreated not
in right time. How such an architecture could be fixed? Simply
removing fragment with onStop()?
Never mess with lifecycle events by yourself, this is managed properly by the android runtime.
The fragment's lifecycle is tied to that of the Activity. From the Android docs:
Stopped
The fragment is not visible. Either the host activity has been stopped or the fragment has been removed from the activity but added to the back stack. A stopped fragment is still alive (all state and member information is retained by the system). However, it is no longer visible to the user and will be killed if the activity is killed.
When the Activity is destroyed, it will call the onCreate() method to recreate the Activity. This means that if the underlying database has changed, the Activity can decide not to launch the fragment.
You can read more about the Fragment lifecycle here, and the Activity lifecycle here.

setRetainInstance fragment with UI Android

Ok, I created a Fragment with some UI (couple textboxes and stuff) and I used setRetainInstance since Im running an AsyncTask to query a server (request can only be sent once) and I need the result of the AsyncTask. So my question is:
Is it wrong to retain the whole fragment with the UI? I saw couple examples where people use an extra Fragment to use the setRetainInstance but.. is there anything wrong not using that extra one??
If there is an issue with using the setRetainInstance why is that? Couldn't find any info in the documentation regarding this.
Even if you use setRetainInstance(true), your Fragment will still recreate its views when you rotate (you will get a call to onDestroyView and then onCreateView). As long as you don't keep references to views past onDestroyView, there will not be any leaks of the old Activity. The best approach would be to explicitly null the references in onDestroyView, but your code in onCreateView would generally overwrite those references anyway.
There are many examples online (including some official ones) where people use a separate fragment (without a view) to retain data. Assuming what I said above is correct, then this is unnecessary (for the sake of preventing leaks). In many cases, you may end up with cleaner code/architecture if you use a separate fragment whose responsibility is just to handle the data and not worry about the UI.
You can check to see if you are leaking Activity contexts after rotating by using Eclipse MAT.
If you are locking your orientation then you should be fine. Otherwise you can end up with memory leaks if you retain widgets that are associated with a particular activity instance.

Workaround for Android bug pertaining to setRetainInstance(true)

Please check out this issue: http://code.google.com/p/android/issues/detail?id=20791
The project (https://github.com/kaciula/BugRetain) uses a CursorLoader to take 2 values from a database through a content provider and shows them on screen. The scenario is this: From activity A, go to activity B, switch once the orientation and go back to activity A. The values from db are no longer showing.
Can anyone provide a workaround for this issue? The problem doesn't appear with only CursorLoader but with any loader. As a consequence of this bug, I can't write an app with fragments that use setRetainInstance and is available in both orientations. I really need a workaround until the Android guys fix the issue. Any ideas?
Do not set your fragments as retainable if you use Loaders. If you need to store some data between configuration changes, create another retainable fragment and pass this data to him.
A similar example can be found here: FragmentRetainInstanceSupport. But this example uses extra fragment for threading purposes. In your case this extra fragment will be used as data container.

Using the Android Application class to persist data

I'm working on a fairly complex Android application that requires a somewhat large amount of data about the application (I'd say a total of about 500KB -- is this large for a mobile device?). From what I can tell, any orientation change in the application (in the activity, to be more precise) causes a complete destruction and recreation of the activity. Based on my findings, the Application class does not have the same life-cycle (i.e. it is, for all intents and purposes, always instantiated). Does it make sense to store the state information inside of the application class and then reference it from the Activity, or is that generally not the "acceptable" method due to memory constraints on mobile devices? I really appreciate any advice on this topic. Thanks!
I don't think 500kb will be that big of a deal.
What you described is exactly how I tackled my problem of losing data in an activity. I created a global singleton in the Application class and was able to access it from the activities I used.
You can pass data around in a Global Singleton if it is going to be used a lot.
public class YourApplication extends Application
{
public SomeDataClass data = new SomeDataClass();
}
Then call it in any activity by:
YourApplication appState = ((YourApplication)this.getApplication());
appState.data.UseAGetterOrSetterHere(); // Do whatever you need to with the data here.
I discuss it here in my blog post, under the section "Global Singleton."
Those who count on Application instance are wrong. At first, it may seem as though the Application exists for as long as the whole app process exists but this is an incorrect assumption.
The OS may kill processes as necessary. All processes are divided into 5 levels of "killability" specified in the doc.
So, for instance, if your app goes in the background due to the user answering to an incoming call, then depending on the state of the RAM, the OS may (or may not) kill your process (destroying the Application instance in the process).
I think a better approach would be to persist your data to internal storage file and then read it when your activity resumes.
UPDATE:
I got many negative feedbacks, so it is time to add a clarification. :) Well, initially I realy used a wrong assumption that the state is really important for the app. However if your app is OK that sometimes the state is lost (it could be some images that will be just reread/redownloaded), then it is fully OK to keep it as a member of Application.
If you want to access the "Global Singleton" outside of an activity and you don't want to pass the Context through all the involved objects to obtain the singleton, you can just define a static attribute in your application class, which holds the reference to itself. Just initialize the attribute in the onCreate() method.
For example:
public class ApplicationController extends Application {
private static ApplicationController _appCtrl;
public static ApplicationController getAppCtrl()
{
return _appCtrl;
}
}
Because subclasses of Application also can obtain the Resources, you could access them simply when you define a static method, which returns them, like:
public static Resources getAppResources()
{
return _appCtrl.getResources();
}
But be very careful when passing around Context references to avoid memory leaks.
Dave, what kind of data is it? If it's general data that pertains to the application as a whole (example: user data), then extend the Application class and store it there. If the data pertains to the Activity, you should use the onSaveInstanceState and onRestoreInstanceState handlers to persist the data on screen rotation.
You can actually override the orientation functionality to make sure that your activity isn't destroyed and recreated. Look here.
You can create Application class and save your all data on that calss for use that anywhere in your application.
I know this is the very old question but using the ViewModel from the jetpack components is the best way to preserve the data between Activity rotation.
The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations.

Categories

Resources