I am an android beginner and tried almost every method to use Shared Preferences but when I try to get value in nonactivity class method doesn't work ONLY IF APP REMOVED FROM RECENT APPS.
My Scenario is like:
Class LocationRequestHelper.java
import android.content.Context;
import android.preference.PreferenceManager;
public class LocationRequestHelper {
...
...
public static String getUserId() {
return MainActivity.preferences.getString("userId","");
}
public static void setUserId(String userId) {
MainActivity.preferences.edit().putString("userId", userId ).commit();
}
}
MainActivity
public class MainActivity extends FragmentActivity implements
SharedPreferences.OnSharedPreferenceChangeListener {
public static SharedPreferences preferences;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
preferences = PreferenceManager.getDefaultSharedPreferences(this);
LocationRequestHelper.setUserId("1");
....
....
Utils
public class Utils {
public static void getLocationUpdates(final Context context, final Intent intent, String broadcastevent){
LocationResult result = LocationResult.extractResult(intent);
if (result != null) {
List<Location> locations = result.getLocations();
Location firstLocation = locations.get(0);
accuracy = firstLocation.getAccuracy();
LocationData data = new LocationData();
// MY FUNCTION DOESN'T WORK HERE
data.setUsrid(LocationRequestHelper.getUserId());
updateServer(data);
}
}
getLocationUpdates is called from BroadcastReceiver
Now problems appears after I try to get userID using
LocationRequestHelper.getUserId();
EDITED : Added Broadcast code
LocationUpdatesBroadcastReceiver
public class LocationUpdatesBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "LUBroadcastReceiver";
public static final String ACTION_PROCESS_UPDATES ="PROCESS_UPDATES";
#Override
public void onReceive(Context context, Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_PROCESS_UPDATES.equals(action)) {
Utils.getLocationUpdates(context,intent,"PROCESS_UPDATES");
}
}
}
}
NOTE:: THIS WORKS FINE IF APP IS IN BACKGROUND. BUT CRASHES AFTER I KILL FROM RECENT APPS.
Please tell me how I can send saved user id in my api call?
sorry for caps but this is main problem :P
The reason the app is crashing is that when it is killed any static variables become null. However you are still trying to access the SharedPreferences from the broadcast receiver and as such are getting a NPE.
The first thing you can do is update your LocationRequestHelper class to accept a context in the method rather than using the static SharedPreferences:
public class LocationRequestHelper {
//...
public static String getUserId(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getString("userId","");
}
public static void setUserId(Context context, String userId) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
prefs.edit().putString("userId", userId ).commit();
}
}
Then update you MainActivity to get rid of the SharedPrefs business and change the call to:
LocationRequestHelper.setUserId(this, "1");
And the Utils class to:
data.setUsrid(context, LocationRequestHelper.getUserId());
Related
I'm trying to take string from user and use it later when the app is closed..now it works just when the app is in background but i lose the string when i close the app..is there a way to do it like this or i have to use SharedPreference and if i have to use it please explain how because i tried and failed..thanks alot.
this is my code in my MainActivity to the string from the EditText
public class MainActivity extends AppCompatActivity {
private SharedPreferences sharedPreferences;
private static String reminder;
private EditText et;
private Intent intent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// initialize variables
sharedPreferences = getSharedPreferences("MyPREFERENCES",Context.MODE_PRIVATE);
final SharedPreferences.Editor editor = sharedPreferences.edit();
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
et = (EditText) findViewById(R.id.Name);
reminder = et.getText().toString();
if(reminder == null){
reminder = "TWEAK!";
}
editor.putString("TAG",reminder);
editor.commit();
// do stuff
}
// get the user's string
public String getRem() {
reminder = sharedPreferences.getString("TAG", "");
return reminder;
}
the app crashes and gives
"Attempt to invoke interface method 'java.lang.String android.content.SharedPreferences.getString(java.lang.String, java.lang.String)' on a null object reference"
at this line
reminder = sharedPreferences.getString("TAG", "");
this is the class where i call the method
public class Notifications extends BroadcastReceiver {
private String rem;
// set notification
#Override
public void onReceive(Context context, Intent intent) {
// object to access MainActivity methods
MainActivity main = new MainActivity();
rem = main.getRem();
}
Wherever you called the method of getRem(), you can't do that outside the Activity as the SharedPreferences are null.
Like, I assume you made a new MainActivity(), then called getRem() on that, perhaps?
You need to obtain the SharedPreferences again from an available Context, and then you can use getString("TAG", "")
EDIT Borrowed from Shared preferences inside broadcastreceiver
public class Notifications extends BroadcastReceiver {
private String rem;
// set notification
#Override
public void onReceive(Context context, Intent intent) {
setRem(context);
}
private void setRem(Context context) {
SharedPreferences prefs = context.getSharedPreferences("MyPREFERENCES",Context.MODE_PRIVATE);
rem = prefs.getString("TAG", "");
}
}
You can use sharedpreferences
SharedPreferences sharedpreferences = getSharedPreferences("MyPREFERENCES",Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedpreferences.edit();
editor.putString("TAG",reminder);
editor.commit();
to retrive it:
sharedpreferences.getString("TAG","");
[update]
public class Notifications extends BroadcastReceiver {
private String rem;
// set notification
#Override
public void onReceive(Context context, Intent intent) {
// object to access MainActivity methods
SharedPreferences sharedPreferences = context.getSharedPreferences("MyPREFERENCES",Context.MODE_PRIVATE);
rem = sharedPreferences.getString("TAG", "");
}
more about SharedPreferences
I'm trying to implement MVP without Dagger (for learning purposes). But I got to the problem - I use Repository patter to get raw data either from cache (Shared Preferences) or network:
Shared Prefs|
|<->Repository<->Model<->Presenter<->View
Network|
But to put my hands on Shared Preferences I have to put somewhere line like
presenter = new Presenter(getApplicationContext());
I use onRetainCustomNonConfigurationInstance/getLastCustomNonConfigurationInstance pair to keep Presenter "retained".
public class MyActivity extends AppCompatActivity implements MvpView {
#Override
protected void onCreate(Bundle savedInstanceState) {
//...
presenter = (MvpPresenter) getLastCustomNonConfigurationInstance();
if(null == presenter){
presenter = new Presenter(getApplicationContext());
}
presenter.attachView(this);
}
#Override
public Object onRetainCustomNonConfigurationInstance() {
return presenter;
}
//...
}
So how to use Shared Preferences in MVP without Dagger and not causing Presenter to be Context dependent?
Your Presenter should not be Context dependent in the first place. If your presenter needs SharedPreferences you should pass them in the constructor.
If your presenter needs a Repository, again, put that in the constructor. I highly suggest watching Google clean code talks since they do a really good job explaining why you should use a proper API.
This is proper dependency management, which will help you write clean, maintainable, and testable code.
And whether you use dagger, some other DI tool, or supply the objects yourself is irrelevant.
public class MyActivity extends AppCompatActivity implements MvpView {
#Override
protected void onCreate(Bundle savedInstanceState) {
SharedPreferences preferences = // get your preferences
ApiClient apiClient = // get your network handling object
Repository repository = new Repository(apiClient, preferences);
presenter = new Presenter(repository);
}
}
This object creation can be simplified by using a factory pattern, or some DI framework like dagger, but as you can see above neither Repository nor your presenter depends on a Context. If you want to supply your actual SharedPreferences only their creation of them will depend on the context.
Your repository depends on some API client and SharedPreferences, your presenter depends on the Repository. Both classes can easily be tested by just supplying mocked objects to them.
Without any static code. Without any side effects.
This is how I do it. I have a singleton "SharedPreferencesManager" class that will handle all the read write operations to shared prefs like below
public final class SharedPreferencesManager {
private static final String MY_APP_PREFERENCES = "ca7eed88-2409-4de7-b529-52598af76734";
private static final String PREF_USER_LEARNED_DRAWER = "963dfbb5-5f25-4fa9-9a9e-6766bfebfda8";
... // other shared preference keys
private SharedPreferences sharedPrefs;
private static SharedPreferencesManager instance;
private SharedPreferencesManager(Context context){
//using application context just to make sure we don't leak any activities
sharedPrefs = context.getApplicationContext().getSharedPreferences(MY_APP_PREFERENCES, Context.MODE_PRIVATE);
}
public static synchronized SharedPreferencesManager getInstance(Context context){
if(instance == null)
instance = new SharedPreferencesManager(context);
return instance;
}
public boolean isNavigationDrawerLearned(){
return sharedPrefs.getBoolean(PREF_USER_LEARNED_DRAWER, false);
}
public void setNavigationDrawerLearned(boolean value){
SharedPreferences.Editor editor = sharedPrefs.edit();
editor.putBoolean(PREF_USER_LEARNED_DRAWER, value);
editor.apply();
}
... // other shared preference accessors
}
Then whenever access to shared preference is needed I pass the SharedPreferencesManager object in the relevant Presenter's constructor. For example :
if(null == presenter){
presenter = new Presenter(SharedPreferencesManager.getInstance(getApplicationContext()));
}
Hope this helps!
Another approach can also be found in the Android Architecture libraries:
As the Shared Preferences depends on a context, it solely should know about it. To have things in one place, I choose a Singleton to manage this. It consists of two classes: the Manager (i.e. the SharePreferenceManager or ServiceManager or whatever), and an initializer which injects the Context.
class ServiceManager {
private static final ServiceManager instance = new ServiceManager();
// Avoid mem leak when referencing context within singletons
private WeakReference<Context> context
private ServiceManager() {}
public static ServiceManager getInstance() { return instance; }
static void attach(Context context) { instance.context = new WeakReference(context); }
... your code...
}
The initializer is basically an empty Provider (https://developer.android.com/guide/topics/providers/content-providers.html), which is registered in the AndroidManifest.xml and loaded when the app starts:
public class ServiceManagerInitializer extends ContentProvider {
#Override
public boolean onCreate() {
ServiceManager.init(getContext());
return false;
}
#Nullable
#Override
public Cursor query(#NonNull Uri uri, #Nullable String[] projection, #Nullable String selection, #Nullable String[] selectionArgs, #Nullable String sortOrder) {
return null;
}
#Nullable
#Override
public String getType(#NonNull Uri uri) {
return null;
}
#Nullable
#Override
public Uri insert(#NonNull Uri uri, #Nullable ContentValues values) {
return null;
}
#Override
public int delete(#NonNull Uri uri, #Nullable String selection, #Nullable String[] selectionArgs) {
return 0;
}
#Override
public int update(#NonNull Uri uri, #Nullable ContentValues values, #Nullable String selection, #Nullable String[] selectionArgs) {
return 0;
}
}
All function are default implementations except the onCreate, which injects the required context into our manager.
Last step to get this working is to register the provider in the manifest:
<provider
android:authorities="com.example.service-trojan"
android:name=".interactor.impl.ServiceManagerInitializer"
android:exported="false" />
This way, your service manager is decoupled from any external context initialization. It now can be completely replaced with another implementation which is context-independent.
This is how I implement it. You can design it with an interface where you have different implementation for your app and test. I have used interface PersistentStorage which I provide depdencdy from UI/tests. This is just an idea, feel free to modify it.
From your Activity/Fragment
public static final String PREF_NAME = "app_info_cache";
#Inject
DataManager dataManager;
void injectDepedendency(){
DaggerAppcompnent.inject(this);//Normal DI withDagger
dataManager.setPersistentStorage(new PersistentStorageImp(getSharedPreferences()));
}
//In case you need to pass from Fragment then you need to resolve getSharedPreferences with Context
SharedPreferences getSharedPreferences() {
return getSharedPreferences(PREF_NAME,
Context.MODE_MULTI_PROCESS | Context.MODE_MULTI_PROCESS);
}
//This is how you can use in Testing
#Inject
DataManager dataManager;
#Before
public void injectDepedendency(){
DaggerTestAppcompnent.inject(this);
dataManager.setPersistentStorage(new MockPersistentStorageImp());
}
#Test
public void testSomeFeature_ShouldStoreInfo(){
}
/**
YOUR DATAMANAGER
*/
public interface UserDataManager {
void setPersistentStorage(PersistentStorage persistentStorage);
}
public class UserDataManagerImp implements UserDataManager{
PersistentStorage persistentStorage;
public void setPersistentStorage(PersistentStorage persistentStorage){
this.persistentStorage = persistentStorage;
}
}
public interface PersistentStorage {
/**
Here you can define all the methods you need to store data in preferences.
*/
boolean getBoolean(String arg, boolean defaultval);
void putBoolean(String arg, boolean value);
String getString(String arg, String defaultval);
void putString(String arg, String value);
}
/**
PersistentStorage Implementation for Real App
*/
public class PersistentStorageImp implements PersistentStorage {
SharedPreferences preferences;
public PersistentStorageImp(SharedPreferences preferences){
this.preferences = preferences;
}
private SharedPreferences getSharedPreferences(){
return preferences;
}
public String getString(String arg, String defaultval) {
SharedPreferences pref = getSharedPreferences();
return pref.getString(arg, defaultval);
}
public boolean getBoolean(String arg, boolean defaultval) {
SharedPreferences pref = getSharedPreferences();
return pref.getBoolean(arg, defaultval);
}
public void putBoolean(String arg, boolean value) {
SharedPreferences pref = getSharedPreferences();
SharedPreferences.Editor editor = pref.edit();
editor.putBoolean(arg, value);
editor.commit();
}
public void putString(String arg, String value) {
SharedPreferences pref = getSharedPreferences();
SharedPreferences.Editor editor = pref.edit();
editor.putString(arg, value);
editor.commit();
}
}
/**
PersistentStorage Implementation for testing
*/
public class MockPersistentStorageImp implements PersistentStorage {
private Map<String,Object> map = new HashMap<>();
#Override
public boolean getBoolean(String key, boolean defaultval) {
if(map.containsKey(key)){
return (Boolean) map.get(key);
}
return defaultval;
}
#Override
public void putBoolean(String key, boolean value) {
map.put(key,value);
}
#Override
public String getString(String key, String defaultval) {
if(map.containsKey(key)){
return (String) map.get(key);
}
return defaultval;
}
#Override
public void putString(String key, String value) {
map.put(key,value);
}
}
I want to get a string from my shared preference file and use for more classes, but I don't know why not work.
My reader class is:
import android.app.Activity;
import android.content.SharedPreferences;
public class A {
public static String url2;
public void execute() {
String URLPref = "URL";
SharedPreferences prefs = getSharedPreferences("com.exam.search_preferences",Activity.MODE_PRIVATE);
url2 = prefs.getString(URLPref , "");
}
private SharedPreferences getSharedPreferences(String string,
int modePrivate) {
return null;
}
}
And the second class that uses the string
public class SearchHome extends Activity {
static String url2;
A cls2= new A();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.search_activity);
cls2.execute();
url2 = A.url2;
Toast.makeText(getApplicationContext(),"URL:" + url2 ,
Toast.LENGTH_LONG).show();
...
Sorry for my bad english, I never learned.But I'm trying!
You need to pass the Context to your class A, because you can get the SharedPreferences from a Context object. NOTE, an Activity is a Context to some extend
public class A {
public static String url2;
/** #param context used to get the SharedPreferences */
public void execute(Context context) {
String URLPref = "URL";
SharedPreferences prefs = context.getSharedPreferences("com.exam.search_preferences",Activity.MODE_PRIVATE);
url2 = prefs.getString(URLPref , "");
}
}
And then pass the Context to your execute method
public class SearchHome extends Activity {
static String url2;
A cls2= new A();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.search_activity);
// pass context 'this' to the execute function
// This works, because SearchHome extends Activity
cls2.execute(this);
url2 = A.url2;
...
if your data is not confidential it would be a lot easier if you can make a class specially for shared preference and have other activities access it. you will save a lot of time and code will be a lot simpler to follow up
public class HelperShared {
public static final String score = "Score";
public static final String tag_User_Machine = "tag_User_Machine",
tag_Machine_Machine = "tag_Machine_Machine",
tag_Draw_Machine = "tag_Draw_Machine",
tag_Total_Machine = "tag_Total_Machine";
public static SharedPreferences preferences;
public static Editor editor;
public HelperShared(Context context) {
this.preferences = context.getSharedPreferences(score,
Activity.MODE_PRIVATE);
this.editor = preferences.edit();
}
/*
* Getter and Setter methods for Machine
*/
public void setUserMachine(int UserMachine) {
editor.putInt(tag_User_Machine, UserMachine);
editor.commit();
}
public void setMachineMachine(int MachineMachine) {
editor.putInt(tag_Machine_Machine, MachineMachine);
editor.commit();
}
public void setDrawMachine(int DrawMachine) {
editor.putInt(tag_Draw_Machine, DrawMachine);
editor.commit();
}
public void setTotalMachine(int TotalMachine) {
editor.putInt(tag_Total_Machine, TotalMachine);
editor.commit();
}
public int getUserMachine() {
return preferences.getInt(tag_User_Machine, 0);
}
public int getMachineMachine() {
return preferences.getInt(tag_Machine_Machine, 0);
}
public int getDrawMachine() {
return preferences.getInt(tag_Draw_Machine, 0);
}
public int getTotalMachine() {
return preferences.getInt(tag_Total_Machine, 0);
}
}
private SharedPreferences getSharedPreferences(String string,
int modePrivate) {
return null;
}
problem is here.
return null;
you have to return valid SharedPreferences object. otherwise you will always get NullPointerException.
Call this when you want to put a pref:
putPref("myKey", "mystring", getApplicationContext());
Call this when you want to get a pref:
getPref("myKey", getApplicationContext());
You can use SharedPreferences to save any primitive data: booleans, floats, ints, longs, and strings. This data will persist across user sessions (even if your application is killed).
Different Modes:
1 MODE_APPEND
This will append the new preferences with the already exisiting preferences
2 MODE_ENABLE_WRITE_AHEAD_LOGGING
Database open flag. When it is set , it would enable write ahead logging by default
3 MODE_MULTI_PROCESS
This method will check for modification of preferences even if the sharedpreference instance has already been loaded
4 MODE_PRIVATE
By setting this mode , the file can only be accessed using calling application
5 MODE_WORLD_READABLE
This mode allow other application to read the preferences
6 MODE_WORLD_WRITEABLE
This mode allow other application to write the preferences
Read More
You just need to make shared prefrences object in class where you want to have data
SharedPreferences prefrences = getSharedPreferences("my prefs",MODE_PRIVATE)
Editor editor = prefrences.edit();
String s = edit.getString("your key",value);
hope it helps !
I have activity and service, I would like to get a reference to Service integer, that is being updated from time to time in Service. My problem is that in my Activity I only get that integer first declared Value (for instance 0).
My main goal is to know Services' updated value every time I start my program.
Main activity:
if(Service.doesCounter>0){
//do something
//in this state Service.doesCounter always is 0(checked by log)
}
Service:
public static int doesCounter=0; // declared after class as class memeber
//code where I start my method does();
.....
public void does(){
doesCounter++;
Log.e("cccccc","Service Counter "+doesCounter); // everything ok, value is changing as suppose to.
}
Edit
my Shared Preferences class:
public class AppPreferences extends PreferenceActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
}
#Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
}
private static final String APP_SHARED_PREFS = "com.aydabtu.BroadcastSMS_preferences"; // Name of the file -.xml
private SharedPreferences appSharedPrefs;
private Editor prefsEditor;
public AppPreferences(Context context)
{
this.appSharedPrefs = context.getSharedPreferences(APP_SHARED_PREFS, Activity.MODE_PRIVATE);
this.prefsEditor = appSharedPrefs.edit();
}
public boolean getAnything() {
return appSharedPrefs.getBoolean("Anything", false);
}
public void setAnything(Boolean text) {
prefsEditor.putBoolean("Anything", text);
prefsEditor.commit();
}
Then from Main Activity:
public class MainActivity extends Activity {
protected AppPreferences appPrefs;
appPrefs = new AppPreferences(getApplicationContext());
appPrefs.setAnything(fasle);
Then from Service:
appPrefs = new AppPreferences(getApplicationContext());
And when this happens all earlier made changes are reseted, how to make service and MainActivity use same prefs? Maybe I can somehow make AppPrefs class static?
Using static class fields is considered a bad practice in android.
Your app's resources may be revoked by the os and another process of your app may be re-initialized whenever the user gets back to it. In this case you will loose doesCounter updates. I don't know if this is the case (it should work in a common scenario where your app is foregrounded, unless you are running your service in another process (using the flag isolatedProcess) .
The easiest way to achieve what you are trying to do "the android way" is to store the doesCounter in SharedPreferences.
One way to achieve that is having a static class like this:
public class PrefUtils {
private final static String NUM_DOES = "NumDoes";
public static int getNumDoes(Context c)
{
int mode = Activity.MODE_PRIVATE;
SharedPreferences mySharedPreferences = c.getSharedPreferences(PREF_NAME, mode);
return mySharedPreferences.getInt(NUM_DOES, 0);
}
public static void setNumDoes(int numDoes , Context c)
{
int mode = Activity.MODE_PRIVATE;
SharedPreferences mySharedPreferences = c.getSharedPreferences(PREF_NAME, mode);
SharedPreferences.Editor editor = mySharedPreferences.edit();
editor.putInt(NUM_DOES, numDoes);
editor.commit();
}
And you are done. Just call PrefUtils.getNumDoes / setNumDoes
I have a service that tracks battery information and stores it in Shared Preferences. Then from my main activity I have BroadcastReceiver(that has OnReceive for listening ACTION_BATTERY_CHANGED)
Every time OnReceive occurs battery level and temperature changes, but my Shared Preferences leaves the same until I change my screen orientation(I think because of recreation of activity).
Please, help me to somehow make onReceive method also update shared preferences.
My Main Activity:
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
appPrefs = new AppPreferences(getApplicationContext());
}
......
/* Broadcast Receiver for battery stats */
BroadcastReceiver batteryReceiver = new BroadcastReceiver() {
.....
private TextView text;
int level = -1;
#Override
public void onReceive(Context context, Intent intent) {
level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
text=(TextView) findViewById(R.id.TV_text);
text.setText("level"+level+"something from Shared Pref"+appPrefs.getAnything());
}
}
}
My Shared Preferences:
public class AppPreferences extends PreferenceActivity {
private static final String APP_SHARED_PREFS = "com.aydabtu.BroadcastSMS_preferences"; // Name of the file -.xml
private SharedPreferences appSharedPrefs;
private Editor prefsEditor;
public AppPreferences(Context context)
{
this.appSharedPrefs = context.getSharedPreferences(APP_SHARED_PREFS, Activity.MODE_PRIVATE);
this.prefsEditor = appSharedPrefs.edit();
}
public long getAnything() {
return appSharedPrefs.getLong("Anything", 9);
}
public void setAnything(long a) {
prefsEditor.putLong("Anything", a);
prefsEditor.commit();
}
My service:
BroadcastReceiver batteryLevelReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
......
appPrefs.setAnything(a);
So, why Shared Preferences are not updated as level or temperature in OnReceive method?
And what to do? Any help will be appreciated. :)
Finally, after so much research, I could make it work: just add this:
private Intent starterIntent;
in your class members area above class name, in my example below main class:
public class MainActivity extends Activity {
private Intent starterIntent;
Then anywhere you want place a code to refresh activity without showing to user.:
finish();
starterIntent.setFlags(intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivity(starterIntent);
overridePendingTransition(0,0);