1

I'm in the process of porting an iOS project to Android. The iOS project makes use of several key singletons to provide global resources for the application, such as a database and an set of web services. Many of these singletons use resources such as files, network, or shared preferences.

I'm running into a lot of problems trying to port this to Android, because I often require a "Context" object when I am writing singleton code that is not part of an Activity. I can usually pass in a Context when I first construct the singleton, but I have heard that I should not store this as a member variable because it could cause memory leaks.

What is the best way to deal with this issue?

Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
Flarosa
  • 1,287
  • 1
  • 13
  • 27
  • Get the context from `getApplicationContext()`. In general, you should not be requiring a static reference to context though. – Anindya Dutta Nov 01 '17 at 17:00
  • It's very inconvenient to pass Context to every method that needs it, particularly when methods are calling other methods in a deep tree. What exactly does Context do that's so critical, anyway? Why do I need a Context object to open a file or read shared preferences? – Flarosa Nov 01 '17 at 17:11
  • True. A better model would be to use the singleton object in your classes which have access to Context, instead of passing new Activity contexts to the singleton each time. – Anindya Dutta Nov 01 '17 at 17:13
  • Possible duplicate of [Android Singleton with Global Context](https://stackoverflow.com/questions/14057273/android-singleton-with-global-context) – Viktor Yakunin Nov 01 '17 at 17:19
  • There are two good solutions you can do: the best one is to use Dependency Injection (e.g. Dagger 2) and the other one is to create your own implementation of Application class and lazily init your resources (DB, SP, etc) – Viktor Yakunin Nov 01 '17 at 17:28
  • Maybe inconvenient but it ensures that context will not be kept after method's scope (unless kept explicitly). And I dont understand what methods having to call other methods have to do with any implementation of accessing Context. You can pass along context, just make sure it does not get held by any of them. – Kashan Danish Nov 01 '17 at 17:30
  • I guess my source of frustration is that I have never run into this problem in iOS development, which is just as rich and complex as Android, if not moreso. Seems like if the iOS engineers could figure out how to avoid this problem, the Google engineers could have also. Well, can't change Android I guess, so I'll live with what I've got. Thanks for all the feedback. – Flarosa Nov 02 '17 at 17:17

2 Answers2

1

I can usually pass in a Context when I first construct the singleton, but I have heard that I should not store this as a member variable because it could cause memory leaks.

Use the Application object, which you get from getApplication() (on Activity) or getApplicationContext() (on any other Context). This is itself a singleton, and so it is pre-leaked and cannot be leaked further.

Note, though that the Application object is not a suitable Context for anything closely tied to the UI (e.g., inflating layouts); such things should not be in singletons in general.

See Dave Smith's epic blog post on the roles of different types of Context for more details.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Thanks, I'm using the application context when I have to store it, otherwise I'm just passing context to individual methods where I need it. – Flarosa Nov 02 '17 at 17:15
1

If you HAVE to keep singletons in app, you can pass context to method of singleton which uses context

In MySingleton.java

public class MySingleton {
   //private instance, private constructor and getInstance() goes here

   public void doSomething(Context context, int foo, int bar) {
       //Use context
   }
}

Calling from Activity/Service

MySingleton.getInstance().doSomething(this, 1, 2);

Calling from Fragments

MySingleton.getInstance().doSomething(getActivity, 1, 2);

If you can afford changing architecture, please steer clear of singletons. You can use dependency injection as an alternative. This is how you do it.

Kashan Danish
  • 155
  • 2
  • 9
  • never pass a reference of Activity to singleton, it causes memory leaks! In Android you have singleton and it's Application Context. – Viktor Yakunin Nov 01 '17 at 17:17
  • In Android it's absolutely ok to have singletons, the JVM there is not so good in GC and resources are limited, so it's better to have singletons than create/recycle objects. – Viktor Yakunin Nov 01 '17 at 17:21
  • Of course you can pass activity context to a singleton *method*. As long as you can guarantee that the singleton will not hold a reference to that activity context after that method finishes executing. – Eugen Pechanec Nov 01 '17 at 17:21
  • @EugenPechanec can you guarantee that activity would not be re-created while your singleton method running? and of course we are talking about resource initialization like DB, SP, etc... – Viktor Yakunin Nov 01 '17 at 17:30
  • @ViktorYakunin As long as the method is executed on the main thread, then yes. Plus if the singleton doesn't hold the reference after the method is done, the reference would be garbage collected later anyway (so no *long term* memory leak). In any case if you're *not* working with UI you could just say `final Context context = activity.getApplicationContext()` at the method beginning and work with that. – Eugen Pechanec Nov 01 '17 at 17:34
  • @EugenPechanec ok, here we have specific type of work, so we can discuss this question concrete. For example he needs to create db instance so you need to pass a context to create SQLiteOpenHelper object, it will hold a reference to context until db instance is alive. – Viktor Yakunin Nov 01 '17 at 17:41
  • @ViktorYakunin Yes, let's say we have a `MyDbHelper extends SQLiteOpenHelper`. The constructor takes a context. In that I'd call `super(context.getApplicationContext(),...)`. That way I can pass any context while instantiating `MyDbHelper` but it will *make sure* the application context (which is singleton) will be used and stored by the super class. – Eugen Pechanec Nov 01 '17 at 17:46