лучший способ использовать контекст во фрагменте

я использую фрагменты в своем приложении. я создал один родительский класс с именем BaseFragment, и все остальные фрагменты расширяют этот Basefrgment ниже, это фрагмент этого Basefragment

BaseFragment.java

public class BaseFragment extends Fragment {
    public MainActivity activity;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (activity == null && context instanceof  MainActivity) {
            activity = (MainActivity) context;
        }
    }
}


public void replaceFragment(Fragment fragment, FragmentDetail last) {

    fragmentManager = getSupportFragmentManager();
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    boolean push = true;
    if (Validator.isNotNull(last)) {
        push = false;
    }
    /*if(Validator.isNull(last)){
        transaction.setCustomAnimations(R.anim.enter_from_left, R.anim.exit_to_right);
    }else{
        transaction.setCustomAnimations(R.anim.enter_from_right, R.anim.exit_to_left);
    }*/
    transaction.add(R.id.frame_container, fragment, fragment.getClass().getName());
    if (Validator.isNull(last) && preferences.getFragmentStack().size() > 0) {
        last = preferences.getFragmentStack().lastElement();
    }
    if (Validator.isNotNull(last)) {
        Fragment f = fragmentManager.findFragmentByTag(last.className);
        if (Validator.isNotNull(f)) {
            f.onPause();
            transaction.remove(f);
        }
    }
    last = new FragmentDetail(fragment.getClass().getName(), getTitle().toString(), preferences.isBack());
    if (preferences.isBack() || preferences.getFragmentStack().size() == 0) {
        if (push) {
            preferences.getFragmentStack().push(last);
        }
    } else {
        while (preferences.getFragmentStack().size() > 1) {
            preferences.getFragmentStack().pop();
        }
        if (!preferences.getFragmentStack().lastElement().className.equals(last.className)) {
            preferences.getFragmentStack().push(last);
        }
    }
    transaction.commitAllowingStateLoss();
    changeNavigationIcon();

// HWUtil.showToast(this, fragmentManager.getBackStackEntryCount() + ""); }

и во всех других фрагментах, которые я использую в качестве контекста, мой вопрос заключается в том, является ли это плохим способом доступа к контексту таким образом или создает ли он утечку памяти или любой другой подход для доступа к контексту. Любая помощь приветствуется.


person Hardik Mehta    schedule 06.08.2016    source источник


Ответы (5)


Я думаю, что способ хранения context действительно оптимален, так как вы сможете использовать его в каждом из ваших экземпляров подфрагментов. Поскольку MainActivity является переменной экземпляра в вашем фрагменте, она будет удалена сборщиком мусора, когда ваш фрагмент будет уничтожен. И если я не ошибаюсь в жизненном цикле Activity-Fragment, когда ваша активность будет вращаться, будут созданы новые фрагменты, а старые экземпляры фрагментов будут уничтожены. Так что у нас и там хорошо. Однако вам нужно быть осторожным с объявлением переменной контекста:

public MainActivity activity;

Это делает его доступным из любого места. Любой класс может вызвать что-то вроде context = fragIns.activity и сохранить его там. Это будет очень плохо для вас, потому что теперь он содержит ссылку на эту контекстную переменную. Теперь, когда ваш фрагмент больше не нужен, он не будет собирать мусор, потому что какой-то другой класс содержит ссылку на одну из его переменных. Вы окажетесь в «городе утечки памяти».

Убедитесь, что вы бережно храните эту переменную и ее ссылка не передается другим классам. Поскольку он находится в суперклассе, вы можете определить его как:

protected MainActivity activity;

Это должно сделать работу.

person Shaishav    schedule 06.08.2016
comment
@ Shaishav, вы абсолютно правы .. можете ли вы дать еще одно решение о том, как получить доступ к этому контексту в классе адаптера - person Hardik Mehta; 06.08.2016
comment
Вы можете передать ссылку на свой экземпляр адаптера, просто передав ему переменную activity. Просто убедитесь, что сам экземпляр adapter объявлен только в вашем фрагменте. Мы не хотим, чтобы экземпляр activity был передан какому-либо классу, который не может быть уничтожен нашим фрагментом :) - person Shaishav; 06.08.2016

лучший способ - использовать функцию getActivity() внутри фрагмента для доступа к контексту, потому что она вернет экземпляр действия, к которому прикреплен фрагмент.

person Gokul Kumar    schedule 06.08.2016
comment
но много раз я сталкивался с исключением нулевого указателя, поскольку фрагмент отсоединен от активности - person Hardik Mehta; 06.08.2016
comment
используйте функцию getActivity (). - person Gokul Kumar; 06.08.2016
comment
что, если он случайно станет нулевым, поскольку статические переменные не рекомендуются - person Hardik Mehta; 06.08.2016
comment
@GokulKumar Ничего не сохраняйте context в статических полях, так как это делает ваше приложение восприимчивым к утечкам памяти. - person Shaishav; 06.08.2016
comment
@Shaishav, так что это лучший способ добиться этого, пожалуйста, предложите - person Hardik Mehta; 06.08.2016

использование getActivity() - это простой и быстрый способ получить контекст родительской активности, но проблема возникает, когда этот фрагмент отсоединен.

Таким образом, используя его как этот внутренний фрагмент, imo, будет достаточно, чтобы сделать это лучше....

@Override
public void onAttach(Activity activity) {        
    super.onAttach(activity);
    mContext=activity;
}
person mfaisalhyder    schedule 06.08.2016

Чтобы избежать проблем с памятью, рекомендуется всякий раз, когда вы используете onAttach(Context context), вы также использовали onDetach():

@Override
public void onAttach(Context context) {
    super.onAttach(context);
    if (activity == null && context instanceof  MainActivity) {
        activity = (MainActivity) context;
    }
}

@Override
public void onDetach() {
    this.activity = null;
    super.onDetach();
}
person Dimas Mendes    schedule 17.02.2017

Мой метод:

public class AppManager extends Application {


    private static AppManager mApp;

    @Override
    public void onCreate() {
        super.onCreate();
        mApp = this;
    }

    public static Context getContext() {
        return mApp.getApplicationContext();
    }
}

Итак, любой хочет получить Context, просто используя AppManager.getContext(), кроме запуска Activity. Это очень просто.

По-вашему, если активность перезапустится, фрагмент автоматически создастся снова. Если вы не обрабатываете действие перезапущенного действия, возможно, что действие имеет два одинаковых фрагмента, и тот, который автоматически создает не вызываемый OnAttch(), вызовет NullPointerException.

Мое решение:

public abstract class BaseTabActivity extends BaseActivity {

    @CallSuper
    protected void initTabs(boolean isRestarted) {
        if (isRestarted) {
            FragmentManager manager = getSupportFragmentManager();
            FragmentTransaction transaction = manager.beginTransaction();
            if (manager.getFragments() == null)
                return;
            Stream.of(manager.getFragments())
                    .forEach((fragment) -> {
                        if (fragment != null)
                            transaction.remove(fragment);
                    });
            transaction.commit();
            manager.executePendingTransactions();
        }
    }

    public FragmentTransaction getSlideAnimTransaction() {
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        transaction.setCustomAnimations(R.anim.slide_from_right, R.anim.slide_out_left);
        return transaction;
    }

}
person Yakami    schedule 06.08.2016
comment
@ IntelliJ Amiya, но, поскольку это статическая переменная, она может стать нулевой - person Hardik Mehta; 06.08.2016
comment
@ Яками, я не могу понять, что ты пытаешься сказать - person Hardik Mehta; 06.08.2016
comment
@HardikMehta Извините ..... Мой английский плохой ..... Я просто хочу сказать, что по-вашему, переменная MainActivity в некоторых случаях будет нулевой при перезапуске Activtiy. надеюсь, вы понимаете, что я говорю....._(:3」∠)_ очень жаль мой английский.(´;ω;`) - person Yakami; 06.08.2016
comment
@ Якми, на самом деле, я не могу понять, что ты пытаешься сказать. - person Hardik Mehta; 06.08.2016
comment
@HardikMehta Если фрагмент программно заменяет представление для отображения на экране, при перезапуске Activity (вращение экрана и т. д.) фрагмент автоматически создаст себя снова перед нашим кодом. Поэтому, если мы не обработаем действие, мы получим два одинаковых фрагмента после перезапуска действия. - person Yakami; 06.08.2016
comment
@Yakmi Я должен создать его только для портретного режима - person Hardik Mehta; 06.08.2016
comment
@HardikMehta О, это очень плохой способ избежать проблемы, потому что, если на устройстве мало доступной памяти и ваша активность невидима, система может восстановить память вашей активности. Поэтому, когда мы вернемся, активность перезапустится. - person Yakami; 06.08.2016
comment
@Yakmi Да, очень часто я запускаю приложение, оно перезапускает активность, поэтому, что мне нужно сделать, пожалуйста, дайте предложение, мое требование касается только портретного режима. - person Hardik Mehta; 07.08.2016
comment
@HardikMehta мое решение - это код второго сегмента, действие расширяет BaseTabActivity только тогда, когда действие запускается onCreate(Bundle savedIntanceBundle), вызывает initTabs(savedInstanceBundle) в onCreate, это очистит стек фрагмента, избегая проблемы. Это не лучший способ, но очень простой :) - person Yakami; 07.08.2016
comment
@Yakmi Я опубликую логику для замены фрагмента, и она управляется из основного действия, и все фрагменты в приложении расширяются из этого базового фрагмента. - person Hardik Mehta; 07.08.2016
comment
@Yakmi я разместил метод замены фрагментов - person Hardik Mehta; 08.08.2016