-2

In the time I have spent learning Android Development, I have realized that the use of "Context" is a common theme in nearly everything we do.
I recently read the following Article, and all of it's references: What is it about Context?

In addition to this being an informative resource regarding Context, I had an additional question, based on something it states..
It says (and I quote): (6) When in fragment, assign a context to the one from onAttach(Context context) method

QUESTION (1) : I am currently trying to adjust some Preferences using the Preferencec-API from within a PreferenceFragment.. In terms of Context, how should I go about this?
NOTE : I am doing this from within onPreferenceChangedListener.

QUESTION (2) : Is there a simple answer, or must I follow the instructions from the Quote I provided from the Link? And if so, how would I go about doing this, as my PreferenceFragment does not have any onAttach Method?

Aside from changing extends PreferenceFragment to be PreferenceFragmentCompat, I understand I must must also implement onAttach(Context context) into my code.

Q #1 - Does changing to PreferenceFragmentCompat require any other consequential changes?

Q #2 - I see that I must add onAttach to my code - would this go prior to onCreate, or instead?

Q #3 - Must I migrate all of my Code from onCreate into onAttach? ..or what's its's purpose?

Ultimately, I need to know what I've done incorrectly, and how to go about easily correcting it.
Keep in mind, I am still fairly new to many of Android Development concepts - but I'm learning.


import ...

           /*  SUPPOSED to CHANGE TO 'PreferenceFragmentCompat' (?)  */

public class SettingsFragment extends PreferenceFragment {

//  THIS IS A TOGGLE PREFERENCE
public static final String PREF_GPS_STATE_LISTENER = "pref_gpsStateListener";

//  THIS IS A LIST-PREFERENCE
public static final String PREF_NOTIFICATION_MODE = "pref_notificationMode";
    //  I STILL NEED TO IMPLEMENT THESE PREFERENCE CHANGES LATER (DISREGARD)
    public static final String NOTIFICATION_MODE_A = "Mode A";
    public static final String NOTIFICATION_MODE_B = "Mode B";

//  THIS IS A LIST-PREFERENCE
public static final String PREF_NOTIFICATION_TYPE = "pref_notificationType";
    //  I STILL NEED TO IMPLEMENT THESE PREFERENCE CHANGES LATER (DISREGARD)
    public static final String NOTIFICATION_TYPE_SOUND = "Sound";
    public static final String NOTIFICATION_TYPE_VIBRATION = "Vibration";

private SharedPreferences.OnSharedPreferenceChangeListener prefChangeListener;

/*
/   IS THIS WHERE I'M SUPPOSED TO IMPLEMENT 'onAttach(Context context)' (?)
/   AND IF SO, WHAT CHANGES TO MY CURRENT CODE MUST I MAKE.. (?)
*/

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    addPreferencesFromResource(R.xml.preferences);

    prefChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
        @Override
        public void onSharedPreferenceChanged
        (SharedPreferences sharedPreferences, String key) {
            if (key.equals(PREF_GPS_STATE_LISTENER)) {

                //  FIRE METHOD BELOW TO ENABLE/DISABLE A LISTENER OPTION IN PREFERENCES
                gpsListenerChangedMethod();
            }

            if (key.equals(PREF_NOTIFICATION_MODE)) {
                Preference notifModePref = findPreference(key);
                notifModePref.setSummary(sharedPreferences.getString(key, ""));

                //  FIRE METHOD BELOW TO HANDLE [SOME] OF THE CHANGES TO THIS PREFERENCE
                notifModeChangedMethod();
            }

            if (key.equals(PREF_NOTIFICATION_TYPE)) {
                Preference notifTypePref = findPreference(key);
                notifTypePref.setSummary(sharedPreferences.getString(key, ""));

                //  FIRE METHOD BELOW TO HANDLE [SOME] OF THE CHANGES TO THIS PREFERENCE
                notifTypeChangedMethod();
            }
        }
    };
}  // END of [onCreate]


public void gpsListenerChangedMethod() {

    final PackageManager pacMan =
        getActivity().getApplicationContext().getPackageManager();

    final ComponentName comp_LocationReceiver = new ComponentName
      ("com.studio2bdesigns.gpskillerproalpha122018",".LocationReceiver");

    final SharedPreferences getPrefs = 
        PreferenceManager.getDefaultSharedPreferences(getActivity());

    if (getPrefs.getBoolean(PREF_GPS_STATE_LISTENER, true)) {
        pacMan.setComponentEnabledSetting(comp_LocationReceiver, 
        PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
        Log.i(TAG, "PREF_GPS_STATE_LISTENER is 'TRUE' - Enabling Receiver.");

    } else if (!getPrefs.getBoolean(PREF_GPS_STATE_LISTENER, true)) {
        pacMan.setComponentEnabledSetting(comp_LocationReceiver, 
        PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
        Log.i(TAG, "PREF_GPS_STATE_LISTENER is 'FALSE' - Disabling Receiver.");
    }
}

public void notifModeChangedMethod() {

    Log.i(TAG, "Firing METHOD [notifModeChangedMethod]");
    //  Finish NOTIFICATION_MODE changes here, elsewhere, etc
}

public void notifTypeChangedMethod() {

    Log.i(TAG, "Firing METHOD [notifTypeChangedMethod]");
    //  Finish NOTIFICATION_TYPE changes here, elsewhere, etc
}

@Override
public void onResume() {
    super.onResume();

    getPreferenceScreen().getSharedPreferences()
        .registerOnSharedPreferenceChangeListener(prefChangeListener);

    Preference notifModePref = findPreference(PREF_NOTIFICATION_MODE);
    notifModePref.setSummary(getPreferenceScreen().getSharedPreferences()
        .getString(PREF_NOTIFICATION_MODE, ""));

    Preference notifTypePref = findPreference(PREF_NOTIFICATION_TYPE);
    notifTypePref.setSummary(getPreferenceScreen().getSharedPreferences()
        .getString(PREF_NOTIFICATION_TYPE, ""));
}

@Override
public void onPause() {
    super.onPause();

    getPreferenceScreen().getSharedPreferences()
        .unregisterOnSharedPreferenceChangeListener(prefChangeListener);
}


//  This METHOD was referenced in the Link I provided in my original post, as a way to retrieve Context from within a Fragment (such as PreferenceFragment I assume).. I'm unsure of how to go about implementing this.
@Override
public void onAttach (Context context) {
    super.onAttach(context);

    //  UNSURE OF HOW TO IMPLEMENT THIS METHOD.
    }

}

//  END of CLASS [SettingsFragment]
}

So my post has been Edited above to include my current Code for my PreferenceFragment, along with the questions I have pertaining to changing it to PreferenceFragmentCompat, as well as where, when, and how to go about using onAttach() (as currently things seem to function fairly well, but I've been told I need it).

Zoe
  • 27,060
  • 21
  • 118
  • 148
Studio2bDesigns
  • 578
  • 5
  • 12
  • `PreferenceFragment` is deprecated and must migrate to `PreferenceFragmentCompat` instead. All Fragment subclass contains `onAttach()`. – Enzokie Feb 14 '19 at 07:52
  • Oh seriously? Ok so I need to change `public class SettingsFragment extends PreferenceFragment` to be `PreferenceFragmentCompat` instead? And then, do I need to adjust anything in the rest of my code, which is currently within `onCreate' (which contains the onPreferenceChangedListener`, as well as `onPause`, and `onResume`.. **SO IN OTHER WORDS:** How does `onAttach()` come into play, in my case? And thank you in advance. – Studio2bDesigns Feb 14 '19 at 08:32
  • I can't give an answer since I am not fully sure what your code does. It would be nice if you can provide your current code along with the question. – Enzokie Feb 14 '19 at 09:07
  • Since PreferenceFragmentCompat deals with SharedPreferences it already holds the reference to the necessary Context, which most likely is the Activity's context so you don't need to worry about retrieving/using any other context other than the one you already have. PreferenceFragmentCompat does not own an `onAttach()` so you can't possibly implement it! To be honest, can't see a reason why you need it regarding the purpose of PreferenceFragmentCompat, `onCreate()` seems enough for everything you might need to do there – Ricardo Feb 14 '19 at 10:42
  • @Enzokie I have added my full Code example to my initial post, along with my concerns, as you had requested. Thanks for reminding me to add it. – Studio2bDesigns Feb 14 '19 at 14:00
  • @Ricardo This is why I am so confused - everybody is providing me with conflicting answers.. and now my question has received a Down-Vote for some reason - which, for a fairly new member to the community, is not a good thing, as I'm sure you can imagine.. In any case, I simply wish somebody could provide me with a straight-forward answer.. **I have provided an EDIT to my initial post, which includes my current Code**.. If anybody would be so kind as to look it over, and provide me with some constructive feedback to the questions I also added in the Edit, I would be most grateful! **Thank all** – Studio2bDesigns Feb 14 '19 at 14:10
  • **#1** PreferenceFragmentCompat is just similar/does the same thing to `PreferenceFragment` except you need to add an import in your Grade : `implementation 'com.android.support:preference-v7:28.0.0'` (they differ in internal implementation) **#2** By looking at this [Lifecycle Graph](https://developer.android.com/images/fragment_lifecycle.png) `onAttach()` is always called first then `onCreate()` – Enzokie Feb 15 '19 at 07:04
  • **#3** The catch here is that `onCreate()` is guaranteed to be called only once for the entire Fragment lifespan meanwhile `onAttach()` can be called multiple times depending how many times the fragment is reused. If you ask me I could not think of a good usecase that `onAttach()` is preferred over `onCreate()` or `onStart()` , yes `onAttach()` gives a context but you can also use `getContext()` withing `onCreate()` too. – Enzokie Feb 15 '19 at 07:05
  • `6) When in fragment, assign a context to the one from onAttach(Context context) method` I would assume the author is referring to an old bug in Android Fragment that `getContext()` returns null if called within `onCreate()`, getting the context from onAttach() is the only workaround before, this issue should be non existence by now (well hopefully). Also do not worry about that 1 downvote not unless it is a lot it will just add stress ;) – Enzokie Feb 15 '19 at 07:20
  • @Enzokie Thank you so much for taking the time to explain this to me.. Now that you've explained everything I was confused about, I now completely understand, and very much appreciate your help! **My only remaining Question is:** In my Code above (in my initial Post), does everything seem to look correct? If you could please take a quick look, and let me know if anything should be changed, it would be very helpful! (For Example, should I include `onAttach` prior to `onCreate`, and move `addPreferencesFromResource(R.xml.preferences);` into `onAttach` instead of `onCreate`? Thanks again! – Studio2bDesigns Feb 15 '19 at 09:23
  • So far I don't see any need for the `onAttach()`, you can remove it from your code. – Enzokie Feb 15 '19 at 10:27
  • It is preferred if you can post separate questions instead of combining your questions into one. That way, it helps the people answering your question and also others hunting for at least one of your questions. Thanks! – Zoe Feb 28 '19 at 19:16

1 Answers1

1

To initialize preferences (or any other server for that matter) you can give any context to the service, be it your fragment's, activity's or your application context, you can even give a View's context by View.getContext()! since all of them resolve to your application context which the preferences/other APIs need.

It is worth to note that you should care about "when" you are getting the context. For example, If your fragment is detached, It does not have a context and therefore will return null to you. Best place would be onViewCreated or onAttached.

The rules provided in the link are "good" as in terms of following guidelines, but they are not complete (and a complete set is not provided by Google, unfortunately). The #5 is the most important one, which is you should not keep static references to your context (or views, fragments, activities) because of memory leak issues.

For adding preference changed listener, you need to register it in onResume and unregister it in onPaused like below:

@Override
public void onResume() {
    super.onResume();
    getPreferenceManager().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);

}

@Override
public void onPause() {
    getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
    super.onPause();
}
Adib Faramarzi
  • 3,798
  • 3
  • 29
  • 44
  • @Adib_Faramarzi: Currently my `Class` extends `PreferenceFragment`, which contains a `onPreferenceChangedListener` inside of `onCreate`, and then of course separate methods for `onPause` and `onResume`.. I have a sepate `SettingsClass` that simply attaches my Fragment Layout Preferences.. **QUESTION:** As asked above (other than changing `extends PreferenceFragment` into `extends PreferenceFragmentCompat`, what else must I change? For **Example:** Where and how should I implement this `onAttach()` method, and should I be moving anytthing into it? **PS** Where to put onPreferenceChangedListener – Studio2bDesigns Feb 14 '19 at 08:39
  • Do you really need the `onAttached` method? If you are extending anything that extends fragment, you can `override` the `onAttached` method and do the things you want to do in there. For adding preference changes, I updated my answer. – Adib Faramarzi Feb 14 '19 at 10:01
  • @Adib_Faramarzi I have added my current code, **as requested**, to initial post.. I have included **3 Questions** with the my current code.. It seems as though I am getting conflicting answers. One moment I am being told if my Fragment is "detached" I must implement `onAttach` (or) `onViewCreated`, but then you say "Do you really need the onAttached method?" and proceed to confuse things by saying "If you are are extending Fragment you can `override` the `onAttached` and do things in there.. **Can I get clarification of what needs to be changed, based on my current Code?** _I'm doing my best_! – Studio2bDesigns Feb 14 '19 at 13:49
  • @Adib_Faramarzi PS. As you can see from my edit to add my code, I have already handle the "registering" and "un-registering" of the `OnSharedPreferenceChangeListener` - But I noticed in your `onResume` Method you put `super.onResume();` first, and then in your `onPause` method example, you put `super.onPause();` last (after the unregister listener code).. Aside from my other concerns, I just happened to notice that, and was curious if there was a reason for this? I always assumed `super.onSomething();` was always put first.. Am I wrong? or did you simply make a typo/mistake accidently? Thanks. – Studio2bDesigns Feb 14 '19 at 13:57
  • The reasoning is that it is better to do your stuff before something gets destroyed (imagine that super.onPause can do side-effects that might affect your code). If the API (by API I mean Android code-base for fragment) is done properly, this is not needed and it is better to put the `super` methods in first line (since they will do their actual onPause stuff in another internal method that is called after this one). – Adib Faramarzi Feb 15 '19 at 07:41
  • Thank you for explaining, and taking the time to help. Much appreciated! – Studio2bDesigns Feb 15 '19 at 09:10
  • No problem! Please accept the answer if it answers everything or let me know if it needs more clearance. – Adib Faramarzi Feb 15 '19 at 09:43