【问题标题】:Use cases of Context in Android Development (Fragment, etc)Android开发中Context的用例(Fragment等)
【发布时间】:2019-02-14 07:46:11
【问题描述】:

在我学习 Android 开发的过程中,我意识到“上下文”的使用是我们所做的几乎所有事情的共同主题。
我最近阅读了以下文章,以及所有参考文献:What is it about Context?

除了这是关于上下文的信息资源之外,我还有一个额外的问题,基于它所说的......
它说(我引用):(6) When in fragment, assign a context to the one from onAttach(Context context) method

问题(1):我目前正在尝试使用 Preferencec-APIPreferenceFragment..就Context 而言,我应该如何处理?
注意:我是在onPreferenceChangedListener 内部执行此操作的。

问题(2): 是否有一个简单的答案,或者我必须按照我在链接中提供的报价中的说明进行操作?如果是这样,我该怎么做呢,因为我的 PreferenceFragment 没有任何 onAttach 方法?

除了将extends PreferenceFragment 更改为PreferenceFragmentCompat,我知道我还必须在我的代码中实现onAttach(Context context)

Q #1 -更改为PreferenceFragmentCompat 是否需要任何其他相应的更改?

Q #2 - 我知道我必须将 onAttach 添加到我的代码中 - 这会先于添加到 onCreate, 还是 >?

Q #3 -我必须将我的所有代码从onCreate 迁移到onAttach 吗? ..或者它的目的是什么?

最终,我需要知道我做错了什么,以及如何轻松地纠正它。
请记住,我对许多 Android 开发概念仍然很陌生 - 但我米学习


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

因此,我的帖子已在上面进行了编辑,以包含我当前的 PreferenceFragment 代码,以及与将其更改为 PreferenceFragmentCompat 相关的问题,以及在何处、何时以及如何使用 @ 987654339@(目前看来一切运作良好,但有人告诉我我需要它)。

【问题讨论】:

  • PreferenceFragment 已弃用,必须改为迁移到 PreferenceFragmentCompat。所有 Fragment 子类都包含 onAttach()
  • 哦,认真的吗?好的,所以我需要将 public class SettingsFragment extends PreferenceFragment 改为 PreferenceFragmentCompat 吗?然后,我是否需要调整其余代码中的任何内容,目前在onCreate' (which contains the onPreferenceChangedListeneronPauseonResume.. SO IN OTHER WORDS: 如何在我的情况下,onAttach() 会起作用吗?并提前感谢您。
  • 我无法给出答案,因为我不完全确定您的代码是做什么的。如果您能提供您当前的代码以及问题,那就太好了。
  • 由于 PreferenceFragmentCompat 处理 SharedPreferences 它已经拥有对必要上下文的引用,这很可能是 Activity 的上下文,因此您无需担心检索/使用除您的上下文之外的任何其他上下文已经有。 PreferenceFragmentCompat 不拥有onAttach(),所以你不可能实现它!老实说,关于 PreferenceFragmentCompat 的目的,看不出你需要它的原因,onCreate() 似乎足以满足你在那里可能需要做的所有事情
  • @Enzokie 按照您的要求,我已将完整的代码示例以及我的担忧添加到我的初始帖子中。感谢您提醒我添加它。

标签: android android-fragments android-activity android-fragmentactivity android-context


【解决方案1】:

要初始化首选项(或任何其他服务器),您可以为服务提供任何上下文,无论是您的片段、活动还是您的应用程序上下文,您甚至可以通过View.getContext() 提供View 的上下文!因为它们都解析为首选项/其他 API 需要的应用程序上下文。

值得注意的是,您应该关心获取上下文的“时间”。例如,如果您的片段是分离的,它没有上下文,因此将返回 null 给您。最好的地方是onViewCreatedonAttached

链接中提供的规则在以下准则方面是“良好的”,但它们并不完整(很遗憾,Google 并未提供完整的规则集)。 #5 是最重要的一个,因为内存泄漏问题,您不应该保留对上下文(或视图、片段、活动)的静态引用。

要添加偏好更改的侦听器,您需要在onResume 中注册并在onPaused 中取消注册,如下所示:

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

}

@Override
public void onPause() {
    getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
    super.onPause();
}

【讨论】:

  • @Adib_Faramarzi:目前我的Class 扩展了PreferenceFragment,它在onCreate 内部包含了一个onPreferenceChangedListener,当然还有onPauseonResume 的单独方法。有一个单独的SettingsClass,它只是附加了我的片段布局首选项。问题:如上所述(除了将extends PreferenceFragment 更改为extends PreferenceFragmentCompat,我还需要更改什么?例如 : 我应该在哪里以及如何实现这个onAttach() 方法,我应该将任何东西移入其中吗?PS 在哪里放置 onPreferenceChangedListener
  • 你真的需要onAttached 方法吗?如果你正在扩展任何扩展片段的东西,你可以override onAttached 方法并在那里做你想做的事情。为了添加偏好更改,我更新了我的答案。
  • @Adib_Faramarzi 我已按照要求将我当前的代码添加到初始帖子中。我在当前代码中包含了 3 个问题。 . 似乎我得到了相互矛盾的答案。有一刻我被告知如果我的 Fragment 是“分离的”,我必须实现 onAttach(或)onViewCreated,但随后你说“你真的需要 onAttached 方法吗?”并继续通过说“如果您要扩展 Fragment,您可以override onAttached 并在其中执行操作来混淆事情。我可以根据我当前的代码澄清需要更改的内容吗? 我尽力了
  • @Adib_Faramarzi PS。正如您从我添加代码的编辑中看到的那样,我已经处理了OnSharedPreferenceChangeListener 的“注册”和“取消注册” - 但我注意到在您的onResume 方法中您首先放置了super.onResume();,然后在您的onPause 方法示例中,您将super.onPause(); 放在最后(在取消注册侦听器代码之后)。除了我的其他担忧之外,我只是碰巧注意到了这一点,并且很好奇这是否有原因?我一直认为super.onSomething(); 总是放在第一位。我错了吗?还是您只是不小心打错了字/错误?谢谢。
  • 原因是最好在某些东西被破坏之前做你的东西(想象一下 super.onPause 可以产生可能影响你的代码的副作用)。如果 API(API 是指 Android 代码库的片段)正确完成,则不需要这样做,最好将 super 方法放在第一行(因为他们将在另一个内部方法中执行实际的 onPause 内容在这个之后调用)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-07
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 2013-03-05
相关资源
最近更新 更多