So I am using getdefaultsharedpreferences in a method called onLoadFinish (it's from a pdf library from android).
Here's the code:
public void onLoadFinish(DocumentState.OPEN state) {
//some irrelevant code here
SharedPreferences pref= PreferenceManager.getDefaultSharedPreferences(getActivity());
String text = pref.getString("example_list","");
int foo = Integer.parseInt(text);
goToPage(foo);
//some irrelevant code there
}
So the main task of the code is to get a value from my example_list preference (a string), turn it into an integer and put this integer into my goTopage();, which makes the app jump to a certain page in my pdf document.
The problem is this part:
PreferenceManager.getDefaultSharedPreferences(getActivity())
getActivity isn't working. I have tried getApplicationContext() aswell. What should be in the brackets of getDefaultSharedPreferences()?
getDefaultSharedPreferences expects an instance of Context class. getActivity method is declared in the Fragment class, so, unless your onLoadFinish method is declared in any Fragment successor, you can't use it. Per your comments, if I understood you correctly, onLoadFinish is declared inside Activity. If so, you can just use this keyword to pass the context, because Activity is a successor of Context. If this method is declared in another class, you should pass context to it, via constructor injection, for example.
EDIT Example of providing context via constructor injection.
Let's say you have the following interface:
public interface MyInterface {
void myAction();
}
And you have a class, which implements it and requires an instance of Context to do the work:
public class MyClass implements MyInterface {
private WeakReference<Context> mContext;
public MyClass(Context context) {
this.mContext = new WeakReference<Context>(context);
}
#Override
public void myAction() {
Context ctx = mContext.get();
if (ctx != null){
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
//do stuff
}
}
}
As you can see, Context instance is injected via constructor and we don't keep a strong reference to the context (actually it depends on specific needs). This class can be used inside Activity in the following way:
MyClass myClass = new MyClass(this);
Or inside fragment:
MyClass myClass = new MyClass(getActivity());
PreferenceManager should be used with a PreferenceActivity.
Just use context.getSharedPreferences("pref_name", Context.MODE_PRIVATE);
getDefaultSharedPreferences required your application context a parameter,
Try this,
public class MyActivity extends ActionBarActivity
{
......
.......
PreferenceManager.getDefaultSharedPreferences(MyActivity.this);
......
......
}
Related
I started to learn MVP but I have a few questions related the SharedPreferences, as far as I know if I want to save a value in the sharedPreferences I need to pass this value to the presenter and the presenter calls the model to save the value, the same logic I would apply if I want to get or remove a value from the sharedPreference, but how is the best way to do that if I shouldn't pass the Context?
I sae a few code and the people used to pass the Context in the constructor method direct to the Model, but I still don't think that's a good idea.
Do you guys have any ideas?
Thanks,
Thales
Android specific imports should never exist in the Presenter if you want to keep it unit testable.
What you can do is, make an abstraction layer above SharedPreferences let's call it Cache, it would be an interface with all the needed caching methods, you would then provide a concrete implementation of it using SharedPreferences.
Here is a quick illustration of the idea:
interface Cache {
// Your caching methods
}
class CacheImpl implements Cache {
private SharedPreferences sharedPrefs;
public CacheImpl(Context context) {
// Takes a context to init sharedPrefs.
}
// implements all of Cache's methods
}
Then you would pass a reference for that implementation to the Presenter's constructor (better yet using DI to inject it to your presenters constructor):
Cache cache = new CacheImpl(myContext); // Naturally that would be an activity context
MyPresenter presenter = new MyPresenter(cache);
Then in your presenter you would receive that instance in the constructor:
private Cache cache;
public MyPresenter(Cache cache) {
this.cache = cache;
}
You can then use the cache variable without knowing about it's concrete implementation nor should you provide it a context.
Create a Storage class Object inside View and pass the context inside Storage Class constructor.
Then pass this storage class object in presenter (constructor) from View class.
Then whenever you need to save or get some data from your presenter - Then simply call the method of storage class from the object you have passed.
This way you will not need to send the context to your presenter.
View class
public class ViewClass extends ActionBarActivity {
private MyPresenter presenter;
private MyStorage storage;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
storage = new MyStorage(this);
presenter = new MyPresenter(this,storage);
}
}
MyStorage Class
public class MyStorage {
private Context mContext;
public MyStorage(Context context) {
this.mContext = context;
}
public void saveData(String data){
}
public String getData(){
return "";
}
}
MyPresenter class
public class MyPresenter {
private final ViewClass mView;
private final MyStorage mStorage;
public MyPresenter(ViewClass viewClass, MyStorage storage) {
this.mView = viewClass;
this.mStorage = storage;
}
}
public class MyClass {
private static MyClass instance = null;
private MyActivity myActivity;
private Button button;
public static MyClass getInstance(){
if (instance == null){
instance = new MyClass();
}
return instance;
}
private MyClass(){};
public void initialize(MyActivity activity){
myActivity = activity;
}
public void releaseMemory(){
instance = null;
}
}
Here in this approach whenver my application exits then
I can release memory by calling releaseMemory() so that myActivity
instance will not be leaked.
public class MyClass {
private static final MyClass instance = new MyClass();
private MyActivity myActivity;
private Button button;
private MyClass(){};
public void initialize(MyActivity activity){
myActivity = activity;
}
public void releaseMemory(){
instance = null; //Can't make it null
//Can do for individual variables
myActivity = null;
button = null;
}
}
In this approach as MyClass instance is final I can't make it null
instead I can make individual variables as null.
So is My understanding correct or am I missing anything respective to memory leaks?
First question, why do you need to keep a reference on an Activity object ?
For most usage you just need a Context object. So if you want to avoid leaking your Activity take the habit to do the following by replacing :
private MyActivity myActivity;
public void initialize(MyActivity activity){
myAcitivity = activity;
}
with:
private Context myContext;
public void initialize(Context context){
myContext = context.getApplicationContext();
}
Since Activity is an implementation of Context, your code should keep working. The method getApplicationContext() will always return an Application Context which you can leak all you want.
Now if an Application Context is not enough and you really need an Activity object (if you need to start other Activity without creating a new task for exemple), first ask yourself why you can't do that in your Activity class directly. If you can't, then maybe you have taken bad decision regarding your code architecture.
If you really need to store Activity object in other object (singleton or not), please remember that activities have a lifecycle, and you need your Activity to notify your MyClass that it will be no longer available (if you don't, and you forget to call releaseMemory(), then your Activity will leak when it goes to background) :
public class MyActivity {
MyClass myClass; // instance initialize somewhere in your code
onPause() {
myClass.setActivity(this);
}
onResume() {
myClass.setActivity(null);
}
}
public class MyClass {
#Nullable Activity myActivity; // tha #Nullable annotation helps you remember to do null checks before using this field.
public void setActivity(Activity activity) {
myActivity = activity;
}
}
In case your MyClass is also a Fragment, you can do the job of setting and releasing your Activity in the methods onAttach() and onDetach() (those methods are automaticaly called by Android, so you don't need to try to call them in your Activity).
Finally, I would advise against your first code sample, because even if you call your method releaseMemory() and ensure you have no reference left in your object A, there is nothing you can do to ensure an object B doesn't still have a reference to your class MyClass.
final field can't be modified. that's why final modifier exists.
second version of your singleton is better regarding conccurency. but it will be never gc'ed until you close the app.
I've just started with Android Development. Now, I want to make an enumeration. Each enum-object within that enumeration, I want to assign a name to. I want to get that name from my project resources (res/values/strings.xml).
But from within the enumeration type, I can't call getResources().getString(R.string.string_name).
Is that because the enumeration doesn't extend an activity or something?
How can I fetch the strings from the resources anyway?
Thanks!
P.S. The enumeration is not nested within a class. If it is possible, I want to keep it as a seperate file.
getResources().getString() is simply a shorthand for getActivity().getResources().getString().
So getResources() still rely on having a context (activity, fragment, context, etc).
To overcome this, i personally extended the Application class and made a public static Context available, so you can always access resources and such in your entire application, no matter if you're in an activity or a POJO.
Example:
public class MyApp extends Application {
public static Context context;
#Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
}
}
With this, you can always call:
MyApp.context.getResources().getString(xxxx);
Enumerators are implicitly static. As in when they are created, your activity and therefor its Context is nowhere to be found. If there's no Context, there are no Resources either.
Perhaps what you want to use is a static class with custom constructor instead of an enumerator. Example:
private static class MyConstants {
private Resources mResources;
public String constant1;
public String constant2;
public String constant3;
public MyConstants(Context ctx) {
mResources = ctx.getResources();
constant1 = mResources.getString(R.id.string1);
constant2 = mResources.getString(R.id.string2);
constant3 = mResources.getString(R.id.string3);
}
}
MyConstants mConstants;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_book);
mConstants = new MyConstants(this);
Log.e(TAG, mConstants.constant1);
...
}
I have a static class containing overlay items that is called by my main class and then added to an overlay itself.
I can get this to work without image types but I would like to use them, however when I do I get the following error:
Cannot make a static reference to the non-static method getResources() from the type ContextWrapper
I have tried quite a few things to overcome this, by following some guides I have tried to add:
private static Context context;
public void onCreate(){
super.onCreate();
Mine.context = getApplicationContext();
}
public static Context getAppContext() {
return Mine.context;
}
I have also ensured that I have the class in question as an application in the manifest.
The class is as follows:
public static ArrayList<ExtendedOverlayItem> array = new ArrayList<ExtendedOverlayItem>();
public ArrayList<ExtendedOverlayItem> getMine() {
return array;
}
public static void addMe() {
Drawable myDrawable = getResources().getDrawable(R.drawable.draw); //This is the line that doesn't work
ExtendedOverlayItem myMarker1 = new ExtendedOverlayItem(
"sample", "sample", new GeoPoint(85.123456,
-14.123456), null);
myMarker1.setMarker(myDrawable);
myMarker1.setDescription("This is a test description");
array.add(myMarker1);
}
private static Context context;
public void onCreate(){
super.onCreate();
Mine.context = getApplicationContext();
}
public static Context getAppContext() {
return Mine.context;
}
I have tried adding the following :
myMarker1.setMarker(Mine.getAppContext().getResources().getDrawable(R.drawable.example));
But I still get null pointer errors when calling from the main method, If I leave the images out, it is called correctly.
In the main method I call this class as follows:
Mine.addMe();
ItemizedOverlayWithBubble<ExtendedOverlayItem> thisThing = new ItemizedOverlayWithBubble<ExtendedOverlayItem>(this, Mine.array, map);
map.getOverlays().add(thisThing);
Any advice greatly appreciated.
In Java static method can not access any non-static method or variable.
One of the basic rules of working with static methods is that you can’t access a nonstatic method or field from a static method because the static method doesn’t have an instance of the class to use to reference instance methods or fields.
for more information Document
here is good example how to access them
if you have context set before this line
Drawable myDrawable = getResources().getDrawable(R.drawable.draw);
then use
Drawable myDrawable = context.getResources().getDrawable(R.drawable.draw);
as add me is static method and can not get context for getResources()
EDIT:
getResources() is called on context. and in your code you are calling it from a static method but static method can not access nonstatic method of your applications context.
so you made a static object for context and stored your context in it.
but have you checked that the context you set is not null and set before calling above line?
I need to get a reference to the shared prefs from inside an abstract class called A that does not extend anything.
I cannot pass a Context object to this class to get the shared prefs because being abstract, I don't instantiate it. I created class A to be extended by other POJOs that share a single attribute, which is the uuid. The UUID is generated once in the app on its first run, which is why I store it in the shared prefs. In class A's constructor, I'm hoping to set the uuid based on what is in the shared prefs.
public abstract class A {
private String uuid;
public A() {
// this is how I'm hoping to use the shared preferences
this.uuid = sharedPrefs.getString("KEY_UUID", "null");
}
// getter and setter
}
One suggestion I found is to extend Application, say in a class called App, and include an attribute android:name=".App" in the <application> tag in the manifest. I imagine App will be written like this:
public class App extends Application {
private static App app;
public void onCreate() {
this.app = this;
}
public static App getApp() {
return app;
}
}
...so that from inside class A, I can do this:
this.uuid = App.getApp().getSharedPreferences("prefs_name.txt", Context.MODE_PRIVATE).getString("KEY_UUID", "null");
However, doesn't the non-final static field app lose its reference to the App and become null when Android kills off the process or when the phone restarts? How can I get a reference to the shared prefs without using this method? Or should I just manually write the UUID to a file?
Modify your App class to something like this:
public class App extends Application
{
private static Context context;
#Override
public void onCreate()
{
super.onCreate();
App.context = getApplicationContext();
}
public static Context getStaticContext()
{
return App.context;
}
}
I cannot pass a Context object to this class to get the shared prefs because being abstract, I don't instantiate it.
Sure you can. Put Context as a parameter on the constructor. Or, put SharedPreferences as a parameter on the constructor, if you prefer. Your subclasses' constructors will need to take the same parameter and pass it to the super() call.
However, doesn't the non-final static field app lose its reference to the App and become null when Android kills off the process or when the phone restarts?
Well, the whole process is gone. Saying that it loses "its reference to the App and become null" is akin to saying that a person at ground zero of a nuclear blast will get a sunburn just prior to being vaporized -- while probably true, the point is a bit moot. :-)