getActivity () retorna nulo na função Fragment

191

Eu tenho um fragmento (F1) com um método público como este

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

e sim quando eu chamo (da Activity), é nulo ...

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

Devo estar fazendo algo muito errado, mas não sei o que é isso

Lukap
fonte
Não tenho certeza se houve apenas um erro ao colá-lo nesta postagem, mas você precisa de parênteses depois getActivity(). Além disso, como você está instanciando o fragmento? Você o possui em seu layout.xml?
precisa saber é o seguinte
Onde o segundo fragmento de código pertence? Para o método oncreate () da atividade? E você já chamou setContentView ()?
Franziskus Karsunke
R.id.upperPar é um elemento no layout, portanto deve ser substituído pelo fragmento, mas esse não é o meu problema. Eu não entendo por que eu recebo nula quando eu chamo getActivity () em métodos de fragmentos de costume, vamos dizer em onActivityCreated método getActivity é a atividade real não nulo
Lukap
o problema não está nos layouts, o aplicativo funciona bem, mas por que eu recebo nulo para o getActivity, btw todos os elementos, incluindo o fragmento ele é processado como não deveria questões aqui?
Lukap
1
Você deve chamar este método: f1.asd (); no método onActivityCreated, que deve ser substituído na sua classe de fragmento.
Namrata Bagerwal

Respostas:

164

commit agenda a transação, ou seja, isso não acontece imediatamente, mas é agendado como trabalho no encadeamento principal na próxima vez que o encadeamento principal estiver pronto.

Eu sugiro adicionar um

onAttach(Activity activity)

método para o seu Fragmente colocando um ponto de interrupção nele e vendo quando é chamado em relação à sua chamada para asd(). Você verá que ele é chamado após o método em que você faz a chamada para asd()saídas. A onAttachchamada é onde Fragmentestá anexado à sua atividade e a partir deste ponto getActivity()retornará não nulo (nb também há uma onDetach()chamada).

PJL
fonte
5
Não entendi como você pode resolver seu problema. Se meu getActivity () não estiver pronto, como posso obter a referência do objeto FragmentActivity?
CeccoCQ
2
@ Vivive Eu não sei bem o que você quer alcançar. Se você precisa do fragmento para exibir um diálogo imediatamente depois tê-lo fazer o que precisa fazer na criação, por exemplo, em seus onCreateViewou onActivityCreatedmétodos. Eu questiono por que asd () precisa ser chamado quando isso ocorre na publicação de perguntas.
PJL
3
onAttach descontinuado
abbasalim 18/08/16
6
onAttach (Activity mActivity) parece estar depreciado. Qualquer solução alternativa para isso
ashish.n
4
API 24 introduzidacommitNow()
Nicolas
92

O melhor para se livrar disso é manter a referência da atividade quando onAttach for chamado e usar a referência da atividade sempre que necessário, por exemplo

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

@Override
public void onDetach() {
    super.onDetach();
    mContext = null;
}
Pawan Maheshwari
fonte
34
Devemos definir mActivity = null onDetach ()?
Oliver Pearmain
5
@ OliverPearmain, se você fizer isso em onDetach (), não haverá lucro. Você precisa anulá-lo em onDestory (). Além disso, você deve segurá-lo no WeakRefernce.
Kirill Popov
Estou anulando-o em ambos onDestroy()e onDetach()porque onDestroy()não é garantido que seja chamado.
Mohammed Ali
8
Estamos vazando Activityse não o anularmos onDestroy()?
Mohammed Ali
2
De acordo com developer.android.com/intl/zh-tw/guide/components/… , o onAttach () é chamado antes de chamar o onCreateView (). Mas ainda recebo uma NullPointerException enquanto chamo getActivity () em onCreateView (). Como isso pôde acontecer?
Kimi Chiu
81

Isso aconteceu quando você chama getActivity()outro segmento que terminou após a remoção do fragmento. O caso típico é chamar getActivity()(por exemplo, a Toast) quando uma solicitação HTTP é concluída ( onResponsepor exemplo).

Para evitar isso, você pode definir um nome de campo mActivitye usá-lo em vez de getActivity(). Este campo pode ser inicializado no método onAttach () do Fragment da seguinte maneira:

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

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

Nos meus projetos, geralmente defino uma classe base para todos os meus fragmentos com este recurso:

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 codificação,

thucnguyen
fonte
19
devemos definir mActivity = null; em onDetach ()?
Bharat Dodeja
thucnguyen, que tal declarar a mActivity estática para o aplicativo de atividade única?
precisa saber é o seguinte
Não vejo motivo para acessar Activityde outro segmento. Você não pode fazer nada com isso de qualquer maneira, nem mesmo mostrar um brinde. Portanto, você deve transferir o trabalho para o encadeamento principal primeiro ou não usar a Atividade.
Dzmitry Lazerka
4
@BharatDodeja devemos definir mActivity = null noDetach? Você descobriu?
SLearner
5
Isso vazará sem anular a atividade.
Darek Deoniziak
30

As outras respostas que sugerem manter uma referência à atividade no onAttach estão apenas sugerindo uma bandaid para o problema real. Quando getActivity retorna nulo, significa que o Fragmento não está anexado à Atividade. Geralmente, isso acontece quando a atividade desaparece devido à rotação ou à conclusão da atividade, mas o fragmento ainda possui algum tipo de ouvinte de retorno de chamada registrado. Quando o ouvinte é chamado, se você precisar fazer algo com a Atividade, mas a Atividade se foi, não há muito o que fazer. No seu código, você deve apenas verificargetActivity() != nulle se não estiver lá, não faça nada. Se você mantiver uma referência à Atividade que se foi, estará impedindo a coleta de lixo da Atividade. Quaisquer coisas da interface do usuário que você tente fazer não serão vistas pelo usuário. Posso imaginar algumas situações em que, no ouvinte de retorno de chamada, você pode querer ter um contexto para algo não relacionado à interface do usuário; nesses casos, provavelmente faz mais sentido obter o contexto do aplicativo. Observe que o único motivo pelo qualonAttach truque não é um grande vazamento de memória é porque, normalmente, depois que o ouvinte de retorno de chamada é executado, ele não é mais necessário e pode ser coletado como lixo junto com o Fragmento, todas as suas visões e o contexto de Atividade. Se vocêssetRetainInstance(true) há uma chance maior de vazamento de memória porque o campo Atividade também será retido, mas após a rotação que poderia ser a Atividade anterior, não a atual.

miguel
fonte
1
Este é exatamente o meu problema. Eu tenho um fragmento que faz um processo -> então um anúncio é exibido -> e então o processo continua. Em alguns dispositivos, após retornar do anúncio (por meio de um ouvinte para os eventos do anúncio), getActivity () é nulo. Mas preciso continuar fazendo a outra parte do trabalho para concluir o trabalho. Você quer dizer que não há solução para isso?
Notbad
É exatamente isso que estou enfrentando. Eu tenho uma interface de atividade em um fragmento onde faço algumas coisas de cobrança. Após o pagamento, desejo usar a interface para fazer alguma coisa, mas a interface foi nula.
Freddie
Essa parece ser a resposta geral correta para centenas de perguntas sobre o SO deste tópico.
Manuel
Melhor resposta. Existem tantas soluções bandaid para Android no SO.
maxbeaudoin
Portanto, se eu quiser fazer alguma operação, como executar depois que o getActivity () estiver disponível (se houver)?
Sreekanth Karumanaghat
17

Desde o nível 23 da API do Android, onAttach (atividade de atividade) foi preterido. Você precisa usar onAttach (contexto de contexto). http://developer.android.com/reference/android/app/Fragment.html#onAttach(android.app.Activity)

Atividade é um contexto, portanto, se você pode simplesmente verificar se o contexto é uma Atividade e transmiti-lo, se necessário.

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

    Activity a;

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

}
Sachin
fonte
Como usar isso
ARR.s
10

PJL está certo. Eu usei sua sugestão e é isso que eu fiz:

  1. variáveis ​​globais definidas para o 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) Eu encerrei minha função, onde eu preciso chamar getActivity (), no thread, porque se ele fosse executado no thread principal, eu bloqueava o thread com a etapa 4. e onAttach () nunca seria chamado.

    Thread processImage = new Thread(new Runnable() {

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

4) na minha função onde eu preciso chamar getActivity (), eu uso isso (antes da chamada getActivity ())

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

Se você tiver algumas atualizações da interface do usuário, lembre-se de executá-las no thread da interface do usuário. Preciso atualizar o ImgeView, então fiz:

image.post(new Runnable() {

    @Override
    public void run() {
        image.setImageBitmap(imageToShow);
    }
});
zajac.m2
fonte
7

A ordem na qual os retornos de chamada são chamados após commit ():

  1. Qualquer método que você chamar manualmente logo após o commit ()
  2. onAttach ()
  3. onCreateView ()
  4. onActivityCreated ()

Eu precisava fazer algum trabalho que envolvesse algumas Views, portanto onAttach () não funcionou para mim; isso quebrou. Então mudei parte do meu código que estava definindo alguns parâmetros dentro de um método chamado logo após commit () (1.), depois a outra parte do código que manipulava a exibição dentro de onCreateView () (3.).

Bogdan Zurac
fonte
3

Estou usando o OkHttp e acabei de enfrentar esse problema.


Para a primeira parte, @thucnguyen estava no caminho certo .

Isso aconteceu quando você chama getActivity () em outro segmento que terminou após a remoção do fragmento. O caso típico é chamar getActivity () (por exemplo, para um Toast) quando uma solicitação HTTP terminar (no onResponse, por exemplo).

Algumas chamadas HTTP estavam sendo executadas mesmo após o fechamento da atividade (porque pode demorar um pouco para que uma solicitação HTTP seja concluída). Eu então, através do HttpCallbacktentou atualizar alguns campos de fragmento e tem uma nullexceção ao tentar getActivity().

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

Na IMO, a solução é impedir que retornos de chamada ocorram quando o fragmento não estiver mais ativo (e isso não ocorre apenas com o Okhttp).

A correção: prevenção.

Se você der uma olhada no ciclo de vida do fragmento (mais informações aqui ), notará que há onAttach(Context context)eonDetach() métodos. Eles são chamados após o Fragmento pertencer a uma atividade e pouco antes de deixarem de ser, respectivamente.

Isso significa que podemos impedir que esse retorno de chamada ocorra controlando-o no 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();
    }
}
zurfyx
fonte
2

Onde você chama essa função? Se você chamá-lo no construtor de Fragment, ele retornará null.

Basta ligar getActivity()quando o método onCreateView()for executado.

Phạm Lam
fonte
1

Faça o seguinte. Eu acho que será útil para você.

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

}
Vinil Chandran
fonte
1

Aqueles que ainda têm problemas com onAttach (atividade de atividade), acabam de mudar para Contexto -

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

Na maioria dos casos, salvar o contexto será suficiente para você - por exemplo, se você quiser fazer getResources (), poderá fazê-lo diretamente do contexto. Se você ainda precisar transformar o contexto em sua Atividade, faça isso -

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

Conforme sugerido pelo usuário1868713.

Shai
fonte
0

Você pode usar onAttach ou se não quiser colocar onAttach em qualquer lugar, poderá colocar um método que retorne ApplicationContext na classe principal do aplicativo:

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

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

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

Depois disso, você pode reutilizá-lo em qualquer lugar do seu projeto, assim:

App.getContext().getString(id)

Informe-me se isso não funcionar para você.

surga
fonte
0

Outra boa solução seria usar o LiveData do Android com arquitetura MVVM. Você definiria um objeto LiveData dentro do seu ViewModel e o observaria no seu fragmento. Quando o valor do LiveData for alterado, ele notificará o observador (fragmento neste caso) apenas se o fragmento estiver no estado ativo, garantindo assim que você faria sua interface do usuário funcionar e acessaria a atividade somente quando seu fragmento estiver no estado ativo. Essa é uma vantagem que vem com o LiveData

Obviamente, quando essa pergunta foi feita pela primeira vez, não havia LiveData. Estou deixando essa resposta aqui porque, como vejo, ainda existe esse problema e pode ser útil para alguém.

Serdar Samancıoğlu
fonte
0

Chame o método getActivity () dentro do onActivityCreated ()

MuM6oJuM6o
fonte