GetActivity() возвращает значение null в функции Фрагмент

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

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

и да, когда я его вызываю (из Activity), он равен null...

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

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

Ответ 1

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

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

onAttach(Activity activity)

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

Ответ 2

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

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

@Override
public void onDetach() {
    super.onDetach();
    mContext = null;
}

Ответ 3

Это произошло, когда вы вызываете 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;
    }
}
}

Удачного кодирования,

Ответ 4

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

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

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

    Activity a;

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

}

Ответ 5

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

Ответ 6

PJL является правильным. Я использовал его предложение, и это то, что я сделал:

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

    private final Object attachingActivityLock = new Object();

    private boolean syncVariable = false;

  • реализованы

@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

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

  • Независимо от метода, который вы вызываете вручную сразу после commit()
  • onAttach()
  • onCreateView()
  • onActivityCreated()

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

Ответ 8

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


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

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

Некоторые HTTP-вызовы выполнялись даже после закрытия активности (поскольку для завершения HTTP-запроса может потребоваться некоторое время). Я тогда, через HttpCallback пытался обновить некоторые фрагментированных поля и получил 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();
    }
}

Ответ 9

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

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

}

Ответ 10

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

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

Ответ 11

Те, у кого до сих пор проблема с onAttach (Activity activity), просто изменились на Context -

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

В большинстве случаев сохранение контекста будет достаточно для вас - например, если вы хотите сделать getResources(), вы можете сделать это прямо из контекста. Если вам все равно нужно сделать контекст в своей деятельности, сделайте это -

 @Override
public void onAttach(Context context) {
    super.onAttach(context);
    mActivity a; //Your activity class - will probably be a global var.
    if (context instanceof mActivity){
        a=(mActivity) context;
    }
}

Как было предложено пользователем1868713.

Ответ 12

Вы можете использовать onAttach или если вы не хотите размещать onAttach всюду, тогда вы можете поместить метод, который возвращает ApplicationContext в основном классе App:

public class App {
    ...  
    private static Context context;

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

    public static Context getContext() {
        return context;
    }
    ...
}

После этого вы можете повторно использовать его повсюду во всем своем проекте, например:

App.getContext().getString(id)

Пожалуйста, дайте мне знать, если это не сработает для вас.

Ответ 13

Еще одним хорошим решением было бы использование Android LiveData с архитектурой MVVM. Вы должны определить объект LiveData внутри вашей ViewModel и наблюдать его в своем фрагменте, а когда значение LiveData изменяется, он уведомит вашего наблюдателя (фрагмент в данном случае) только в том случае, если ваш фрагмент находится в активном состоянии, поэтому будет гарантировано, что вы сделает ваш пользовательский интерфейс работающим и получит доступ только к активности, когда ваш фрагмент находится в активном состоянии. Это одно преимущество, которое поставляется с LiveData

Конечно, когда этот вопрос был впервые задан, LiveData не было. Я оставляю здесь этот ответ, потому что, как я вижу, эта проблема все еще существует, и она может кому-то помочь.

Ответ 14

Лучший и надежный способ:

FragmentActivity activity = (FragmentActivity) getActivity();

activity.finish();