getActivity () возвращает ноль в функции Fragment - PullRequest
170 голосов
/ 02 июня 2011

У меня есть фрагмент (F1) с открытым методом, подобным этому

public void asd() {
    if (getActivity() == null) {
        Log.d("yes","it is null");
    }
}

, и да, когда я его вызываю (из Activity), он нулевой ...

FragmentTransaction transaction1 = getSupportFragmentManager().beginTransaction();
F1 f1 = new F1();
transaction1.replace(R.id.upperPart, f1);
transaction1.commit();
f1.asd();

Это должно быть что-то, что я делаю очень неправильно, но я не знаю, что это такое

Ответы [ 13 ]

154 голосов
/ 03 июня 2011

commit планирует транзакцию, то есть она не происходит сразу, а запланирована как работа над основным потоком в следующий раз, когда основной поток будет готов.

Я бы предложил добавить

onAttach(Activity activity)

метод к вашему Fragment и поставить точку останова на него и посмотреть, когда он вызывается относительно вашего вызова на asd().Вы увидите, что он вызывается после выхода из метода, в котором вы делаете вызов asd().Вызов onAttach - это то, где Fragment привязан к своей активности, и с этого момента getActivity() вернет ненулевое значение (nb, есть также вызов onDetach()).

84 голосов
/ 06 августа 2013

Лучше всего избавиться от этого, чтобы сохранить ссылку на активность при вызове onAttach и использовать ссылку на активность там, где это необходимо, например,

@Override
public void onAttach(Context context) {
    super.onAttach(activity);
    mContext = context;
}

@Override
public void onDetach() {
    super.onDetach();
    mContext = null;
}
73 голосов
/ 28 декабря 2014

Это произошло, когда вы вызываете getActivity() в другом потоке, который завершился после удаления фрагмента.Типичный случай - вызов getActivity() (например, для Toast), когда HTTP-запрос завершен (например, onResponse).

Чтобы избежать этого, вы можете определить имя поля mActivityи используйте его вместо getActivity().Это поле может быть инициализировано в методе Fragment onAttach () следующим образом:

@Override
public void onAttach(Context context) {
    super.onAttach(context);

    if (context instanceof Activity){
        mActivity =(Activity) context;
    }
}

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

public abstract class BaseFragment extends Fragment {

    protected FragmentActivity mActivity;

    @Override
public void onAttach(Context context) {
    super.onAttach(context);

    if (context instanceof Activity){
        mActivity =(Activity) context;
    }
}
}

Счастливого кодирования,

16 голосов
/ 04 марта 2016

Начиная с уровня API Android 23, onAttach (Активность) устарела. Вам нужно использовать onAttach (контекстный контекст). http://developer.android.com/reference/android/app/Fragment.html#onAttach(android.app.Activity)

Активность - это контекст, поэтому, если вы можете просто проверить, что контекст - это Активность, и, если необходимо, привести его.

@Override
public void onAttach(Context context) {
    super.onAttach(context);

    Activity a;

    if (context instanceof Activity){
        a=(Activity) context;
    }

}
11 голосов
/ 03 мая 2017

Другие ответы, в которых предлагается сохранить ссылку на активность в onAttach, просто указывают на реальную проблему.Когда getActivity возвращает ноль, это означает, что фрагмент не привязан к действию.Чаще всего это происходит, когда действие исчезает из-за поворота или завершения действия, но у фрагмента есть какой-то прослушиватель обратного вызова.Когда вызывается слушатель, если вам нужно что-то сделать с Активом, но Активность пропала, вы ничего не можете сделать.В вашем коде вы должны просто проверить getActivity() != null, и если его там нет, ничего не делать.Если вы сохраняете ссылку на пропавшую активность, вы запрещаете сборку мусора.Любой пользовательский интерфейс не сможет увидеть пользовательский интерфейс.Я могу представить некоторые ситуации, когда в прослушивателе обратного вызова вы можете захотеть иметь контекст для чего-то, не связанного с пользовательским интерфейсом, в этих случаях, вероятно, имеет больше смысла получить контекст приложения.Обратите внимание, что единственная причина, по которой трюк onAttach не является большой утечкой памяти, заключается в том, что обычно после выполнения прослушивателя обратного вызова он больше не нужен и может быть собран мусором вместе с фрагментом, всеми его представлениями и контекстом Activity.,Если у вас setRetainInstance(true) есть большая вероятность утечки памяти, поскольку поле «Деятельность» также будет сохранено, но после поворота это может быть предыдущее действие, а не текущее.

10 голосов
/ 17 мая 2013

ПЖЛ прав. Я использовал его предложение, и вот что я сделал:

  1. определены глобальные переменные для фрагмента:

    private final Object attachingActivityLock = new Object();

    private boolean syncVariable = false;

  2. реализована

@Override
public void onAttach(Activity activity) {
  super.onAttach(activity);
  synchronized (attachingActivityLock) {
      syncVariable = true;
      attachingActivityLock.notifyAll();
  }
}

3. Я завернул свою функцию, где мне нужно вызвать getActivity (), в потоке, потому что, если он будет работать в основном потоке, я заблокирую поток с шагом 4. и onAttach () никогда не будет вызываться.

    Thread processImage = new Thread(new Runnable() {

        @Override
        public void run() {
            processImage();
        }
    });
    processImage.start();

4. в моей функции, где мне нужно вызвать getActivity (), я использую это (до вызова getActivity ())

    synchronized (attachingActivityLock) {
        while(!syncVariable){
            try {
                attachingActivityLock.wait();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

Если у вас есть некоторые обновления пользовательского интерфейса, не забудьте запустить их в потоке пользовательского интерфейса. Мне нужно обновить ImgeView, чтобы я сделал:

image.post(new Runnable() {

    @Override
    public void run() {
        image.setImageBitmap(imageToShow);
    }
});
7 голосов
/ 01 февраля 2013

Порядок, в котором обратные вызовы вызываются после commit ():

  1. Какой бы метод вы ни вызывали вручную сразу после commit ()
  2. onAttach ()
  3. onCreateView ()
  4. onActivityCreated ()

Мне нужно было выполнить некоторую работу, включающую некоторые представления, поэтому onAttach () не работал для меня;оно сломалось.Поэтому я переместил часть своего кода, который устанавливал некоторые параметры, внутри метода, вызываемого сразу после commit () (1.), затем другую часть кода, которая обрабатывает представление внутри onCreateView () (3.).

2 голосов
/ 03 января 2018

Я использую OkHttp, и я только что столкнулся с этой проблемой.


Для первой части @ thucnguyen был на правильном пути .

Это произошло, когда вы вызываете getActivity () в другом потоке, завершившемся после удаления фрагмента.Типичным случаем является вызов getActivity () (например, для тоста), когда HTTP-запрос завершен (например, в onResponse).

Некоторые вызовы HTTP выполнялись даже после того, как действие былобыл закрыт (поскольку выполнение HTTP-запроса может занять некоторое время).Затем я через HttpCallback попытался обновить некоторые поля Fragment и получил исключение null при попытке getActivity().

http.newCall(request).enqueue(new Callback(...
  onResponse(Call call, Response response) {
    ...
    getActivity().runOnUiThread(...) // <-- getActivity() was null when it had been destroyed already

IMO, решение состоит в том, чтобы предотвращать обратные вызовы, когдафрагмент больше не жив (и это не только с Okhttp).

Исправление: предотвращение.

Если вы посмотрите на жизненный цикл фрагмента (подробнее здесь ), вы заметите, что есть методы onAttach(Context context) и onDetach().Они вызываются после того, как фрагмент принадлежит к действию, и непосредственно перед тем, как он перестает быть таким, соответственно.

Это означает, что мы можем предотвратить этот обратный вызов, управляя им в методе onDetach.

@Override
public void onAttach(Context context) {
    super.onAttach(context);

    // Initialize HTTP we're going to use later.
    http = new OkHttpClient.Builder().build();
}

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

    // We don't want to receive any more information about the current HTTP calls after this point.
    // With Okhttp we can simply cancel the on-going ones (credits to https://github.com/square/okhttp/issues/2205#issuecomment-169363942).
    for (Call call : http.dispatcher().queuedCalls()) {
        call.cancel();
    }
    for (Call call : http.dispatcher().runningCalls()) {
        call.cancel();
    }
}
1 голос
/ 22 февраля 2017

Где вы вызываете эту функцию?Если вы вызовете его в конструкторе Fragment, он вернет null.

Просто вызовите getActivity() при выполнении метода onCreateView().

1 голос
/ 21 октября 2016

Действуйте следующим образом. Я думаю, что это будет полезно для вас.

private boolean isVisibleToUser = false;
private boolean isExecutedOnce = false;


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View root = inflater.inflate(R.layout.fragment_my, container, false);
    if (isVisibleToUser && !isExecutedOnce) {
        executeWithActivity(getActivity());
    }
    return root;
}

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    this.isVisibleToUser = isVisibleToUser;
    if (isVisibleToUser && getActivity()!=null) {
        isExecutedOnce =true;
        executeWithActivity(getActivity());
    }
}


private void executeWithActivity(Activity activity){
    //Do what you have to do when page is loaded with activity

}
...