Android singleton becomes null - java

I have a singleton class which I first instantiate in 1 activity, then use it in 2 other activities. When the 2nd activity loads, the instance becomes null, where is was not null before.
Here is the code for the singleton class:
public class RestaurantMenuHolder{
private static RestaurantMenuHolder mInstance = null;
public static ArrayList<RestaurantMenuObject> restaurantMenu;
public static RestaurantMenuHolder getInstance(ArrayList<RestaurantMenuObject> restaurantMenu){
if(mInstance == null){
mInstance = new RestaurantMenuHolder(restaurantMenu);
}
return mInstance;
}
public static RestaurantMenuHolder getInstance(){
return mInstance;
}
private RestaurantMenuHolder(ArrayList<RestaurantMenuObject> restaurantMenu){
this.restaurantMenu = restaurantMenu;
}
public static int getCount(){
return restaurantMenu.size();
}
}
I have tried adding synchronized to the getInstance methods, the constructor and getCount, but I still get null when the second activity loads. Does anyone know why or how the singleton would suddenly become null?
I can provide code for the activities if required.
EDIT: More Code
When I initialize the singleton: I run holder = RestaurantMenuHolder.getInstance(restaurantMenu); where restaurantMenu is an arrayList. I initialize this to store data. This is verified to work, the singleton is not null at this point.
In the first activity that I use the singleton, I run RestaurantMenuHolder menuHolder = RestaurantMenuHolder.getInstance(); in onCreateView for the fragment. I use this instance to retrieve the data previously stored. The singleton is also verified to work here.
When the second activity is started, the singleton becomes null, but not immediately. I run menuHolder = RestaurantMenuHolder.getInstance(); again in the second activity and retrieve some more data, all of which is valid.
The problem appears to occur in the ImageAdapter. I use a slidingMenu with an ImageAdapter for both activities. The singleton works for the first activity and is not null, but becomes null when I try to use it again in the second activity.
Here is code from the ImageAdapter:
public class ImageAdapter extends BaseAdapter{
private static LayoutInflater inflater = null;
private ViewHolder holder;
private String id;
private RestaurantMenuHolder menuHolder;
private RestaurantLocationHolder locationHolder;
private Context context;
private String[] sliding_list = {"Home", "Skip to Menu", "Location & Hours", "About Us"};
Typeface tf;
public ImageAdapter(Context context, String id){
this.context = context;
inflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.id = id;
tf = Typeface.createFromAsset(context.getAssets(),
"Roboto-Light.ttf");
menuHolder = RestaurantMenuHolder.getInstance();
locationHolder = RestaurantLocationHolder.getInstance(context);
MenuUtilities.setImageHash();
}
#Override
public int getCount() {
if(id == "menu"){
Log.d((menuHolder == null) + "", "MENUHOLDER NULL");
return menuHolder.getCount();
} else if (id == "location") {
return locationHolder.getCount();
} else {
return sliding_list.length;
}
}
... more code
When the second activity is started, return menuHolder.getCount(); results in a NullPointerException. Log.d((menuHolder == null) + "", "MENUHOLDER NULL"); returns true at this point, when it had previously returned false.

just Do single change and will work
public class RestaurantMenuHolder{
private static RestaurantMenuHolder mInstance = null;
public static ArrayList<RestaurantMenuObject> restaurantMenu;
public static RestaurantMenuHolder getInstance(ArrayList<RestaurantMenuObject> restaurantMenu){
if(mInstance == null){
mInstance = new RestaurantMenuHolder(restaurantMenu);
}
return mInstance;
}
public static RestaurantMenuHolder getInstance(){
if(mInstance == null){
mInstance = new RestaurantMenuHolder(restaurantMenu);
}
return mInstance;
}
private RestaurantMenuHolder(ArrayList<RestaurantMenuObject> restaurantMenu){
this.restaurantMenu = restaurantMenu;
}
public static int getCount(){
return restaurantMenu.size();
}
}

Related

Getting raw resource (sound) in non-activity class

I'm trying to create a singleton class that will be responsible for playing game sounds. I created a singleton class GameSounds with a method playSound(). In the res folder I have a a subfolder 'raw' with a file letter_found.mp3.
This is the source code of the GameSounds class I wrote:
import android.app.Application;
import android.content.Context;
import android.media.MediaPlayer;
public class GameSounds extends Application {
private static GameSounds gameSounds = new GameSounds();
private static MediaPlayer soundPlayer;
private static Context mContext;
private static int mySoundId = R.raw.letter_found;
private GameSounds() {
mContext = this;
}
public static GameSounds getInstance() {
return gameSounds;
}
public static void playSound() {
soundPlayer = MediaPlayer.create(mContext, mySoundId);
soundPlayer.start();
}
}
This doesn't seem to work as I'm getting the following error message:
"java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on a null object reference"
I don't understand why this is happening. I tried to search Stackoverflow but couldn't find a solution.
Any help/explanation is greatly appreciated.
You can have a Singleton holding an Application Context (NOT Activity context) but practically you have to set this context before you use your singleton which can be enforced by throwing exceptions. See below example code.
public class GameSounds {
private static Context sContext;
public static void setContext(Context context) {
if (context == null) {
throw new IllegalArgumentException("context cannot be null!");
}
// In order to avoid memory leak, you should use application context rather than the `activiy`
context = context.getApplicationContext();
if (context == null) {
throw new IllegalArgumentException("context cannot be null!");
}
sContext = context;
}
private static Context getContext() {
if (sContext != null) {
return (Context)sContext;
}
throw new IllegalStateException("sContext was not set yet! Please call method setContext(Context context) first.");
}
// the rest of other methods. e.g. playSounds()
private static GameSounds gameSounds = new GameSounds();
private GameSounds() {
}
public static GameSounds getInstance() {
return gameSounds;
}
public void playSound() {
Context context = getContext();
soundPlayer = MediaPlayer.create(context, mySoundId);
soundPlayer.start();
}
}
You shouldn't inherit Application class unless you try to use Singleton pattern. Because Application is base class which contains all other components such as activities and services.
Instead, GameSound class should contain Context object and proper constructor.
Example)
public class GameSounds {
private GameSounds gameSounds;
private MediaPlayer soundPlayer;
private WeakReference<Context> mContext;
private int mySoundId = R.raw.letter_found;
private GameSounds(Context context) {
mContext = new WeakReference<>(context);
}
public GameSounds getInstance(Context context) {
if (gameSounds == null) {
gameSounds = new GameSounds(context);
}
return gameSounds;
}
public void playSound() {
soundPlayer = MediaPlayer.create(mContext.get(), mySoundId);
soundPlayer.start();
}
}
In this code, there is WeakReference<Context> instead of Context. WeakReference is used to prevent memory leaks because memory leaks can occur if you have an instance outside the activity.
To play sound, execute GameSounds.getInstance(this).playSound(); is fine.
If Context can't provide when try to play sound, implement initialize methods and called in Application class can be ok.
public class GameSounds {
private static GameSounds gameSounds;
private MediaPlayer soundPlayer;
private WeakReference<Context> mContext;
private int mySoundId = R.raw.letter_found;
private GameSounds(Application context) {
mContext = new WeakReference<>(context);
}
public static void initialize(Application context) {
if (gameSounds == null) {
gameSounds = new GameSounds(context);
}
}
public static GameSounds getInstance() {
if (gameSounds == null) {
throw new NullPointerException("You need to initialize this code by GameSound.initialize(this) in application class");
}
return gameSounds;
}
public void playSound() {
soundPlayer = MediaPlayer.create(mContext.get(), mySoundId);
soundPlayer.start();
}
}
In this case, you should make Application class and initialize GameSound class by GameSound.initialize(this) in Application class.
To play sound, GameSound.getInstance().playSound() is fine.

Singleton to get a file location on Android

How can I have access to a file in my res/raw folder from a Singleton (not an activity) on Android?
I've tried:
InputStream is = MainActivity.getResources().openRawResource("data.json");
which doesn't work since "non-static method getResouces() cannot be referenced from static content".
I've also tried:
URL fileURL = getClass().getClassLoader().getResource(R.raw.data);
String filePath = fileURL.getPath();
which throws a Null-pointer exception.
My Singleton:
public class CoursesDataManager {
private static CoursesDataManager instance;
private final List<Course> courses;
public static CoursesDataManager getInstance() {
if (instance == null)
instance = new CoursesDataManager();
return instance;
}
private CoursesDataManager() {
courses = parseCourses(**filePath/inputStream**);
}
The reason I want to get the file is that I want my Singleton to parse the data in that file once, store this data, and have this data never change and be accessible throughout the lifetime of my application.
Thanks a lot.
public class CoursesDataManager {
private static CoursesDataManager instance;
private final List<String> courses;
public static CoursesDataManager getInstance(Context context) {
if (instance == null)
instance = new CoursesDataManager(context);
return instance;
}
private CoursesDataManager(Context context) {
courses =context.getResources().openRawResource(R.raw.filename);
}
}
call it from Activity
CoursesDataManager.getInstance(getApplicationContext());
You need context to access resources. You could use Application context for this purpose. Subclass your Application, save the context in a static variable. Use the context inside singleton.
Create a Application class:
public class MyApplication extends Application {
public static MyApplication context = null;
public void onCreate() {
super.onCreate();
context = this;
}
}
In your manifest, specify the name of the Application class:
<application
android:name=".MyApplication"
...
>
</application>
Now, use this context in singleton:
InputStream is = MyApplication.context.getResources().openRawResource("data.json");
Note: You can use this method, even when you are creating the singleton instance from outside the Activity, where you dont have Activity context.
Add the context to your class and change it to something like that:
public class CoursesDataManager {
private static CoursesDataManager instance;
private Context context;
private final List<Course> courses;
public static CoursesDataManager getInstance(Context context) {
if (instance == null)
instance = new CoursesDataManager(context.getApplicationContext());
return instance;
}
private CoursesDataManager(Context context) {
this.context = context;
courses = parseCourses(**filePath/inputStream**);
}
public class CoursesDataManager {
private static CoursesDataManager instance;
private static Context mContext;
private final List<Course> courses;
public static CoursesDataManager getInstance(Context context) {
if (instance == null)
{
instance = new CoursesDataManager();
}
mContext = context;
return instance;
}
private CoursesDataManager() {
courses = parseCourses(**filePath/inputStream**);
}
You can use resource from here like
mContext.getResources().openRawResource(resourceId);

Android Same instance output different result

I outputted the same object at the same time but I got different results...
What could be the cause of DIFFERENT result?
The function in UserHelper.class:
public void login(String phone, String password) {
UserModel.logInInBackground(phone, password, new LogInCallback<UserModel>() {
#Override
public void done(UserModel userModel, AVException e) {
if (null != userModel) {
if (userModel.getPosition() == UserModel.USER_BUYER) {
refresh();
DebugLog.e("fuck" + mUserStatus + UserInstance.getInstance().getUserStatus());
for (UserListener listener : listeners)
listener.OnUserLogin();
} else if (userModel.getPosition() == UserModel.USER_SELLER)
logout();
} else for (UserListener listener : listeners)
listener.HandleError(e.getCode());
}
}, UserModel.class);
public USER_STATUS getUserStatus() {
return mUserStatus;
}
And the UserInstance.class.
public class UserInstance {
public static UserHelper mInstance;
public static UserHelper getInstance() {
if (null == mInstance) mInstance = new UserHelper();
DebugLog.e(mInstance.toString());
return mInstance;
}
}
First of all, if you meant the UserHelper class to be a singleton,
why do you access the USER_STATUS instance using UserInstance.getInstance().getUserStatus() instead of just getUserStatus() ?
Second of all, you probably get different instances of UserHelper if the singleton is accessed from different threads, because your implementation is not thread-safe.
A correct implementation would be using a double locking pattern:
public class UserInstance {
public static UserHelper mInstance;
private static final ReentrantLock lock = new ReentrantLock();
public static UserHelper getInstance() {
if (null == mInstance){
lock.lock();
try{
if (null == mInstance){
mInstance = new UserHelper();
}
}
finally{
lock.unlock();
}
}
DebugLog.e(mInstance.toString());
return mInstance;
}
}
Ultimately, I get the same instance..
Thanks to Shlomi Uziei. I forgot to use double locking pattern. And I should not make mInstance static...

Android Volley error using Singleton pattern

I am trying to follow this guide on how to work with Volley using a Singleton. The goal is to use this Singleton to have my RequestQueue in an Application context so it won't be affected by shifting from landscape to portrait and such.
But I get the following error:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.io.File android.content.Context.getCacheDir()' on a null object reference
at com.android.volley.toolbox.Volley.newRequestQueue(Volley.java:45)
at com.android.volley.toolbox.Volley.newRequestQueue(Volley.java:105)
at com.android.volley.toolbox.Volley.newRequestQueue(Volley.java:115)
at se.ccconsulting.arenaui3.VolleySingleton.(VolleySingleton.java:20)
at se.ccconsulting.arenaui3.VolleySingleton.getInstance(VolleySingleton.java:34)
at se.ccconsulting.arenaui3.AdventureFragment.onCreateView(AdventureFragment.java:62)
...
And it points towards this line:
mRequestQueue = Volley.newRequestQueue(HelperApplication.getAppContext());
I am not sure what is wrong here as from what I can see in the code the getInstance() method in VolleySingleton.java is not supposed to
VolleySingleton.java
public class VolleySingleton {
private static VolleySingleton mInstance = null;
private RequestQueue mRequestQueue;
private ImageLoader mImageLoader;
private VolleySingleton(){
mRequestQueue = Volley.newRequestQueue(HelperApplication.getAppContext());
mImageLoader = new ImageLoader(this.mRequestQueue, new ImageLoader.ImageCache() {
private final LruCache<String, Bitmap> mCache = new LruCache<String, Bitmap>(10);
public void putBitmap(String url, Bitmap bitmap) {
mCache.put(url, bitmap);
}
public Bitmap getBitmap(String url) {
return mCache.get(url);
}
});
}
public static VolleySingleton getInstance(){
if(mInstance == null){
mInstance = new VolleySingleton();
}
return mInstance;
}
public RequestQueue getRequestQueue(){
return this.mRequestQueue;
}
public ImageLoader getImageLoader(){
return this.mImageLoader;
}
}
HelperApplication.java
public class HelperApplication extends Application{
private static HelperApplication mInstance;
private static Context mAppContext;
#Override
public void onCreate() {
super.onCreate();
mInstance = this;
this.setAppContext(getApplicationContext());
}
public static HelperApplication getInstance(){
return mInstance;
}
public static Context getAppContext() {
return mAppContext;
}
public void setAppContext(Context mAppContext) {
this.mAppContext = mAppContext;
}
}
This is the line of code I used from Volley before implementing the Singlton which worked perfectly but do not meet my needs:
RequestQueue queue = Volley.newRequestQueue(getActivity());
I am debugging on Genymotion.
Fruthermore, row of code from Volley.java mentioned in the exception:
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
I need help getting passed this error.
If you read the README of the Github repo that code links to, it specifically mentions, you need to add this to your manifest XML.
<application android:name="com.company.MyApplication">
</application>
In your case, change com.company.MyApplication to xxx.yyy.HelperApplication, though.

How to instantiate an object of an Activity Class from non-Activity Class?

I am creating a java class that DOES NOT extends Activity, and in its constructor, I want to instantiate an object from another class that instantiate SQLiteOpenHelper. The problem is, when instantiating an object of the SQLiteOpenHelperclass I need to pass a Context of the current class that DOES NOT extend Activity. What argument should I pass to an instance of the SQLiteOpenHelper.
Constructor of SQLiteOpenHelper:
public class MPLDataBase extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 2;
private static final String DATABASE_NAME = "MPL.db";
private static final String MPL_TABLE_NAME = "MPLData";
public MPLDataBase(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
Class that instantiate SQLiteOpenHelper: This is a NON_ACTIVITY Class
public class NotifyArrayAdapter {
private MPLDataBase mplOpenHelperDB = null;
private SQLiteDatabase sqliteDB = null;
private int itemClickedPos;
public NotifyArrayAdapter() {
mplOpenHelperDB = new MPLDataBase(WHAT_SHOULD_I_PASS_HERE);
}
NotifyArrayAdapterClass
public class NotifyArrayAdapter {
private MPLDataBase mplOpenHelperDB = null;
private SQLiteDatabase sqliteDB = null;
private int itemClickedPos;
public NotifyArrayAdapter(Context context) {
if (this.mplOpenHelperDB == null) {
this.mplOpenHelperDB = new MPLDataBase(context);
}
if (this.sqliteDB == null) {
this.sqliteDB = mplOpenHelperDB.getWritableDatabase();
}
}
public void deleteAtPos(int pos) {
if(! this.sqliteDB.isOpen()) {
this.sqliteDB = mplOpenHelperDB.getWritableDatabase();
}
this.itemClickedPos = pos;
int [] dbIDs = this.mplOpenHelperDB.getIDs();
mplOpenHelperDB.deleteRow(dbIDs[this.itemClickedPos]);
}
public void deleteAllDBRows() {
if(! this.sqliteDB.isOpen()) {
this.sqliteDB = mplOpenHelperDB.getWritableDatabase();
}
mplOpenHelperDB.deleteALLRows();
}
}
You need to pass current context to your MPLDataBase constructor
public NotifyArrayAdapter(Context con) {
mplOpenHelperDB = new MPLDataBase(con);
}
like if you call this from any Activity then
NotifyArrayAdapter notifyarrayAdapter = new NotifyArrayAdapter(yourActivity.This);
like if you call this from any Fargment then
NotifyArrayAdapter notifyarrayAdapter = new NotifyArrayAdapter(getActivity());

Categories

Resources