New data suriving config changes in activity - android

Lets say, I have an activity that does a network query, recieves data and does something with that data, how fo I perserve that data on config changes not using livemodel, persistant presenters, DB or shared preferences. One variant is actually a new frgment with a bundle, after config changes the activity will restore the fragment with the data, any more sugestions?
following the answer bellow, might be usefull, dont use key and bundle, because bundle lso holds a key. Here's how u can do this in Kotlin
object DataHolder {
private val data = HashMap<String, Any>()
fun setData(key: String, data: Any) {
this.data[key] = data
}
fun getData(key: String): Any? {
return data[key]
}
fun removeData(key: String) {
data.remove(key)
}
}

It depends how long you want to preserve the data. If you want it to just be persistent for the current app session, you could use a singleton. Here's an example using a Bundle, but you could use any object that holds the data you care about. Here's an example with a map to let you set different persistent data entries with unique String keys.
public class MyDataHolder {
private static MyDataHolder instance = new MyDataHolder();
private final HashMap<String,Bundle> data = new HashMap<>();
private MyDataHolder() {};
public static MyDataHolder getInstance() {
return instance;
}
public void setData(String key, Bundle data) {
this.data.put(key,data)
}
public Bundle getData(String key) {
return data.getOrDefault(key, null);
}
}
then in your activity, to save some data
MyDataHolder holder = MyDataHolder.getInstance();
holder.setData("ActivityB",data);
and to get data (for example, in onCreate)
MyDataHolder holder = MyDataHolder.getInstance();
Bundle data = holder.getData("ActivityB");
if( data != null ) {
// use saved config
}
else {
// use default config
}
If you need it to persist beyond the current app session or persist even if the app is killed, you'll need to save it to a local file or network location. That can be either a built-in method (DB, SharedPreferences) but you don't want to use those, so you'd have to write your own method to read and write the data.

Related

How to display data in Fragment and Activity (both independent) from same Snapshot Listener (Firebase)

Would like to have your help on my weird problem that currently I am facing. I tried for couple of days but no luck and finally decided to post here to take help.
I created a Snapshot Listener attached to a Collection in Firebase defined as follows :-
public class FirebaseTypingStatusLiveData extends LiveData<List<documentSnapshot>> {
// Logging constant
private static final String TAG = "FirebaseQueryLiveData";
// Document Reference
private final DocumentReference documentReference;
// Listener
private final MyDocumentListener listener = new MyDocumentListener();
// Handler
private final Handler handler = new Handler();
private ListenerRegistration listenerRegistration;
// Flag to remove listener
private boolean listenerRemovePending = false;
private MutableLiveData <List<documentSnapshot> mutableLiveData = new MutableLiveData<>();
// Constructor
public FirebaseTypingStatusLiveData(DocumentReference documentReference) {
this.documentReference = documentReference;
}
public LiveData<List<documentSnapshot>> checknow(){
// Add listener
if (!Listeners.LIVESAMPLE.containsKey(documentReference)) {
listenerRegistration = documentReference.addSnapshotListener(listener);
Listeners.LIVESAMPLE.put(documentReference, listenerRegistration);
} else {
listenerRegistration = Listeners.LIVETYPINGSTATUSSAMPLE.get(documentReference);
}
return mutableLiveData;
}
// Listener definition
private class MyDocumentListener implements EventListener<DocumentSnapshot> {
#Override
public void onEvent(#Nullable DocumentSnapshot documentSnapshot, #Nullable
FirebaseFirestoreException e) {
Log.d(TAG, "onEvent");
// Check for error
if (e != null) {
// Log
Log.d(TAG, "Can't listen to query snapshots: " + documentSnapshot
+ ":::" + e.getMessage());
return;
}
setValue(documentSnapshot);
mutableLiveData.setValue(documentSnapshot);
}
}
}
}
The snapshot reads the data perfectly and advised as and when data is available.
The snapshot data is getting displayed 1. in Fragment (not part of Activity that i am talking about) 2. Activity through two view models that have the same code as follows :
#NonNull
public LiveData<List<documentSnapshot>> getDataSnapshotLiveData() {
Firestore_dB db = new Firestore_dB();
DocumentReference docref = db.get_document_firestore("Sample/"+docID);
FirebaseTypingStatusLiveData firebaseTypingStatusLiveData = new
FirebaseTypingStatusLiveData(docref);
return firebaseTypingStatusLiveData.checknow();
}
The Fragment & Activity code is also same except changing owner which are as follows :-
LiveData<List<documentSnapshot>> liveData = viewmodel.getDataSnapshotLiveData();
liveData.observe(this, new Observer<List<documentSnapshot>>() {
#Override
public void onChanged(DocumentReference docreef) {
String name = docreef.get("name");
stringname.setText(name); // The text is displaying either in Fragment or in Activity but not in both.
});
My problem is i need data in both i.e. Fragment & Activity whereas I am getting data either in Fragment or in Activity depending upon the code which I commented.
Kindly advise where I am making mistake. Thanks in Advance
Honestly, I am not sure that my answer wouldn't lead you away to the false way, but you can try.
My guess is that your problem could be somehow connected with ViewModel sharing.
There is a well-known task How to share Viewmodel between fragments.
But in your case, that can't help, because you have to share ViewModel between activities (now you have two separate ViewModels and that could be problem with Firestore EventListeners).
Technically you can share ViewModel between activities (I haven't try since usually I use Single activity pattern). For that as a owner parameter in ViewModelProvider constructor you can set instance of your custom Application class (but you have implement interface ViewModelStoreOwner for it). After that both in your activity and in your fragment you can get the same ViewModel with the Application class-instance:
val sharedViewModel = ViewModelProvider(mainApplication, viewModelFactory).get(SharedViewModel::class.java)
I made LiveData static that listens to changes in source data and provide updated content were ever required in different Activity.

Single Object in Realm

I want to persist object of User in Realm, and I want to persist only single object and get it everywhere like singleton UserProfile.getInstance().
So how to implement it?
I do it using dirty approach (as I mind)
public static User getInstance() {
Realm realm = Realm.getDefaultInstance();
User user = realm.where(User.class).findFirst();
if (user != null) {
// If object exists in db
return user;
}
// If object does not exist, we should to create it
realm.executeTransaction(realm -> {
realm.insertOrUpdate(new User());
});
// After create we should to return already 'managed' object.
return realm.where(User.class).findFirst();
}
This code smells bad, but I not found any better solution. Also I not found any useful information in official docs.
How do you implement singleton objects in Realm?
Since this is a Singleton you can use copyToRealm instead of copyOrUpdate which infers that you want to update the user (defies the goal you're trying to achieve).
class Foo {
private volatile User user = null;
public User getInstance() {
if (user == null) {
synchronized(this) {
if (user == null)
// If object does not exist, we create it
realm.executeTransaction(realm -> {
user = realm.copyToRealm(new User());
});
}
}
return user;
}
}
Note the use of copyToRealm instead of insertToRealm since copy* methods will return the managed object (no need to query for it again).

Cannot retrieve saved SharedPreferences in another activity

So we're working on this Android app. We've got a login activity that receives some information when the user logs in successfully. We've got a class called SessionManager that handles saving said data to SharedPreferences.
SessionManager always retrieves SharedPreferences from the same file always. It's hardcoded in there.
public SessionManager(Context context) {
this.preferences = context.getSharedPreferences(PREFERENCE_NAME, 0);
this.editor = this.preferences.edit();
this.jsonParser = new Gson();
}
The jsonParser is there so we can save the info as a json object.
public final void storeProfile(UserProfile profile) {
this.editor.putString(STORAGE_KEY, this.jsonParser.toJson(profile));
this.editor.commit();
}
private static final String STORAGE_KEY = "PROFILE";
private String getStoredValue() {
return this.preferences.getString(STORAGE_KEY, null);
}
public UserProfile getStoredProfile() {
String val = getStoredValue();
return (val == null) ? null : this.jsonParser.fromJson(val, UserProfile.class);
}
In theory, this should mean we should be able to store the profile in one activity, then get it back in another activity, right?
Except that's not happening! It looks like I can only retrieve saved information in the same activity where it was saved!
I call storeProfile() in the login activity, then getStoredProfile() in another activity, and it returns null.
I call storeProfile() in the login activity, then getStoredProfile() in the login activity, and it returns the stored profile. It also works if two different SessionManager instances call storeProfile() and getStoredProfile().
I set the stored profile manually in the other activity, and it retrieves the manually stored profile just fine.
Is there some scope rule or something to SharedPreferences that I'm missing?
Turns out I'm a fool and I was accidentally wiping my preferences every time I tried to get them.

How to restore data in any activity after returning from another activity?

When i start my android app, it loads large amount of data from server in mainactivity and adapt it with listView and when listview is clicked , new activity will launch . When return back to mainactivity from current activity , my app again load data from server that was previously loaded? How can i use 1st time loaded data after returning back to same activity next time??
There are several possible approaches to your problem depending on what you want to do. First of all, in the activity where you download and process the data, you should override the onSaveInstanceState and onRestoreInstanceState methods so that your data is persisted and survives orientation changes (and not loose the work you did processing the data).
If your application simply opens a detail activity, from the main activity, then when you press back, your data will be available. You do not have to reload anything. If this is not the case, then there may be some problem with how you load the data in the activity's lifecycle (e.g. avoid loading and processing data in the onStart/onResume methods).
If you want to persist data after your application has died, you can either use http caching (e.g. with OkHttp) or use an Sqlite database, as others have pointed out. These approaches can also be combined for additional performance gains.
You can cache the server response in shared preference in form of JSON and store to avoid making server calls each time. (However this is recommended only when data is small)
Example :
public <T> void putJsonIntoSharedPreferences(String key, T object) {
SharedPreferences sharedPreferences = getSharedPreferences();
SharedPreferences.Editor edit = sharedPreferences.edit();
String json = convertToJson(object);
edit.putString(key, json);
edit.commit();
}
public <T> T getFromSharedPreferences(String key, Class<T> clazz) {
SharedPreferences sharedPreferences = getSharedPreferences();
final String fromSharedPreferences = sharedPreferences.getString(key, "");
T object = null;
if (!fromSharedPreferences.equals("")) {
try {
object = JsonConverter.fromJson(fromSharedPreferences, clazz);
} catch (IOException e) {
return null;
}
} else {
try {
return clazz.newInstance();
} catch (Exception e) {
return null;
}
}
return object;
}
private <T> T fromJson(String json, Class<T> clazz) throws IOException {
ObjectMapper objectMapper = getObjectMapper();
if(json!=null)
return objectMapper.readValue(json,clazz);
return null;
}
private String convertToJson(Object object) {
ObjectMapper objectMapper = new ObjectMapper();
String string = null;
try {
string = objectMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
Log.d(ComponentConstants.JSON_PARSE_ERROR_TAG,e.getMessage(),e);
}
return string;
}
You can simply use an http client with caching like OkHttp. more complicated yet more appropriate in some cases is to manage an internal Sqlite Database.
As a Side note You can save all your data and load it when activity is reloaded but you should not. as no metter what way you choose, IO will be involved and its going to be a heavy operation if your data is big enough.
Its good practice to load exactly what the activity needs to show when its being loaded, and not the full data. You should use CursorLoader or CursorAdapter to load dynamically what your activity needs as user interacts with it...

for Android is it a good idea to pass a list of object from one Activity to another ?

Is it a good idea to pass a list of object from one Activity to another for Android ?
It is quiet troublesome to pass a list of object in an intent, and I wonder whether it affect the performance if the object list is too large or the object is too complicated.
Is it a better solution to get the list of object from other place, for example, query the DB once more , or save the list of object in a temporary class and fetch it in new Activity?
As long as you are passing Parcelable objects' list, nothing's wrong when passing through Intent. Regarding performance, that is up to you and the amount of data you pass.
As per my experience, If you are passing data up to 1MB of size, it should be fine. Anything above that will fail as this seems to be the limit. Read this.
Besides, you are welcome to use preferences, SQLite, files or static-object-referencing methodologies to fetch your data anytime and anywhere.
Solution1 : Use Intent
Send :
Intent data = new Intent(Activity1.this,Activity2.class);
data.putParcelableArrayListExtra(Constant.LIST_OBJECT,
(ArrayList<? extends Parcelable>) getObjects());
receive :
List<YOUR_OBJECT> objects = data.getParcelableArrayListExtra(Constant.LIST_OBJECT);
Solution2 :
public class SessionData {
private static SessionData instance;
private final List< YOUR_OBJECT > listSessionObjects;
private SessionData() {
listSessionObjects = new ArrayList<>();
}
public static final SessionData getInstance() {
if (instance == null) {
instance = new SessionData();
}
return instance;
}
public List<YOUR_OBJECT> getListSessionObjects() {
return listSessionObjects;
}
public void setListSessionObjects(List<YOUR_OBJECT > objects) {
listSessionObjects = objects
}
}
to use it :
SessionData.getInstance().getListSessionObjects();
SessionData.getInstance(). setListSessionObjects(objects);

Categories

Resources