android - getActivity () renvoie null dans la fonction Fragment





android-fragments android-activity android-context (12)


Le mieux pour s'en débarrasser est de garder une référence d'activité quand onAttach est appelé et d'utiliser la référence d'activité là où c'est nécessaire, par exemple

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

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

J'ai un fragment (F1) avec une méthode publique comme ça

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

et oui quand je l'appelle (de l'Activité), c'est nul ...

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

Ce doit être quelque chose que je fais très mal, mais je ne sais pas ce que c'est




Vous pouvez utiliser onAttach ou si vous ne voulez pas mettre onAttach partout, vous pouvez mettre une méthode qui retourne ApplicationContext sur la classe App principale:

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

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

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

Après cela, vous pouvez le réutiliser partout dans votre projet, comme ceci:

App.getContext().getString(id)

S'il vous plaît laissez-moi savoir si cela ne fonctionne pas pour vous.




Cela s'est produit lorsque vous appelez getActivity() dans un autre thread qui s'est terminé après la suppression du fragment. Le cas typique consiste à appeler getActivity() (par exemple pour un Toast ) lorsqu'une requête HTTP est terminée (dans onResponse par exemple).

Pour éviter cela, vous pouvez définir un nom de champ mActivity et l'utiliser à la place de getActivity() . Ce champ peut être initialisé dans la méthode onAttach () de Fragment comme suit:

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

Dans mes projets, je définis généralement une classe de base pour tous mes fragments avec cette fonctionnalité:

public abstract class BaseFragment extends Fragment {

    protected FragmentActivity mActivity;

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

Codage heureux,




Depuis le niveau d'API Android 23, onAttach (activité d'activité) est obsolète. Vous devez utiliser onAttach (Context context). http://developer.android.com/reference/android/app/Fragment.html#onAttach(android.app.Activity)

L'activité est un contexte, donc si vous pouvez simplement vérifier que le contexte est une activité, lancez-la si nécessaire.

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

    Activity a;

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

}



Faites comme suit. Je pense que cela vous sera utile.

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

}



Les autres réponses qui suggèrent de garder une référence à l'activité dans onAttach ne font que suggérer un bandaid au vrai problème. Lorsque getActivity renvoie null, cela signifie que le fragment n'est pas associé à l'activité. Le plus souvent, cela se produit lorsque l'activité a disparu en raison de la rotation ou de la fin de l'activité, mais le fragment a une sorte d'écouteur de rappel. Lorsque l'auditeur est appelé si vous avez besoin de faire quelque chose avec l'activité mais que l'activité est partie, vous ne pouvez pas faire grand-chose. Dans votre code, vous devriez simplement vérifier getActivity() != null et si ce n'est pas le cas, ne faites rien. Si vous conservez une référence à l'activité annulée, vous empêchez l'activité d'être collectée. Toutes les choses de l'interface utilisateur que vous pourriez essayer ne seront pas vues par l'utilisateur. Je peux imaginer des situations où, dans l'écouteur de rappel, vous pourriez avoir un contexte pour quelque chose de non lié à l'interface utilisateur, dans ce cas, il est probablement plus logique d'obtenir le contexte de l'application. Notez que la seule raison pour laquelle l'astuce onAttach n'est pas une grosse onAttach mémoire est que normalement après l'exécution de l'écouteur de callback, il ne sera plus nécessaire et pourra être récupéré avec le fragment, toutes ses vues et son contexte d'activité. Si vous setRetainInstance(true) , la probabilité de fuite de mémoire est plus importante, car le champ Activity sera également conservé, mais après la rotation, il peut s'agir de l'activité précédente et non de l'actuelle.




PJL a raison. J'ai utilisé sa suggestion et c'est ce que j'ai fait:

  1. variables globales définies pour le fragment:

    private final Object attachingActivityLock = new Object();

    private boolean syncVariable = false;

  2. mis en œuvre

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

3. J'ai enveloppé ma fonction, où j'ai besoin d'appeler getActivity (), dans le thread, car si elle s'exécutait sur le thread principal, je bloquerais le thread avec l'étape 4. et onAttach () ne serait jamais appelé.

    Thread processImage = new Thread(new Runnable() {

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

4. dans ma fonction où je dois appeler getActivity (), je l'utilise (avant l'appel getActivity ())

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

Si vous avez des mises à jour de l'interface utilisateur, n'oubliez pas de les exécuter sur le thread de l'interface utilisateur. J'ai besoin de mettre à jour ImgeView donc j'ai fait:

image.post(new Runnable() {

    @Override
    public void run() {
        image.setImageBitmap(imageToShow);
    }
});



Où appelez-vous cette fonction? Si vous l'appelez dans le constructeur de Fragment , il renverra null .

Appelez simplement getActivity() lorsque la méthode onCreateView() est exécutée.




Meilleur et solide moyen:

FragmentActivity activity = (FragmentActivity) getActivity();

activity.finish();



J'utilise OkHttp et je viens de faire face à ce problème.

Pour la première partie, @thucnguyen était sur la bonne voie .

Cela s'est produit lorsque vous appelez getActivity () dans un autre thread qui s'est terminé après la suppression du fragment. Le cas typique consiste à appeler getActivity () (par exemple pour un Toast) lorsqu'une requête HTTP est terminée (dans onResponse par exemple).

Certains appels HTTP étaient en cours d'exécution même après la fermeture de l'activité (car le traitement d'une requête HTTP peut prendre un certain temps). J'ai ensuite, à travers le HttpCallback essayé de mettre à jour certains champs Fragment et obtenu une exception null en essayant de getActivity() .

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

IMO la solution est d' empêcher les rappels de se produire lorsque le fragment n'est plus en vie (et ce n'est pas seulement avec Okhttp).

Le correctif: Prévention.

Si vous regardez le cycle de vie des fragments (plus d'infos here ), vous remarquerez qu'il existe des onAttach(Context context) et onDetach() . Ceux-ci sont appelés après que le fragment appartient à une activité et juste avant d'arrêter de l'être respectivement.

Cela signifie que nous pouvons empêcher ce rappel de se produire en le contrôlant dans la méthode 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();
    }
}



Ceux qui ont encore le problème avec onAttach (Activité Activité), Son juste changé en Contexte -

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

Dans la plupart des cas, sauvegarder le contexte vous suffira. Par exemple, si vous voulez faire getResources (), vous pouvez le faire directement à partir du contexte. Si vous avez encore besoin de faire le contexte dans votre activité, faites-le -

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

Comme suggéré par l'utilisateur1868713.




final FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.hide(currentFragment);
ft.add(R.id.content_frame, newFragment.newInstance(context), "Profile");
ft.addToBackStack(null);
ft.commit();




android android-fragments android-activity null android-context