getActivity () devuelve nulo en la función Fragment


191

Tengo un fragmento (F1) con un método público como este

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

y sí, cuando lo llamo (de la Actividad), es nulo ...

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

Debe ser algo que estoy haciendo muy mal, pero no sé qué es eso


No estoy seguro de si solo hubo un error cuando lo pegó en esta publicación, pero necesita paréntesis después getActivity(). Además, ¿cómo estás instanciando el fragmento? ¿Lo tienes en tu layout.xml?
CaseyB

¿A dónde pertenece el segundo fragmento de código? ¿Al método oncreate () de la Actividad? ¿Y ya has llamado a setContentView ()?
Franziskus Karsunke

R.id.upperPar es un elemento en el diseño, por lo que se supone que debe reemplazarse con el fragmento, pero ese no es mi problema. No entiendo por qué me pongo nulo cuando llamo getActivity () en métodos de fragmentos personalizados, digamos en el método onActivityCreated getActivity es la actividad real no nula
Lukap

el problema no está en los diseños, la aplicación funciona bien, pero ¿por qué me sale nulo para el getActivity, por cierto todos los elementos incluyendo el fragmento que aparece así que no debería cuestiones aquí?
Lukap

1
Debe llamar a este método: f1.asd (); en el método onActivityCreated que se anulará en su clase de fragmento.
Namrata Bagerwal

Respuestas:


164

commit programa la transacción, es decir, no sucede de inmediato, pero está programada como trabajo en el hilo principal la próxima vez que el hilo principal esté listo.

Sugeriría agregar un

onAttach(Activity activity)

método Fragmenty poner un punto de interrupción y ver cuándo se llama en relación con su llamada a asd(). Verá que se llama después del método donde realiza la llamada a las asd()salidas. La onAttachllamada es donde Fragmentse adjunta a su actividad y desde este punto getActivity()devolverá no nulo (nb también hay una onDetach()llamada).


55
No entendí cómo puedes resolver tu problema. Si mi getActivity () no está listo, ¿cómo puedo obtener la referencia del objeto FragmentActivity?
CeccoCQ

2
@Vivek No sé exactamente lo que quieres lograr. Si necesita el fragmento para mostrar un diálogo inmediatamente a continuación, hacer que haga lo que tiene que hacer en la creación, por ejemplo, en sus onCreateViewo onActivityCreatedmétodos. Me pregunto por qué se debe llamar a asd () cuando lo hace en la publicación de preguntas.
PJL

3
onAttach en desuso
abbasalim

66
onAttach (Activity mActivity) parece estar depreciado ... cualquier solución para esto
ashish.n

44
API 24 introducidocommitNow()
Nicolas

92

Lo mejor para deshacerse de esto es mantener la referencia de actividad cuando se llama a onAttach y usar la referencia de actividad donde sea necesario, por ejemplo

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

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

34
¿Deberíamos establecer mActivity = null onDetach ()?
Oliver Pearmain el

55
@OliverPearmain si lo harás en onDetach (), entonces no habrá ganancias. Tienes que anularlo en onDestory (). Además, debes mantenerlo en WeakRefernce.
Kirill Popov

Lo estoy anulando en ambos onDestroy()y onDetach()porque onDestroy()no se garantiza que se llame.
Mohammed Ali

8
¿Estamos filtrando el Activitysi no lo anulamos onDestroy()?
Mohammed Ali

3
De acuerdo con developer.android.com/intl/zh-tw/guide/components/… , se llama a onAttach () antes de llamar a onCreateView (). Pero todavía recibo una NullPointerException mientras llamo a getActivity () en onCreateView (). ¿Cómo pudo pasar eso?
Kimi Chiu

81

Esto sucedió cuando llamas getActivity()a otro hilo que terminó después de que el fragmento ha sido eliminado. El caso típico es llamar getActivity()(por ejemplo, a Toast) cuando finaliza una solicitud HTTP ( onResponsepor ejemplo, en).

Para evitar esto, puede definir un nombre de campo mActivityy usarlo en lugar de getActivity(). Este campo se puede inicializar en el método onAttach () de Fragment de la siguiente manera:

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

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

En mis proyectos, generalmente defino una clase base para todos mis Fragmentos con esta característica:

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

Feliz codificación,


19
¿estableceremos mActividad = nulo; en onDetach ()?
Bharat Dodeja

Thucnguyen, ¿qué tal si declaramos mActividad estática para la aplicación de actividad única?
iamthevoid

No veo ninguna razón para acceder Activitydesde otro hilo en absoluto. De todos modos, no puedes hacer nada con él, ni siquiera mostrar un brindis. Por lo tanto, primero debe transferir el trabajo al hilo principal o no usar Actividad en absoluto.
Dzmitry Lazerka

44
@BharatDodeja ¿deberíamos configurar mActivity = null onDetach? ¿Te diste cuenta?
SLearner

55
Esto se filtrará sin anular la actividad.
Darek Deoniziak

30

Las otras respuestas que sugieren mantener una referencia a la actividad en onAttach solo sugieren una venda al problema real. Cuando getActivity devuelve nulo, significa que el Fragmento no está adjunto a la Actividad. Lo más común es que esto ocurra cuando la Actividad se ha ido debido a la rotación o al final de la Actividad, pero el Fragmento todavía tiene algún tipo de escucha de devolución de llamada registrada. Cuando se llama al oyente si necesita hacer algo con la Actividad pero la Actividad se ha ido, no hay mucho que pueda hacer. En su código solo debe verificargetActivity() != nully si no está allí, no hagas nada. Si mantiene una referencia a la Actividad que se ha ido, está evitando que la Actividad se recolecte basura. El usuario no verá las cosas de la interfaz de usuario que intente hacer. Me imagino algunas situaciones en las que en el oyente de devolución de llamada es posible que desee tener un contexto para algo no relacionado con la interfaz de usuario, en esos casos, probablemente tenga más sentido obtener el contexto de la aplicación. Tenga en cuenta que la única razón por la que el onAttachtruco no es una gran pérdida de memoria es porque, normalmente, después de que se ejecuta la escucha de devolución de llamada, ya no será necesaria y se puede recolectar basura junto con el Fragmento, todas sus Vistas y el contexto de Actividad. Si tusetRetainInstance(true) existe una mayor posibilidad de una pérdida de memoria porque el campo Actividad también se retendrá, pero después de la rotación podría ser la Actividad anterior y no la actual.


1
Este es exactamente mi problema. Tengo un fragmento que realiza un proceso -> luego se muestra un anuncio -> y luego el proceso continúa. En algunos dispositivos después de regresar del anuncio (a través de un oyente a los eventos del anuncio) getActivity () es nulo. Pero necesito continuar haciendo la otra parte del trabajo para terminar el trabajo. ¿Quieres decir que no hay solución para esto?
Notbad

Esto es exactamente lo que estoy enfrentando. Tengo una interfaz de actividad en un fragmento donde hago algunas cosas de facturación. Una vez realizado el pago, quiero usar la interfaz para hacer algo, pero la interfaz ha sido nula.
Freddie el

Esta parece ser la respuesta general correcta a cientos de preguntas SO para este tema.
Manuel

La mejor respuesta. Hay tantas soluciones de bandaid para Android en SO.
maxbeaudoin

Entonces, si quiero hacer alguna operación, ¿cómo puedo ejecutar después de que getActivity () esté disponible (si es que lo está)?
Sreekanth Karumanaghat

17

Desde el nivel 23 de la API de Android, onAttach (Actividad de actividad) ha quedado en desuso. Debe usar onAttach (contexto de contexto). http://developer.android.com/reference/android/app/Fragment.html#onAttach(android.app.Activity)

La actividad es un contexto, por lo que si simplemente puede verificar el contexto, es una actividad y emitirla si es necesario.

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

    Activity a;

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

}

Cómo usar eso
ARR.s

10

PJL tiene razón. He usado su sugerencia y esto es lo que he hecho:

  1. Variables globales definidas para el fragmento:

    private final Object attachingActivityLock = new Object();

    private boolean syncVariable = false;

  2. implementado

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

3) Terminé mi función, donde necesito llamar a getActivity (), en thread, porque si se ejecutara en thread principal, bloquearía el thread con el paso 4. y onAttach () nunca sería llamado.

    Thread processImage = new Thread(new Runnable() {

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

4) en mi función donde necesito llamar a getActivity (), uso esto (antes de la llamada getActivity ())

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

Si tiene algunas actualizaciones de la interfaz de usuario, recuerde ejecutarlas en el hilo de la interfaz de usuario. Necesito actualizar ImgeView, así que hice:

image.post(new Runnable() {

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

7

El orden en que se llaman las devoluciones de llamada después de commit ():

  1. Cualquiera sea el método que llame manualmente justo después de commit ()
  2. onAttach ()
  3. onCreateView ()
  4. onActivityCreated ()

Necesitaba hacer un trabajo que involucrara algunas vistas, por lo que onAttach () no funcionó para mí; se estrelló. Así que moví parte de mi código que estaba configurando algunos parámetros dentro de un método llamado justo después de commit () (1.), luego la otra parte del código que manejaba la vista dentro de onCreateView () (3.).


3

Estoy usando OkHttp y acabo de enfrentar este problema.


Para la primera parte, @thucnguyen estaba en el camino correcto .

Esto sucedió cuando llamas a getActivity () en otro hilo que terminó después de que el fragmento ha sido eliminado. El caso típico es llamar a getActivity () (por ejemplo, para un Toast) cuando finaliza una solicitud HTTP (en onResponse, por ejemplo).

Algunas llamadas HTTP se estaban ejecutando incluso después de que se cerró la actividad (porque puede llevar un tiempo completar una solicitud HTTP). Luego, a través del HttpCallbackintento de actualizar algunos campos Fragmento y obtuve una nullexcepción al intentarlo getActivity().

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

En mi opinión, la solución es evitar que se produzcan devoluciones de llamada cuando el fragmento ya no está vivo (y eso no es solo con Okhttp).

La solución: prevención.

Si usted tiene una mirada en el ciclo de vida del fragmento (más información aquí ), usted notará que hay onAttach(Context context)y onDetach()métodos. Estos se llaman después de que el Fragmento pertenece a una actividad y justo antes de dejar de serlo, respectivamente.

Eso significa que podemos evitar que esa devolución de llamada suceda al controlarla en el onDetachmétodo.

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

2

¿Dónde llamas a esta función? Si lo llama en el constructor de Fragment, volverá null.

Simplemente llame getActivity()cuando onCreateView()se ejecute el método .


1

Haz lo siguiente. Creo que te será útil.

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

Aquellos que todavía tienen el problema con onAttach (Actividad de actividad), simplemente cambió a Contexto:

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

En la mayoría de los casos, guardar el contexto será suficiente para usted; por ejemplo, si desea obtener getResources (), puede hacerlo directamente desde el contexto. Si aún necesita incluir el contexto en su Actividad, hágalo:

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

Según lo sugerido por el usuario1868713.


0

Puede usar onAttach o si no desea poner onAttach en todas partes, puede poner un método que devuelva ApplicationContext en la clase principal de la aplicación:

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

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

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

Después de eso, puede reutilizarlo en todas partes en todo su proyecto, de esta manera:

App.getContext().getString(id)

Por favor, avíseme si esto no funciona para usted.


0

Otra buena solución sería usar LiveData de Android con la arquitectura MVVM. Definiría un objeto LiveData dentro de su ViewModel y lo observaría en su fragmento, y cuando se cambie el valor de LiveData, notificará a su observador (fragmento en este caso) solo si su fragmento está en estado activo, por lo que se garantizará que haría que su IU funcione y acceda a la actividad solo cuando su fragmento esté en estado activo. Esta es una ventaja que viene con LiveData

Por supuesto, cuando se hizo esta pregunta por primera vez, no había LiveData. Dejo esta respuesta aquí porque, como veo, todavía existe este problema y podría ser útil para alguien.


Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.