How to do Singleton in Android - java

I tried from this example
https://stackoverflow.com/a/9472019/485978
I have a service, this service just connects to the database directly and I put all data in a bean which is located inside this class.
public class ServiceApplication extends Application {
private static ServiceApplication mInstance;
public ServiceApplication() {
super();
}
public static ServiceApplication getInstance() {
return mInstance;
}
#Override
public void onCreate() {
super.onCreate();
mInstance = this;
}
private Person personalData;
public Person getPersonalData() {
return personalData;
}
public void setPersonalData(Person personalData) {
this.personalData = personalData;
}
}
When retrieving a data from the database I used an AsyncTask where in doBackground() this is the code
ServiceApplication.getInstance().setPersonalData(personalData);
Log.d("AndroidService", "First name: "+ ServiceApplication.getInstance().getPersonalData().getFirstName());
So far it can retrieved the First Name.
However when I try to access those data from another activity all I get is null.
I tried two ways but it produces null
First implementation:
ServiceApplication app = (ServiceApplication) getApplication();
String name = (app.getPersonalData() != null) ? app.getPersonalData().getFirstName().trim() : "user";
Second implementation:
String name = (ServiceApplication.getInstance().getPersonalData() != null) ? ServiceApplication.getInstance().getPersonalData().getFirstName().trim() : "user";
Do you guys no how to persist the data and how to retrieve it from other activities?

If you are trying to pass data between a service and an activity you have to use another approach, as described here. If you just want to load Person data from a database into a singleton then you don't need to a Service. Just open a connection to the db, read the data and store it. Something like:
...
public Person getPersonalData() {
if(personalData == null) {
... open the db and load the data here ...
}
return personalData;
}
...

Note that only mInstance is static. It does not guarantee that you will receive the same personalData object. personalData could be and is definitely null in your case.
You might want to declare personalData as static as well.
private static Person personalData;
But in this case, you will reset the data on your next call to setPersonalData.
I would recommend you create a static structure as
private static ArrayList<Person> pList = new ArrayList<Person>();
Then in your setPersonalData() you can add objects to your pList.
You can replace your getPersonalData to return the pList. Hence you can use the same singleton instance from you activity to add data to the list and use the same to retrieve

Related

using shared preferences to store user name

I have a class to help me with handling back end requests; in this class I have a line to resolve my user object and also store the username locally:
public class BackEndHelper {
....
public static void Post_Login_async(user_username_pass_obj obj, final Context context) {
...
user_obj userObj = utilsJson.getUser_when_SignInUp(response.body().string(), context);
}
....
}
In the utilsJson.getUser_when_SignInUp I have the line to save the username:
public static void Write2SharedPref(Context context, user_obj userObj) {
PreferenceManager.getDefaultSharedPreferences(context)
.edit()
.putString("username", userObj.getUsername())
.apply();
}
the above method breaks even if username is not null. My plan is to read this then in my MainActivity:
PreferenceManager.getDefaultSharedPreferences(context)
.getString("token", null);
Would you please let me know if my local read/write is ok to store some variables?
Thanks

How to read data and listen to changes from Room database in a Service?

i am storing user information in a local room database. In activities and fragments I use AndroidViewModel and LiveData to listen to changes made to the database and update the UI.
Now I want to analyze all of the past user data to give recommendations for future decisions. My recommendations change on every change to the database made by the user so I need to update my reommendations frequently while doing the same calculations over and over again.
I was thinking about starting a service on app start that listens to database changes via ViewModel and LiveData and updates my recommendations (which are also stored in the same database). But somehow a Service cannot
get a ViewModel via viewModel = ViewModelProviders.of(this).get(DataViewModel.class);
observe a LiveData object as it is not a LifecycleOwner.
Basically I simply need to read all entries from the database, analyze the data and update 5-10 values every time the database content changes.
How and where should I do my calculations if not in a service? Maybe I am trapped in a wrong thought and a service is not the right way to do it so any idea on how to do this is very much appreciated!
observe a LiveData object as it is not a LifecycleOwner
Use observeForever() on the LiveData, manually unregistering via removeObserver() when appropriate (onDestroy() of the service, if not sooner).
Bear in mind that standard service limitations apply here (e.g., services run for ~1 minute on Android 8.0+ unless they are foreground services), so it may be that you need to consider other approaches anyway.
I ended up using a Service and solved my problem as follows:
In the onCreate method of my Application object I bind MyService to it:
serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder iBinder) {
service = ((MyService.MyLocalBinder) iBinder ).getService();
}
#Override
public void onServiceDisconnected(ComponentName name) {
}
};
Intent intent = new Intent(this, MyService.class);
getApplicationContext().bindService(intent, serviceConnection, BIND_AUTO_CREATE);
Binding a Service to the application context should keep the Service alive as long as the application is not destroyed. In MyService I get an instance of ViewModel via AndroidViewModelFactory like this
MyViewModel myViewModel = ViewModelProvider.AndroidViewModelFactory.getInstance(getApplication()).create(MyViewModel.class);
and I am able to observe the fetched LiveData from the ViewModel via observeForever like this
Observer<List<Entry>> obsEntries = new Observer<List<Entry>>() {
#Override
public void onChanged(#Nullable List<Entry> entries) {
//perform calculations with entries in here
}
};
viewModel.getEntries().observeForever(obsEntries);
Important: Remove the observer from the LiveData reference in onDestroy of the Service (that is why I keep a local reference to the Observer object):
#Override
public void onDestroy() {
super.onDestroy();
viewModel.getEntries().removeObserver(obsEntries);
}
Thanks everybody!
Nothing to change in Entity.
In DAO, say for e.g.,
#Dao
public interface TimeDao {
// ...
// ...
// for a started Service using startService() in background
#Query("select * from times where name = :bName")
List<TimeEntity> getServiceTimes(String bName);
}
which is not a LiveData
In Database, for e.g.,
#Database(entities = {DataEntity.class, TimeEntity.class}, version = 1)
public abstract class BrRoomDatabase extends RoomDatabase {
public abstract TimeDao iTimeDao();
public static BrRoomDatabase getDatabase(final Context context) {
if (INSTANCE == null) {
// ...
}
return INSTANCE;
}
}
An interface class
public interface SyncServiceSupport {
List<TimeEntity> getTimesEntities(String brName);
}
An implementation class for it.
public class SyncServiceSupportImpl implements SyncServiceSupport {
private TimeDao timeDao;
public SyncServiceSupportImpl(Context context) {
timeDao = BrRoomDatabase.getDatabase(context).iTimeDao();
// getDatabase() from where we get DB instance.
// .. and ..
// public abstract TimeDao iTimeDao(); <= defined abstract type in RoomDatabase abstract class
}
#Override
public List<TimeEntity> getTimesEntities(String name) {
return timeDao.getServiceTimes(name);
}
}
And finally... in the service..
public class SyncService extends Service {
//..
// now, iEntities will have a List of TimeEntity objects from DB ..
// .. retrieved using #Query
private static List<TimeEntity> iEntities = new ArrayList<>();
private static SyncServiceSupportImpl iSyncService;
private static void getFromDB(String brName) {
new AsyncTask<String, Void, Void>() {
#Override
protected Void doInBackground(String... params) {
iEntities = iSyncService.getTimesEntities(params[0]);
return null;
}
#Override
protected void onPostExecute(Void agentsCount) {
}
}.execute(brName);
}
#Override
public void onCreate() {
super.onCreate();
//init service
iSyncService = new SyncServiceSupportImpl(SyncService.this);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// this can be elsewhere also !..
getFromDB(myName);
}
}

Room doesn't store data

My aim is to store some data into a SQLite database using Room.
So I made a lot of #Entities in POJO.
For each #Entity I made a #Dao with at least these queries:
#Dao
public interface RouteDao {
#Query("SELECT * FROM route")
LiveData<List<Route>> getAll();
#Insert
void insertAll(List<Route> routes);
#Query("DELETE FROM route")
void deleteAll();
}
My Singleton Room #Database is:
#Database(entities = {Agency.class, Calendar.class, CalendarDate.class, FeedInfo.class, Route.class, Stop.class, StopTime.class, Transfer.class, Trip.class}, version = 1)
#TypeConverters(MyConverters.class)
public abstract class GtfsDatabase extends RoomDatabase {
private static final String DATABASE_NAME = "gtfs-db";
private static GtfsDatabase INSTANCE;
public abstract AgencyDao agencyDao();
public abstract CalendarDao calendarDao();
public abstract CalendarDateDao calendarDateDao();
public abstract FeedInfoDao feedInfoDao();
public abstract RouteDao routeDao();
public abstract StopDao stopDao();
public abstract StopTimeDao stopTimeDao();
public abstract TransferDao transferDao();
public abstract TripDao tripDao();
public static synchronized GtfsDatabase getDatabase(Context context) {
return INSTANCE == null ? INSTANCE = Room.databaseBuilder(
context.getApplicationContext(),
GtfsDatabase.class,
DATABASE_NAME
).build() : INSTANCE;
}
}
When I open the app for the first time, I fill the database with data in a background IntentService:
public static void importData(Context context, Map<String, String> data) {
GtfsDatabase db = GtfsDatabase.getDatabase(context);
db.beginTransaction();
try {
db.agencyDao().deleteAll();
db.calendarDao().deleteAll();
db.calendarDateDao().deleteAll();
db.feedInfoDao().deleteAll();
db.routeDao().deleteAll();
db.stopDao().deleteAll();
db.stopTimeDao().deleteAll();
db.transferDao().deleteAll();
db.tripDao().deleteAll();
db.agencyDao().insertAll(rawToAgencies(data.get(AGENCY_FILE)));
db.calendarDao().insertAll(rawToCalendars(data.get(CALENDAR_FILE)));
db.calendarDateDao().insertAll(rawToCalendarDates(data.get(CALENDAR_DATES_FILE)));
db.feedInfoDao().insertAll(rawToFeedInfos(data.get(FEED_INFO_FILE)));
db.routeDao().insertAll(rawToRoutes(data.get(ROUTES_FILE)));
db.tripDao().insertAll(rawToTrips(data.get(TRIPS_FILE)));
db.stopDao().insertAll(rawToStops(data.get(STOPS_FILE)));
db.stopTimeDao().insertAll(rawToStopsTimes(data.get(STOP_TIMES_FILE)));
db.transferDao().insertAll(rawToTransfers(data.get(TRANSFERS_FILE)));
PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(context.getString(R.string.empty), false).apply();
} finally {
db.endTransaction();
}
}
I am absolutely sure that the data is correct. I debugged each line and I can say 100% sure that the list of objects I pass to these functions is correct. No error at all.
When this service is finished (if(!sharedPreferences.getBoolean(getString(R.string.empty), true))) I try to access to the database in another activity and this show me that is empty.
I checked with this library debugCompile 'com.amitshekhar.android:debug-db:1.0.0' and every table is really empty.
What I'm doing wrong?
I know you cannot see all my code, and maybe there's something wrong, so my actual question is: is the above code correct?
Solved.
Android Room is handling transactions automatically.
#Query are asynchronous, while #Insert and #Delete are synchronous.
My error was to try including all those operations in a single transaction.
The solution is: let that Room handles them automatically.
public static void importData(Context context, Map<String, String> data) {
GtfsDatabase db = GtfsDatabase.getDatabase(context);
db.agencyDao().deleteAll();
db.calendarDao().deleteAll();
db.calendarDateDao().deleteAll();
db.feedInfoDao().deleteAll();
db.routeDao().deleteAll();
db.stopDao().deleteAll();
db.stopTimeDao().deleteAll();
db.transferDao().deleteAll();
db.tripDao().deleteAll();
db.agencyDao().insertAll(rawToAgencies(data.get(AGENCY_FILE)));
db.calendarDao().insertAll(rawToCalendars(data.get(CALENDAR_FILE)));
db.calendarDateDao().insertAll(rawToCalendarDates(data.get(CALENDAR_DATES_FILE)));
db.feedInfoDao().insertAll(rawToFeedInfos(data.get(FEED_INFO_FILE)));
db.routeDao().insertAll(rawToRoutes(data.get(ROUTES_FILE)));
db.tripDao().insertAll(rawToTrips(data.get(TRIPS_FILE)));
db.stopDao().insertAll(rawToStops(data.get(STOPS_FILE)));
db.stopTimeDao().insertAll(rawToStopsTimes(data.get(STOP_TIMES_FILE)));
db.transferDao().insertAll(rawToTransfers(data.get(TRANSFERS_FILE)));
PreferenceManager.getDefaultSharedPreferences(context).edit().putBoolean(context.getString(R.string.empty), false).apply();
}

Best way to call object created in Listener class in other parts of the application

I am working on an application developed using Servlet and spring.
Below is the code followed by the description.
package com.mymodule.listener;
import net.sf.ehcache.*;
//imports
public class MyInitializationListener implements ServletContextListener {
/** Singleton instance of CacheManager. */
private static CacheManager singletonManager = null;
public void contextDestroyed(ServletContextEvent arg0) {
System.out.println("--ServletContextListener destroyed*--");
}
private static CacheManager getInstance() {
if (singletonManager == null) {
singletonManager = CacheManager.create();
}
return singletonManager;
}
private Cache getCache() {
Cache cache = null;
cache = MyInitializationListener.getInstance().getCache("myCache");
return cache;
}
// Run this before web application is started
public void contextInitialized(ServletContextEvent arg0) {
final Cache cache = getCache();
//logic here
}
Above is my Listener class which gets loaded as soon as the server is started. I have created the ehcache object in the above class. My requirement is as below:
Connect to the database and get the value.
Store the value in cache , so that any further request to that value is retrieved from a cache instead of a database hit.
But the issue is when the above Listener is initialized during server startup, my other XML files where I have configured the data sources are not yet started.So I cannot write any database logic in my Listener class. To solve this issue I have to write the database logic in other class and that class is present in other packages.
package com.mymodule.dao.hibernate;
public class MyDAOImpl extends HibernateDaoSupport implements MyDAO {
public String getDataValue() throws DataLayerException {
//String SQL = "...";
//logic to connect to the database and get the value.
//here I want to get that ehcache object which was created after the server is started in MyInitializerListener class.
}
Please suggest which would be the best possible way and how to get the ehcache object in other classes of the application present in different packages. The value returned from the database table is being used in many parts of the application , and that's the reason I want to store that value in ehcache and call from the cache whenever required.
Create a getter method like this:
public MyObject getMyObject()
{
return myObjectInstance;
}
Then you will be able to get the object from any class that creates an instance of the listener. If you need to get it into a class that doesn't create an instance of the listener, you can still use this, as long as the requesting class has a reference to a class that can reference a class (...) that has access to an instance of the listener.
For example, if You had a class that needs the object and has an instance of the class that instantiated the listener, you could just create a getter method inside both the listener and the class that created it:
In the listener:
public MyObject getMyObject()
{
return myObject;
}
In the class that created the listener:
public MyObject getMyObjectFromListener()
{
return listenerInstance.getMyObject();
}
It's dirty, but it should work.
Since you are using Spring, the correct way is to make the CacheManager or the Cache proper Spring beans.
Once that is achieved you will be able to inject them in the places that require them, whether it is a ServletContextListener or you DAO implementation.

Extend Application Class for ParseUser

I would like to extend my Application class in order to globally access the logged in ParseUser without repeatedly calling .getCurrentUser() and accessing it's fields in every activity.
Currently my Application class is already extended to initialize Parse and ParseFacebookUtils. As per this thread, it says to make an instance of the ParseUser in the Application class however I'm not sure how to do so.
Note: I do not want to use intents as ParseUser is not serializable.
Thank you for your help. I can't see the relevance for code inclusion but please ask if you require any.
Method ParseUser.getCurrentUser() doesn't perform any network operations so it can't block UI. Also it is static, so by some means it already gives you global access.
I think it is not the best idea to duplicate it's value somewhere and it could cause bugs
What you can do is have 2 attributes in the existing application class.
isLoggedIn(boolean) - initialise it as false
user(ParseUser)
And place getters and setters.
When the user logs into the system you can set the isLoggedIn to true and set the user as well in the loginActivity. Suppose extended Application class is MyApplication.
((MyApplication)getApplication).setIsLoggedIn(true)
((MyApplication)getApplication).setUser(parseUser) .
After that you can in other activities you can simply check the isLoggedIn boolean value and do the necessary actions.
You can retrieve the set user by
ParseUser currentUser = ((MyApplication)getApplication).getUser()
Hope this helps.
I have implemented a Singleton to solve my problem. I have extended the Application class to initialize a Singleton so that an instance of this Singleton exists whether an activity is destroyed or othewise. I can access this instance via any activity and as such access all the fields of the current ParseUser.
// Application Class
public class Application extends android.app.Application{
#Override
public void onCreate() {
Parse.initialize(this, "redacted", "redacted");
ParseFacebookUtils.initialize(this);
initSingleton();
}
protected void initSingleton() {
ParseUserSingleton.initInstance();
}
}
// Singleton Class
public class ParseUserSingleton {
private static ParseUserSingleton instance;
public ParseUser user;
public HashMap<String, String> userFields = new HashMap<>();
public static void initInstance() {
if (instance == null) {
// Create the instance
instance = new ParseUserSingleton();
}
}
public static ParseUserSingleton getInstance() {
// Return the instance
return instance;
}
private ParseUserSingleton() {
// Constructor hidden because this is a singleton
}
public void customSingletonMethod() {
try {
user = ParseUser.getCurrentUser().fetch();
} catch (ParseException e) {
e.printStackTrace();
}
userFields.put("name", user.get("availability").toString());
// repeat for other fields
}
}
// Activity access
ParseUserSingleton.getInstance().customSingletonMethod();
userHashMap = ParseUserSingleton.getInstance().userFields;

Categories