Android Singleton with Global Context - android

Per the Android Documentation it states:
There is normally no need to subclass Application. In most situation,
static singletons can provide the same functionality in a more modular
way. If your singleton needs a global context (for example to register
broadcast receivers), the function to retrieve it can be given a
Context which internally uses Context.getApplicationContext() when
first constructing the singleton.
How do I go about creating a static singleton that has global context so that it survives the running activity changing in my app? Is it enough to have a static context which references the getApplicationContext()?

Another edit to the question (2016)
Lately (as of 2016 and onward) what I've been doing, and would be my suggestion for any developer, is:
Just use Dagger 2. Wherever you need a Context you do:
#Inject Context context;
and that's it. While at it, inject all the other stuff that would be a singleton.
Edited/improved answer (2014)
because this answer is getting kinda-of popular, I'll improve my own answer with example code of what I've been using lately (as of Jul/2014).
Start by having the application keeping a reference to itself.
public class App extends Application {
private static App instance;
public static App get() { return instance; }
#Override
public void onCreate() {
super.onCreate();
instance = this;
}
}
then on any singleton that needs access to the context I lazy load the singles in a thread safe manner using double check synchronization as explained here https://stackoverflow.com/a/11165926/906362
private static SingletonDemo instance;
public static SingletonDemo get() {
if(instance == null) instance = getSync();
return instance;
}
private static synchronized SingletonDemo getSync() {
if(instance == null) instance = new SingletonDemo();
return instance;
}
private SingletonDemo(){
// here you can directly access the Application context calling
App.get();
}
Original answer
what the documentation is suggesting is to use a normal singleton pattern
public class SingletonDemo {
private static SingletonDemo instance = null;
private SingletonDemo() { }
public static SingletonDemo getInstance() {
if (instance == null) {
instance = new SingletonDemo ();
}
return instance;
}
}
and include inside it a method like this:
private Context context;
init(Context context){
this.context = context.getApplicationContext();
}
and remember to call this to initialise the singleton.
The difference between the Application approach and the Singleton approach and why the Singleton is better is on the documentation same functionality in a more modular way

I have such class in my application :
public class ApplicationContext {
private Context appContext;
private ApplicationContext(){}
public void init(Context context){
if(appContext == null){
appContext = context;
}
}
private Context getContext(){
return appContext;
}
public static Context get(){
return getInstance().getContext();
}
private static ApplicationContext instance;
public static ApplicationContext getInstance(){
return instance == null ?
(instance = new ApplicationContext()):
instance;
}
}
and then for example in Launch Activity initialize it :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//init
ApplicationContext.getInstance().init(getApplicationContext());
//use via ApplicationContext.get()
assert(getApplicationContext() == ApplicationContext.get());
}

Related

How to use Shared preferences in singleton of other class which not include context?

I got a singleton. In methods of this singleton i need to get access to shared preferences. How I can do that?
public class SubscriptionManager {
private static SubscriptionManager instance = null;
private SubscriptionManager() {}
private SharedPreferences mPrefs;
public static SubscriptionManager getInstance() {
if (instance == null)
instance = new SubscriptionManager();
return instance;
}
public void setFreeOpenSubscription(){
//here i need to write something like
// String type = mPrefs.getValue("type");
}
}

How to return different value for a static method which is invoked during the flow during unit testing?

I'm trying to write unit testing for the following snippet.
class ABC {
int getMyValue(final Activity activity) {
if(MyClass.getInstance(activity).getValue() == 1) return 10;
else return 20;
}
void doSomething() {
}
}
I've tried something like this to test the doSomething function.
mABC = new ABC();
public void test_doSomething() {
doReturn(20).when(mABC).getMyValue();
//validate
}
How can I test getMyValue similarly? I would like to assert when the value is 1 it's returning me 10 and in all other cases, it's returning me 20.
I'm doing this in my android application. Is there any existing framework that can help me do this?
EDIT:
MyClass looks something like this
public class MyClass {
private static Context mContext;
public static getInstance(Context context) {
mContext = context;
return new MyClass();
}
private MyClass() {}
public void getDreamValue() {
Settings.Secure.getInt(mContext.getContentResolver(), "dream_val", -1);
}
}
You might consider modifying your MyClass as follows.
public class MyClass {
private static Context mContext;
// Create a private variable that holds the instance.
private Myclass instance;
public static getInstance(Context context) {
mContext = context;
if (instance == null)
instance = new MyClass(); // Assign the instance here
return instance;
}
private MyClass() {}
public void getDreamValue() {
Settings.Secure.getInt(mContext.getContentResolver(), "dream_val", -1);
}
}
Now, as you are using Robolectric, you can set the instance value to a mock as follows in your test class.
#RunWith(RobolectricTestRunner.class)
public class ABCTest {
#Mock
MyClass mockInstance;
#Mock
Context mockContext;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
// Set the mock instance for MyClass
ReflectionHelpers.setStaticField(MyClass.class, "instance", mockInstance);
}
#Test
public void testWhen1() {
doReturn(1).when(mockInstance).getDreamValue();
Assert.assertEquals(10, new ABC().getMyValue());
}
#Test
public void testWhenNot1() {
doReturn(2).when(mockInstance).getDreamValue();
Assert.assertEquals(20, new ABC().getMyValue());
}
#After
public void tearDown() {
// Set the instance to null again to enable further tests to run
ReflectionHelpers.setStaticField(MyClass.class, "instance", null);
}
}
I hope that helps.
Note: It looks like you are trying to provide a singleton instance of MyClass. Hence, you really should not create a new instance of MyClass in the getInstance function. I avoided creating a new instance each time, using the null check in my code.

Dagger: proper way to define injectable class that takes context in its constructor

I want to use dagger (dagger v1 by Square) to create a singleton class whose constructor requires context as an argument. I then want to inject this singleton class into my MainActivity. What are the proper steps to define this?
I tried doing this:
SingletonClass:
#Module(
injects = {MainActivity.class}
)
#Singleton
public class SingletonClass {
...
#Inject
SingletonClass(Context ctx) {}
}
to which I receive the error:
no injectable members on android.content.Context
I understand that SingletonClass is supposed to receive it's context from somewhere in order to be injectable, but since I'm not "calling" it anymore in the traditional sense, but rather I'm injecting it like so in the MainActivity at the class-level:
#Inject SingletonClass singletonClass;
how am I supposed to pass it the context?
Here are additional files I created for dagger (two of which I saw used in official examples):
AndroidModule:
#Module(library = true, injects = MainActivity.class)
public class AndroidModule {
private final Context context;
public AndroidModule(Context context) {
this.context = context;
}
/**
* Allow the application context to be injected but require that it be
* annotated with to explicitly
* differentiate it from an activity context.
*/
#Provides
#Singleton
#ForApplication
Context provideApplicationContext() {
return context;
}
}
App.class (to extend my application):
public class App extends Application {
private static final String TAG = App.class.getSimpleName();
private static App instance;
public MainActivity mainActivity;
private static Context context;
private ObjectGraph objectGraph;
public void onCreate() {
super.onCreate();
instance = this;
context = getApplicationContext();
objectGraph = ObjectGraph.create(getModules().toArray());
}
public static App getInstance() {
return instance;
}
public static Context getContext() { return context; }
protected List<Object> getModules() {
return Arrays.asList(new AndroidModule(this), new App());
}
public void inject(Object object) {
objectGraph.inject(object);
}
}
ForApplication class:
import java.lang.annotation.Retention;
import javax.inject.Qualifier;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#Qualifier #Retention(RUNTIME)
public #interface ForApplication {
}
You need to add the #ForApplication qualifier to your Context parameter in the SingletonClass constructor.
Dagger is now looking for a Context to inject, but only has a #ForApplication Context, which is a mismatch.
#Singleton
public class SingletonClass {
#Inject
SingletonClass(#ForApplication Context ctx) {
}
}
Now you can also get rid of the library = true line in your AndroidModule, which you've probably added because Dagger warned you that #ForApplication Context was unused (Don't ignore these warnings!).
Also, and that might just be a copy-paste error, this SingletonClass should not have an #Module annotation.

Starting an Android Activity from a static method

I want to start an activity from a static java method on an android device.
I do not have any context or anything passed as parameter to the static function.
For starting the activity I must call "startActivity" with the current running method as "this" pointer. So is there a way to get the current running activity?
You can access only static variables/objects inside static method.
So You need to Implement this way
public class MainActivity extends Activity {
private static Context mContext;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
}
public static void goToLoginActivity() {
Intent login = new Intent(mContext, LoginActivity.class);
mContext.startActivity(login);
}
}
NOTE : But this is not the proper way to do so, this may cause window leak issue.
Better approach is pass activity/context object as parameter like this.
public static void goToLoginActivity(Context mContext) {
Intent login = new Intent(mContext, LoginActivity.class);
mContext.startActivity(login);
}
Create a Class in your app extending class Application, define a static context and initialise this with your application context. You can expose a static method from this class for accessing defined static reference. Thats it.
class MyApp extends Application{
private static Context mContext;
public void onCreate(){
mContext = this.getApplicationContext();
}
public static Context getAppContext(){
return mContext;
}
}
Now you can use this static method for accessing context anywhere in your app.

Access Android sharedPreferences from surfaceview

The code to access sharedpreferences is
SharedPreferences settings = getSharedPreferences("MySettings", 0);
float X = settings.getFloat("myFloat", 0);
But this only works from within an activity.
How do I access it from else where?
Would a singleton be a good idea? And how would I set up and access this singleton.
I only want to store 4 floats, and this would have been great! But nothing is straight forward :(
Isn't there a getContext() method for SurfaceView?
getContext().getSharedPreference();
If that didn't work, you can load those vars in a singleton on start up, since there are only 4 of them.
public class ClassicSingleton {
public float float1;
public float float2;
public float float3;
public float float4;
private static ClassicSingleton instance = null;
protected ClassicSingleton() {
// Exists only to defeat instantiation.
}
public static ClassicSingleton getInstance() {
if(instance == null) {
instance = new ClassicSingleton();
}
return instance;
}
}

Categories

Resources