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


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

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

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

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

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

13 156

13 ответов:

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

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

onAttach(Activity activity)

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

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

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

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

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

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

@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
    mActivity = activity;
}

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

public abstract class BaseFragment extends Fragment {

    protected FragmentActivity mActivity;

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        mActivity = (FragmentActivity) activity;
    }
}

удачи в кодировании,

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

}

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);
    }
});

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

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

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

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

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

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

}

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

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

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


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

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

ИМО решение заключается в предотвратить обратные вызовы происходят, когда фрагмент больше не жив больше (и это не только с 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();
    }
}

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

    @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;
    }
}

как предложено user1868713.

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

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)

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

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

FragmentActivity activity = (FragmentActivity) getActivity();

activity.finish();