it-swarm.com.ru

getActivity () возвращает значение null в функции Fragment

У меня есть фрагмент (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();

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

160
Lukap

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

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

onAttach(Activity activity)

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

150
PJL

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

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

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

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

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

66
thucnguyen

Начиная с уровня API Android 23, onAttach (Activity Activity) устарела. Вам нужно использовать 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;
    }

}
16
Sachin

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

  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);
    }
});
9
zajac.m2

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

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

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

7
Bogdan Zurac

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

6
miguel

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


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

Это произошло, когда вы вызываете getActivity () в другом потоке, завершившемся после удаления фрагмента. Типичным случаем является вызов getActivity () (например, для Toast), когда 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
zurfyx

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

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

}
1
Vinil Chandran

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

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

1
Phạm Lam

Те, у кого все еще есть проблема с 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.

0
Shai

Вы можете использовать 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)

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

0
surga

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

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

0
Serdar Samancıoğlu