Android MVP - Share Preference - java

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;
}
}

Related

Memory allocation difference between Singleton vs static final instance

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.

Using getDefaultSharedPreferences in a method

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);
......
......
}

getResources() from within an enumeration -- Android Studio

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);
...
}

Avoiding class scoped factories with RoboGuice

I'm trying to use a Model-View-Presenter pattern in my Android project. I am using the excellent RoboGuice project with AssistedInject to manage my dependencies. I am struggling with the style of creating instances of my presenters.
AssistedInject appears to require me to first inject a factory to create a presenter and then use that factory to create an instance of a presenter. Injection (appears) to only work at the class scope level; I cannot inject local variables. My methods do not need both a factory and a presenter; I only care about the factory long enough to generate a single presenter. I'd like to avoid keeping the mostly useless factory around.
More Detail
In my implementation of the pattern, I choose for each presenter and view to hold a reference to the other. A presenter must usually be passed one or more "Service" objects that are used to interact with the model. An Android Activity is an implementation of a (MVP) View. An Activity must be the composition root of any Android application. Therefore, each activity must instantiate a presenter, and that presenter needs a service as well as a reference to the view.
In general, presenters look like
public class GreatStuffPresenter {
private final SomeService service;
private final GreatStuffView view;
#Inject
public GreatStuffPresenter(SomeService service, #Assisted GreatStuffView view) {
this.service = service;
this.view = view;
bind();
}
public void bind() {
Record r = service.getSomeRecord();
view.setField(r.field);
}
}
and Activites look like
public class GreatStuffActivity extends RoboActivity {
#Inject private final GreatStuffPresenterFactory presenterFactory;
private GreatStuffPresenter presenter;
#Override
public void onCreate(...) {
super.onCreate(savedInstanceState);
setContentView(R.layout.create_update_record);
presenter = presenterFactory.create(this);
}
}
Now What?
I am dissatisfied that I must scope the presenterFactory at the instance level; I only need it during onCreate(). Am I missing some additional magic that RoboGuice could perform for me? If not, is there a better practice or pattern I should be using to avoid this unnecessary scoping?
In the end, I decided to get the RoboGuice injector, ask it for an instance of my factory, and create the presenter I'm looking for. I am reasonably happy with this approach; I'm not polluting my class with variables I won't use, and the line seems reasonably simple.
public class GreatStuffActivity extends RoboActivity {
private GreatStuffPresenter presenter;
#Override
public void onCreate(...) {
super.onCreate(savedInstanceState);
setContentView(R.layout.create_update_record);
presenter = RoboGuice.getInjector(this).getInstance(GreatStuffPresenterFactory.class).create(this);
}
}

How do I create a class specifically for the purpose of using SharedPreferences?

I have a couple activities in my app that I would like to utilize shared preferences. Initially, I created a method in each activity to utilize SharedPreferences, which worked fine. However, since there are multiple activities that use the same data, I’m basically tucking similar methods in multiple places. So it seemed like it made more sense to create a class specifically for the purpose of handling all these methods.
Unfortunately, I don’t understand how to do it properly.
This won’t compile, because it says “getSharedPreferences is undefined for the type AppPrefs.”
public class AppPrefs {
public void foo() {
SharedPreferences settings = getSharedPreferences("MyAppPrefs", 0);
}
}
Finally, I thought, maybe since SharedPreferences is an interface I could do this, but then I’d have to implement the inherited methods. I have no reason to Override any of those methods, so there is no reason to do this either.
public class AppPrefs implements SharedPreferences {
public void foo() {
SharedPreferences settings = getSharedPreferences("MyAppPrefs", 0);
}
}
What makes sense to do here? Is there a concept am I missing? Could anyone elaborate and explain? Thanks.
Pass your context into your preference getter.
public class AppPrefs {
public static void foo(Context ctx) {
SharedPreferences settings = ctx.getSharedPreferences("MyAppPrefs", 0);
}
}
Now just pass in this from an activity class to foo()
If the preferences are global to the application, you can use PreferenceManager.getDefaultSharedPreferences(); when you need to access the common preferences. If the preferences are specific to a subset of Activities, you have a few different options:
You can make a Activity subclass that is extended by all classes which need to access the preferences:
public abstract class AbstractFooActivity extends Activity
{
protected SharedPreferences getFooPreferences()
{
return getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
}
private static final String PREFS_NAME = "FooPrefs";
}
public class AFooActivity extends AbstractFooActivity
{
public void aMethodThatNeedsPrefs()
{
// ...
SharedPreferences myPrefs = getFooPreferences();
}
}
Or, if like me, you'd rather not mess with the class hierarchy you can simply create a common constant value for the group of activities which need to access the preferences. This is useful in the situation where you have class outside of the Activity hierarchy that need to access the preferences. For instance, a Service.
public final class FooConstants
{
public static final String FOO_PREFS_NAME = "FooPrefs";
}
public class AFooActivity extends Activity
{
public void aMethodThatNeedsPrefs()
{
// ...
SharedPreferences myPrefs = getSharedPreferences(FOO_PREFS_NAME, MODE_PRIVATE);
}
}
public class AFooService extends Service
{
public void aMethodThatNeedsPrefs()
{
// ...
SharedPreferences myPrefs = getSharedPreferences(FOO_PREFS_NAME, MODE_PRIVATE);
}
}
The second method is slightly less encapsulated, but puts fewer restrictions on the object hierarchy, which is a good tradeoff in my opinion.

Categories