I am working on the PreferencesActivity of an App:
https://developer.android.com/reference/android/preference/PreferenceActivity
Pref. activity looks something like this :
http://i.imgur.com/qnHbJX9.png
There are some actions we need to take when the user selects or deselects options in this Pref. Activity (which involve turning services on/off, making a connection to DB etc.)
Currently, most of the logic to take this action is written in the activity itself (the activity is huge) and some static classes.
We now want to save these settings on the server and perform these actions on user login (without launching the pref. activity). How should I move the logic?
To completely static classes? To individual broadcast receivers? Something else?
Image taken from here:
Preference Activity on Preference Click Listener
Create PreferencesStorage class, which load asynchronously settings from server, and send changes in preferences to the server.
Then use this class in your PreferenceActivity and SplashActivity (if you use di - then inject this class as singleton, if no - use instance of this class).
If some of settings need to be readed in every activity - yes, I think BroadcastReceiver will be best solution.
If you want to use clean architecture approach, then you can create PreferencesInteractor, which includes PreferencesRepository, and inject this interactor to SplashPresenter and PreferencesPresenter.
Related
There are 3 activities: A->B->C. Each contains a Button (to open next activity) and a EditText.
For example: if I type some text in C and go back to A(by pressing Back Button), how can I see the same text there?
I know 3 solution:
LocalBroadcastManager
SharedPreferences
Create Singleton class with static field and then get this field in onStart method of A - which cons of this solution?
IMHO, there are always cons in using Singleton design pattern in your applications. Some of them are (from the top of my head):
Coupling between otherwise unrelated objects and flows through Singleton's instance
Emergence of a "global state", which makes debug a lot harder
Inability to mock static fields and methods through "conventional" mocking
The fact that a reference to Singleton can be easily obtained in any part of the application leads to a total mess (people stop thinking about dependency graph)
Singletons tend to breed: you introduce one, then another one, then you find yourself with 10 singletons which hold app's state in a "global cloud state"
Note that what you're trying to do is against Android guidelines - if the user taps on "back" button, then he should find the previous Activity or Fragment in the exact same state it had the last time the user saw it, without any additions (unless you explicitly don't want to save it in the back-stack).
If you still want to do it, then I could suggest several options:
Use SharedPreferences and store the value there. Get the value in each Activity and diplay it in onResume()
Use startActivityForResult() call in order to start new Activities and pass the value back in the result. Note that by default press on "back" cancels the action, therefore you'll have to override onBackPressed() method.
Override onBackPressed() method in Activity in such a way that it starts another Activity (instead of just popping the back-stack) and pass the value in the Intent that you use. You might want to use FLAG_ACTIVITY_CLEAR_TOP in this case.
Use some event bus that supports "sticky" events. When user inputs the text you post a sticky event to event bus. In onResume() of Activity you check whether event of this type exists and if it is - you update UI.
Once again - the fact that you CAN do what you want doesn't mean it SHOULD be done.
Simply set into onResume() method of your class A, a call to the Singleton class instance you want to save (or istance of Application class which is the same)
LocalBroadcastManager is not a reliable option. It assumes the bottom activity to still be alive, which might not be the case. While you use B, A might be collected to free resources. Also, you should unregister LocalBroadcastManager receivers onResume(). So, no.
Singletons with static fields are generally to be avoided. It’s not worrying for a single string of text, but singleton fields are easily forgotten and can lead to memory leaks. Better to avoid this pattern if possible, and in your case it is.
Possible options.
If the field is something that must persist, e.g. user editing his username, use SharedPreferences or another storing solution (saving to a server, saving to cache, saving to device SQLite database).
If the field is temporary and is the result of activity B, you can start activity B with startActivityForResult() and then send the result back to activity A through an Intent.
For your problem the simpliest solution - store your value in Application class. Any activity can access it and read/write values.
Cons is that if you accidentally store static reference to activity, it will cause memory leak.
You may try using EventBus for horizontal communication: Activity-> Service, Service -> Fragment, etc.
It has static instance by default plus you can subscribe/unsubscribe to it in onPause and onResume methods.
Another advantage is a STICKY EVENTS - you can post event from Service and it will wait until something handle it - Activity will receive this event when it is ready - after onResume().
I have 2 UI Activities (w/ Fragments) as well as a SettingsActivity (derives from PreferenceActivity) in my first-ever android app. It 'works' but I want to make sure I'm doing the idiomatic right thing as the user flows between views and the preferences.
Specifically - when I change my 'location' preference (when the user is in the SettingsActivity) to a different zip code, I would like the app to fetch some background data from a service. But then I need to refresh the UI in whichever View preceded the settings change (taking the new data into account).
So is it 'the right thing to do' - to do work (fetch the background data) in the PreferenceActivity's onPreferenceChangeListener() when the zip code change, and then have the preceding UI activity consume the data (that's now updated in a singleton abstraction of the data source) in its onStart() method when the user returns to it? Or is there a better way (e.g., to somehow capture the 'preference changed' in the UI Activities, and do all the work there, rather than in the PreferenceActivity).
Hope this isn't too abstract, I'd share code, but there's so much cleanup I'd have to do that I'm hoping this to suffice.
Thanks in advance.
I would put the functionality into a seperate class MyCurrentZipcode that
can be used by every activity that consumes the zip-code dependendant data and
gets informed, if the zipcode changes.
does the background download of the zip data.
You can create a method MyCurrentZipcode.addOnZipcodeChanged() where the consuming activities can register to get informed about changes.
I have an Android Application with multiple activities where each activity corresponds to a different screen/view and they are actually somehow sequential (in the sense that one internal activity launches the other after a certain event).
In that context, I want to be able to receive an external Intent that sets some configurations from my Application (configurations potentially in common for all Activities) but I do not want that to change the current view.
How should I do that? Is there a way to bind a broadcast receiver to the application iteself rather than a specific activity?
Aren't broadcast receivers attached directly to the activities? Looking at the [Android tutorial on intents] (http://developer.android.com/guide/components/intents-filters.html#npex) all the intent filters are attached to different activities in the Manifest file. However looking at this [tutorial] (http://www.vogella.com/articles/AndroidBroadcastReceiver/article.html), I see that it is possible to declare Receiver on the manifest directly under the application.
If I attach it directly to a class under the Application (on the manifest), does that mean that the code will be executed and the frontground activity (view) will not change?
Here There may be different design pattern could be used based on your requirement.
If you want to have some persistent information in memory then you can use shared preferences for whole application.
You can have one broadcast receiver which would be intended to get some external intent containing all configuration values.
Each time , you can check the external configuration hash with internal hash ( which is stored in shared preferences ) , If you find some difference , then you can update the shared preferences .
So , better approach would be that you can have a configuration class and this class object can be saved in shared preferences.
There are several ways to save object in shared preferences.
so , Now in each activity , you can read the preferences in onResume() to apply new configuration .
If you don't want to persist the conf object in shared preferences , then you can have static object of Conf class and that can be initialized in application class.
eg-
public class BaseClass extends Application {
public static Conf conf ;
....
}
http://developer.android.com/reference/android/app/Application.html
So application class is a Base class for those who need to maintain global application state
rather than component (Activity , Service ) specific.
I have written an activity A, when users press a button, it will do MyConfig.doSomething() where MyConfig is simple class with activity A passed to it.
public class A extends PreferenceActivity {
private MyConfig mMyConfig;
/* pseudo code, when button clicked, call */
mMyConfig.doSomething();
}
In mMyConfig, it accesses SharedPreferences for some configuration. Thus, I can do this to pass the activity to mMyConfig for calling getSharedPreferences().
mMyConfig = new MyConfig ( this );
Here comes my request:
I want to do something that MyConfig.doSomething() already does, but except when users click some button to invoke it, I want to invoke it when Android Boots-Up.
I can write another class to extend BroadcastReceiver and then starts activity A by calling startActivity(A.class), and then in A, do some tricks to make mMyConfig.doSomething() happen. It works but the Application will be shown on screen when Android Boots-Up.
I want to make mMyConfig.doSomething() happen implicitly without letting users be aware of it. I suppose two possible solutions but I don't know how to do it.
Solution A:
Write a class that extends BroadcastReceiver, start a service (instead of activity A) that reads the SharedPreferences of A and create MyConfig object to do doSomething(). However, I don't if this can work if activity itself is never launched and how could I do this (read SharedPreferences from a service)?
Solution B:
Write a class that extends BroadcastReceiver, start activity A without showing it, put it to activity stack by calling startActivity(A.class) in onReceive. Is this possible?
Instead of Activity, which are meant to be visible to the user, you can make your BoardcastReceiver to start a Service instead. It is meant to perform tasks in the background without disturbing the user. The official guide is a nice place to start with.
Edited:
To access the SharedPreference of your application, simply call this line inside your service:
SharedPreferences pref = PreferenceManager.getSharedPreferences();
I have an Activity which is an OpenGL view. I also have an xml layout to use for preferences. Until now, to show the preference menu, I just brought it to front by setContentView(). And the same to get back to the OpenGL view.
But is this a case where I should give the preference menu its own Activity?
I guess this would make a few things much easier. For example, the back button would just work, opposed to now where I have to code it or it will just exits the application.
And if this is a good idea, how do I pass data both ways? I have a class that store all preferences. Can I send it to the Activity and back again? Or is the best way to store the preferences in a sqlite database and then use it for passing data?
I find it easier to segregate menus and such into separate activities (unless you are using dialogs etc..) As far as storing data you can do it a number of ways:
Database
StoredPreferences
Intent extras with putExtra/Bundle
Creating an application subclass and storing preferences there
Each have their merit. 4 is pretty easy as you just have to state the application class name in your manifest then call: MyAppClass app = (MyAppClass)getApplicationContext(); and you can then use any variables in MyAppClass via app. 2 is also straightforward.
You already pointed out the main difference: history management.
You can pass data to Activity via Intents putExtra()/getExtra():
Create an Intend and add custom data via Intent.putExtra(..)
Start the new Activity: startActivityForResult(intent).
Inside new Activity you can get extra data with intent.getXyzExtra() (where xyz is type).
When new Activity is done just call setResult(int, resultIntent). Again you can add extra data as described in 1.
Call finish() to end the activity.
In original Activity method onActivityResult will be called. Again extract data from Intent as described in 3.