I'm not sure if I actually need this right now but if my app ever expands I could see the possibility. I basically have a wrapper around SharedPreferences that pulls a few values out of SharedPreferences and bundles them into an object. It also takes an object and uses it to update preferences. I wanted to make it thread-safe, but I wanted to try it with a Semaphore. My SharedPreferences wrapper will get a reference to the class below from getSyncedPrefManager(). It will then call aquireLock() followed by getPref(), do its work and then call releaseLock(). Does this look like something that would work or am I way off base?
public class SyncedPreferenceManager {
private final static SyncedPreferenceManager me =
new SyncedPreferenceManager();
private SharedPreferences prefs;
private static Semaphore mutex;
public static SyncedPreferenceManager getSyncedPrefManager(){
return me;
}
private SyncedPreferenceManager(){
mutex = new Semaphore(1, true);
}
public SharedPreferences getPref(Context caller){
if(prefs == null)
prefs = PreferenceManager.getDefaultSharedPreferences(caller);
return prefs;
}
public boolean aquireLock(){
try {
mutex.acquire();
} catch (InterruptedException e) {
return false;
}
return true;
}
public boolean releaseLock(){
mutex.release();
return true;
}
}
You might not like this answer.
You are not using the right system here. SharedPreferences is for storing simple preferences. Just because you can do this does not mean you should. You're basically trying to make SharedPreferences into something its not. You can add all this fancy locking but it won't stop someone from coming in underneath this later and accidentally blowing it up.
If you find yourself needing these feature in earnest, you should look at just using sqlite directly. There is little doubt you could add synchronization to SharedPreferences (and I am sure it is safe to some degree as it is already designed with a transaction/commit model) but it seems to me like reinventing the wheel.
Syncronized sections shall be enough for this purpose. Usually there are no performance concerns in saving / loading values from preferences (there are not that many of values in there).
And I also doubt, that you need it at all. Preferences are usually loaded on activity startup or saved on pause ( which is kind of songle threaded anyway, only one of activities in your application is being started or stopped at the time )
In my apöications preferecens are read eagerly, and saved only when dedicated settings activity is paused. I do not need any mutual exclusionin this case.
I also developed small wrapper library, which allows easy marshalling / unmarshalling preferences into object properties:
https://github.com/ko5tik/andject
You can always make your changes in the SharedPreferences.Editor and use apply() to apply the changes atomically.
editor.apply() is available from API level 9.
Documentation here: http://developer.android.com/reference/android/content/SharedPreferences.Editor.html#apply()
Related
I'm using an ASyncTask in my app to get some data (a short URL) via a REST API from a web service (Bitly).
When the ASyncTask completes I want to pass the result back to my MainActivity.
Getting the data back to the MainActivity is acheievd by using the onPostExecute method of the AsyncTask.
I've read and read and read about how to do this and there seem to be two general approaches.
Originally I was using a 'WeakReference' approach whereby at the start of the AsyncTask class you create a weak reference to your MainActivity as follows:
private class getShortURL extends AsyncTask<String, Void, String> {
private WeakReference<MainActivity> mainActivityWeakReference;
myASyncTask(MainActivity activity) {
mainActivityWeakReference = new WeakReference<>(activity);
}
{etc etc}
With this approach your AsyncTask class sits outside of your MainActivity class and so a lot of things need to be referenced via the weak reference.
This worked fine (except I suspected - possibly incorrectly - that this weak reference may have been the cause of occassional NPEs), but I then found another way of doing things.
This second approach involved moving the ASyncTask class inside of the MainActivity class.
This way I was able to access everything that was accessible in the MainActivity class directly, inlcuding UI elements and methods defined in the MainActivity. It also means that I can access resources such as strings etc and can generate toasts to advise the user what is happening.
In this case the whole of the WeakReference code above can be removed and the AsyncTask class can be made private.
I am also then able to do things like this directly in onPostExecute or to keep this in a method within the MainActivity that I can call directly from onPostExecute:
shorten_progress_bar.setIndeterminate(false);
shorten_progress_bar.setVisibility(View.INVISIBLE);
if (!shortURL.equals("")) {
// Set the link URL to the new short URL
short_link_url.setText(shortURL);
} else {
CommonFuncs.showMessage(getApplicationContext(), getString(R.string.unable_to_shorten_link));
short_link_url.setHint(R.string.unable_to_shorten_link);
}
(note that CommonFuncs.showMessage() is my own wrapper around the toast function to make it easier to call).
BUT, Android Studio then gives a warning that "the AsyncTask class should be static or leaks might occur".
If I make the method static I then get a warning that the method from the MainActivity that I want to call from onPostExecute cannot be called as it is non-static.
If I make that method from MainActivity a static method, then it cannot access string resources and any other methods that are non static - and down the rabbit hole I go!
The same is true, as you would expect, if I just move the code from the method in the MainActivity into the onPostExecute method.
So...
Is having an AsyncTask as a non-static method really a bad thing? (My
app seems to work fine with this warning in AS, but I obviously don't
want to be creating a memory leak in my app.
Is the WeakReference appraoch actually a more correct and safer approach?
If I use the WeakReference approach, how can I create things like toasts which need to be run on the UI thread and access string
resources etc from the MainActivity?
I read somewhere about creating an interface but got a bit lost and couldn't find that again. Also would this not have the same kind of reliance on the MainActivity that a WeakReference does and is that a bad thing?
I'm really looking for best practice guidance on how to get some data back to the MainActivity and the UI thread from an AsyncTask that is safe and doesn't risk memory leaks.
Is having an AsyncTask as a non-static method really a bad thing? (My app seems to work fine with this warning in AS, but I obviously don't want to be creating a memory leak in my app.
Yes, your Views and your Context will leak.
Enough rotations and your app will crash.
Is the WeakReference approach actually a more correct and safer approach?
It's lipstick on a dead pig, WeakReference in this scenario is more-so a hack than a solution, definitely not the correct solution.
What you're looking for is a form of event bus from something that outlives the Activity.
You can use either retained fragments* or Android Architecture Component ViewModel for that.
And you'll probably need to introduce Observer pattern (but not necessarily LiveData).
If I use the WeakReference approach, how can I create things like toasts which need to be run on the UI thread and access string resources etc from the MainActivity?
Don't run that sort of thing in doInBackground().
I'm really looking for best practice guidance on how to get some data back to the MainActivity and the UI thread from an AsyncTask that is safe and doesn't risk memory leaks.
The simplest way to do that would be to use this library (or write something that does the same thing yourself, up to you), put the EventEmitter into a ViewModel, then subscribe/unsubscribe to this EventEmitter inside your Activity.
public class MyViewModel: ViewModel() {
private final EventEmitter<String> testFullUrlReachableEmitter = new EventEmitter<>();
public final EventSource<String> getTestFullUrlReachable() {
return testFullUrlReachableEmitter;
}
public void checkReachable() {
new testFullURLreachable().execute()
}
private class testFullURLreachable extends AsyncTask<Void, Void, String> {
...
#Override
public void onPostExecute(String result) {
testFullUrlReachableEmitter.emit(result);
}
}
}
And in your Activity/Fragment
private MyViewModel viewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
// ...
}
private EventSource.NotificationToken subscription;
#Override
protected void onStart() {
super.onStart();
subscription = viewModel.getTestFullUrlReachable().startListening((result) -> {
// do `onPostExecute` things here
});
}
#Override
protected void onStop() {
super.onStop();
if(subscription != null) {
subscription.stopListening();
subscription = null;
}
}
Is it a good idea/practice to put static shared preferences editor in a utility class so I can call it whenever needed? The method in the utility class would look like this:
public static SharedPreferences.Editor editor (Context context){
final SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
return sharedPrefs.edit();
}
and use it like this in different classes:
Utility.editor(mContext).putBoolean(categoryId, true);
Utility.editor(mContext).apply();
At least I would't say it's a bad idea.
But here is an even better idea: abstract away the Android specific details and create a clean, readable interface for storage access that fits your domain.
e.g:
interface UserSettings {
void setAutoReloadEnabled(boolean enabled);
boolean isAutoReloadEnabled();
...
}
and then implement it using SharedPreferences
class SharedPreferencesUserSettings implements UserSettings {
final SharedPreferences sharedPrefs;
public SharedPreferencesUserSettings(Context ctx) {
sharedPrefs = ...;
}
#Override void setAutoReloadEnabled(boolean enabled) {
sharedPrefs.editor().putBoolean("...", enabled).commit();
}
...
}
This gives you more readable code and you can actually provide a stub/mock implementation in your tests! If the API of SharedPreferences should change (or when you want to move from using commit to apply or vice-versa, or changing the Tags you used for the preferences) you only have to change it in one File, not everywhere in your code.
But there is more: if you should later decide that SharedPreferences were actually a bad choice, you can switch the implementation to use e.g. a . SQLite Database or ObjectBox instead. Again, without changing the rest of the code.
Needless to say that this might be overkill (aka over-engineering) in certain situations, but in bigger projects this pays out pretty fast.
Its not necessarily a bad idea and it will clean up the code. But it will slow your app down.
Not by a noticeable amount but nonetheless - if time is an issue in your project do not do this. If not, then go ahead.
Coming from a non-Java background, I find myself writing a lot of View classes with extensive functionality (in an effort to be portable), that don't necessarily fit nicely into the Android FW setup as far as maintenance - for example, I might have a widget that does something on a interval that I want to stop and clean up when an Activity is paused/stopped/destroyed. Generally I can manage this by just calling a public method on the widget from the containing Activity, but A) sometimes this gets pretty deep, and having to create a public method to access a child in every parent can get ugly, and B) requires extra (uneeded?) attention.
I'm considering using an approach like a global delegate to manage this kind of thing, but have read a lot of warnings about this sort of approach - would something like the class that follows have any inherent flaws that I might be missing?
import java.util.HashMap;
import java.util.HashSet;
public class ActiveRegistry {
private static final ActiveRegistry instance = new ActiveRegistry();
public static ActiveRegistry getInstance(){
return instance;
}
private HashMap<String, HashSet<Runnable>> registry = new HashMap<String, HashSet<Runnable>>();
private ActiveRegistry(){
}
public void register(String key, Runnable runnable){
if(!registry.containsKey(key)){
HashSet<Runnable> list = new HashSet<Runnable>();
registry.put(key, list);
}
HashSet<Runnable> list = registry.get(key);
list.add(runnable);
}
public void execute(String key){
if(registry.containsKey(key)){
HashSet<Runnable> list = registry.get(key);
for(Runnable runnable : list){
runnable.run();
}
}
}
}
Use might be something like...
A View has something that needs to be cleaned up. On instantiation, register it... ActiveRegistry.getInstance().register("paused", someRunnableThatCleansUpStuff)
Extend Activity so that onPause calls ActiveRegistry.getInstance().execute("paused");
You are doing way more work than you need to. Using Fragments (from the support package, if you want to ensure backwards compatibility with older versions of android), will make your life a whole lot easier. Each fragment is embedded in an activity and has a lifecycle that is directly linked with its host activity's lifecycle. Using them should significantly reduce the complexity of your code, as most of what you are currently worrying about will be managed by the system instead.
I'm creating a music player for Android and it's mostly working. The problem is that when I turn the device horizontally I lose all the variables from the Activity (which makes sense because it is destroyed and re-created).
I've tried using bundles to store the state of the player with onSaveInstanceState & onRestoreInstanceState but I still can't access the media player. Is there a way to pass objects like the MediaPlayer in bundles? Should I be using a database instead?
Thanks
You should use a Service to Provides "background" audio playback capabilities, allowing the
user to switch between activities or Rotate device without stopping playback.
Check out android_packages_apps_Music which is opensource by CM on github , It use MediaPlaybackService extends Service to do this , checkout MediaPlaybackService.java
For objects you couldn't pass via a bundle, I would suggest you to use the simple SharedPreference to store objects.
Here you have a simple implementation:
public class Data {
private SharedPreferences preferences;
private int test;
public Data (Context context)
{
preferences = context.getSharedPreferences("Data", 0);
test = preferences.getInt("test", 0);
}
public int getTest()
{
return test;
}
public void setTest(int input)
{
this.test = input;
SharedPreferences.Editor editor = preferences.edit();
editor.putInt("Test", input);
editor.commit();
}
}
You have just to initialize the variable in the onCreate():
Data mydata = new Data(this);
And you can use set/get with mydata to store/retrieve your persistent data.
Edit: It is maybe not suitable for MediaPlayer objects, but for other classical types (int, string, boolean...).
Both of the methods below would allow you to keep your mediaplayer object through the rotation, but neither use bundles.
You could persist your media player by using onRetainNonConfigurationInstance() to save the variable and getLastNonConfigurationInstance() to retrieve it after the rotation, but this method isn't necessarily the best as it is not always called
-See this SO post for more info https://stackoverflow.com/a/3916068/655822
Or you could persist your media player by extending your application class and storing it in there
below info copied from the linked SO answer for the purpose of making this answer quicker to read
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.
-See this SO post for more info on that https://stackoverflow.com/a/4208947/655822
Another way would be to :
In your AndroidManifest.xml, find your entry for your activity and add the following attribute and value:
android:configChanges="orientation|screenSize"
This will stop your activity from being destroyed and recreated on orientation.
Wondering if there is a way to change the object on the heap that other objects are referencing.
What I am specifically trying to do is manage my transient configuration. What I am doing is loading "bound" configuration from JAXB or JPA. I have a manager which maintains some threads to check if those config stores change. If they do, I wish to load the configuration from the store again (creates a new instance of the config) and REPLACE the "stale" configuration instance with the new one on the heap, so any objects referencing the configuration data will get the latest.
I understand I'll likely be running into a nightmare with having to deal with the hierarchical object references - but I simply want to learn about the various potential approaches before I decide to simply document not to create local reference and always call from the config manager if you expect the latest =)
Any ideas how to do this? I'm not too familiar with AOP...but from what I know about it...I am thinking this might provide an avenue to achieve this.
Any other ideas are welcome, of course =)
Steve
I don't know anything about JAXB or JPA, but here's what I'd do. Give your various objects a reference to a wrapper for the config. Then you can update the config (in a synchronized manner) without needing to change those references:
interface Config { String getSomeProperty(); }
class ConcreteConfig implements Config{
public String getSomeProperty() {
return "some value";
}
}
class ConfigWrapper implements Config {
private Config backing;
private ReadWriteLock lock = new ReentrantReadWriteLock();
public void setBacking(ConfigBacking backing) {
try {
lock.writeLock().lock();
this.backing = backing;
} finally {
lock.writeLock().unlock();
}
}
#Override
public String getSomeProperty() {
try {
lock.readLock().lock();
return backing.getSomeProperty();
} finally {
lock.readLock().unlock();
}
}
}
Then you would only distribute the instance of ConfigWrapper, and can freely reassign the backing object whenever you want.
You can check out the Spring framework. If my memory serves, it supports something like that (maybe this?).