I initiate a singleton instance in Application.onCreate, this instance has a member mApplicationContext which is initiated by getApplicationContext(), and this is the only place mApplicationContext be assigned value. From the crash log, mApplicationContext becomes null in certain scenarios, my question is in which this would happen?
public class ClassicSingleton {
private static ClassicSingleton instance = null;
private Context mApplicationContext = null;
private ClassicSingleton() {
}
public static ClassicSingleton getInstance() {
if(instance == null) {
instance = new ClassicSingleton();
}
return instance;
}
public void initiate(Context context){
this.mApplicationContext = context;
}
}
public class MyApplication extends Application{
#Override
public void onCreate()
{
super.onCreate();
ClassicSingleton.getInstance().initiate(getApplicationContext());
}
}
I find similar question here Android static object lifecycle, but it didn't answer my question.
Since you are writing a library, don't trust the caller to get it right. Check!
i.e:
public void initiate(Context context){
if (context == null) {
throw new Error("Attempt to set null context");
}
if (mApplicationContext != null) {
throw new Error("Why are you setting context twice?");
}
this.mApplicationContext = context.getApplicationContext();
}
Note the call to getApplicationContext ensures that you don't save an Activity context by mistake. An alternative would be to throw if context != context.getApplicationContext(), but that's probably overkill.
This doesn't fix your bug, but it will help you find it quickly.
Oh-- and you can probably find something better to throw than Error
Even better:
public static ClassicSingleton getInstance() {
if(instance == null) {
throw new Error("you forgot to initiate ClassicSingleton!");
}
return instance;
}
public static void initiate(Context context){
if (context == null) {
throw new Error("Attempt to set null context");
}
if (instance == null) {
instance = new ClassicSingleton();
}else{
// optional
throw new Error("Why are you initializing ClassicSingleton twice?");
}
instance.mApplicationContext = context.getApplicationContext();
}
Related
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.
Android TV app in strict mode initialise my AAR library like this:
public class MainApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
LibraryApplication.init(this);
}}
The library
public class LibraryApplication extends Application {
#SuppressLint("StaticFieldLeak")
private static LibraryApplication instance;
#SuppressWarnings("unused")
public static void init(Application application) {
if (instance == null) {
instance = new LibraryApplication(application);
return;
}
throw new RuntimeException("Library already initialized.");
}
private final LibraryActivityHandler activityHandler;
public LibraryApplication(final Application application) {
this.activityHandler = new LibraryActivityHandler(this);
}}
LibraryActivityHandler
class LibraryActivityHandler implements Application.ActivityLifecycleCallbacks {
private Activity currentActivity;
#Override
public void onActivityResumed(Activity activity) {
assert activity != null;
this.currentActivity = activity;
//Just to show you that activity is really not null
currentActivity.getLayoutInflater();
Log.debug("activity set");
}
boolean isActivityNull() {
Log.debug("is null: " + (currentActivity == null));
return currentActivity == null;
}
Question: In the log I can see "activity set" and later I can see "is null: true". Can someone please explain to me how current Activity might become null ?
After over 10 years with Android and Java,
I am admitting that I don't have much ideas about it.
edit: The app is also using Dagger library, and the strict mode is not causing the issue.
The issue is caused by multi processes application.
I want to use the method sendEvent(), but how to get the ReactContext reactContext?
private void sendEvent(ReactContext reactContext,
String eventName,
#Nullable WritableMap params) {reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);}
There is not need to use a static reference to ReactInstanceManager; you can obtain it by calling getReactNativeHost().getReactInstanceManager().
I solved this tentatively by saving a static reference to the ReactInstanceManager on the MainActivity class, and adding a public static getContext() method. It doesn't feel right, but it seems to work.
public final class MainActivity extends ReactActivity {
private static ReactInstanceManager sReactInstanceManager = null;
/* [...] */
#Override
protected ReactInstanceManager createReactInstanceManager() {
ReactInstanceManager manager = super.createReactInstanceManager();
sReactInstanceManager = manager;
return manager;
}
public static ReactContext getContext() {
if (sReactInstanceManager == null){
// This doesn't seem to happen ...
throw new IllegalStateException("Instance manager not available");
}
final ReactContext context = sReactInstanceManager.getCurrentReactContext();
if (context == null){
// This really shouldn't happen ...
throw new IllegalStateException("React context not available");
}
return context;
}
/* [...] */
}
createReactInstanceManager() is called by ReactActivity's onCreate() method, so I presume it's safe to assume that sReactInstanceManager will point to the correct instance, but don't take my word for it.
I'm trying to trace AOSP code from the grepcode site.
When I call getSystemService(Context.WIFI_P2P_SERVICE), it gets to the following code:
#Override public Object getSystemService(String name) {
if (getBaseContext() == null) {
throw new IllegalStateException(
"System services not available to Activities before onCreate()");
}
if (WINDOW_SERVICE.equals(name)) {
return mWindowManager;
} else if (SEARCH_SERVICE.equals(name)) {
ensureSearchManager();
return mSearchManager;
}
return super.getSystemService(name);
}
And since WIFI_P2P_SERVICE declared as public static final String WIFI_P2P_SERVICE = "wifip2p";, if will not fall in one of the conditions and will go to the super.getSystemService(name);
Activity extends ContextThemeWrapper, the code there is:
#Override public Object getSystemService(String name) {
if (LAYOUT_INFLATER_SERVICE.equals(name)) {
if (mInflater == null) {
mInflater = LayoutInflater.from(mBase).cloneInContext(this);
}
return mInflater;
}
return mBase.getSystemService(name);
}
Here also, the required service name will not match, mBase is an instance of Context so the code in Context is:
public abstract Object getSystemService(String name);
which means that classes which extends from it must handle that functionality.
Well, Where my request is being treated?
As far as i know the implementation code of Context is under the package android.app with class name ContextImpl
Here is getSystemService from that class -
#Override
public Object getSystemService(String name) {
ServiceFetcher fetcher = SYSTEM_SERVICE_MAP.get(name);
return fetcher == null ? null : fetcher.getService(this);
}
Edit -
The entry point for WIFI_P2P_SERVICE -
registerService(WIFI_P2P_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
IBinder b = ServiceManager.getService(WIFI_P2P_SERVICE);
IWifiP2pManager service = IWifiP2pManager.Stub.asInterface(b);
return new WifiP2pManager(service);
}});
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());
}