199 votes

Android. Fragment getActivity () renvoie parfois null

Les développeurs de la console de rapports d'erreur j'ai parfois des rapports contenant des NPE question. Je ne comprends pas quel est le problème avec mon code. Sur émulateur et mon appareil application fonctionne bien, sans forcecloses, cependant, certains utilisateurs d'obtenir NullPointerException fragment de classe lorsque les appels getActivity() la méthode.

L'activité

pulic class MyActivity extends FragmentActivity{

private ViewPager pager; 
private TitlePageIndicator indicator;
private TabsAdapter adapter;

 @Override
public void onCreate(Bundle savedInstanceState) {

    pager = (ViewPager) findViewById(R.id.pager);;
    indicator = (TitlePageIndicator) findViewById(R.id.indicator);
    adapter = new TabsAdapter(getSupportFragmentManager(), false);

    adapter.addFragment(new FirstFragment());
    adapter.addFragment(new SecondFragment());
    indicator.notifyDataSetChanged();
    adapter.notifyDataSetChanged();

    // push first task
    FirstTask firstTask = new FirstTask(MyActivity.this);
    // set first fragment as listener
    firstTask.setTaskListener((TaskListener) adapter.getItem(0));
    firstTask.execute();

}

 indicator.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageSelected(int position) {
            Fragment currentFragment = adapter.getItem(position);
            ((Taskable) currentFragment).executeTask();
        }

        @Override
        public void onPageScrolled(int i, float v, int i1) {}

        @Override
        public void onPageScrollStateChanged(int i) {}
 });

}

La classe AsyncTask

public class FirstTask extends AsyncTask{

private TaskListener taskListener;

...

@Override
protected void onPostExecute(T result) {
   ...  
   taskListener.onTaskComplete(result);
}

}

Fragment de classe

public class FirstFragment extends Fragment immplements Taskable, TaskListener{


public FirstFragment() {
}


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    return inflater.inflate(R.layout.first_view, container, false);;
}

@Override
public void executeTask() {
    FirstTask firstTask = new FirstTask(MyActivity.this);
    firstTask.setTaskListener(this);
    firstTask.execute();
}


@Override
public void onTaskComplete(T result) {
    // NPE is here 
    Resources res = getActivity().getResources();
    ...
}

}

Peut-être cette erreur se produit lorsque les applications ont repris de fond. Dans ce cas, comment je dois gérer cette situation?

125voto

Georgy Gobozov Points 4814

Il semble que j'ai trouvé une solution à mon problème. Très bonnes explications sont données ici et ici. Voici mon exemple:

pulic class MyActivity extends FragmentActivity{

private ViewPager pager; 
private TitlePageIndicator indicator;
private TabsAdapter adapter;
private Bundle savedInstanceState;

 @Override
public void onCreate(Bundle savedInstanceState) {

    .... 
    this.savedInstanceState = savedInstanceState;
    pager = (ViewPager) findViewById(R.id.pager);;
    indicator = (TitlePageIndicator) findViewById(R.id.indicator);
    adapter = new TabsAdapter(getSupportFragmentManager(), false);

    if (savedInstanceState == null){    
        adapter.addFragment(new FirstFragment());
        adapter.addFragment(new SecondFragment());
    }else{
        Integer  count  = savedInstanceState.getInt("tabsCount");
        String[] titles = savedInstanceState.getStringArray("titles");
        for (int i = 0; i < count; i++){
            adapter.addFragment(getFragment(i), titles[i]);
        }
    }


    indicator.notifyDataSetChanged();
    adapter.notifyDataSetChanged();

    // push first task
    FirstTask firstTask = new FirstTask(MyActivity.this);
    // set first fragment as listener
    firstTask.setTaskListener((TaskListener) getFragment(0));
    firstTask.execute();

}

private Fragment getFragment(int position){
     return savedInstanceState == null ? adapter.getItem(position) : getSupportFragmentManager().findFragmentByTag(getFragmentTag(position));
}

private String getFragmentTag(int position) {
    return "android:switcher:" + R.id.pager + ":" + position;
}

 @Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putInt("tabsCount",      adapter.getCount());
    outState.putStringArray("titles", adapter.getTitles().toArray(new String[0]));
}

 indicator.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageSelected(int position) {
            Fragment currentFragment = adapter.getItem(position);
            ((Taskable) currentFragment).executeTask();
        }

        @Override
        public void onPageScrolled(int i, float v, int i1) {}

        @Override
        public void onPageScrollStateChanged(int i) {}
 });

L'idée principale de ce code est que, lors de l'exécution de votre application normalement, vous créez de nouveaux fragments et de les transmettre à la carte. Lorsque vous reprenez votre demande fragment manager a déjà ce fragment de l'instance et vous avez besoin pour obtenir à partir de ce fragment de manager et de le transmettre à la carte.

Mise à JOUR

Aussi, il est une bonne pratique lors de l'utilisation de fragments de vérifier isAdded avant getActivity() est appelée. Cela permet d'éviter une exception de pointeur null lorsque le fragment est détaché de l'activité. Par exemple, une activité peut contenir un fragment qui pousse un async tâche. Lorsque la tâche est terminée, le onTaskComplete auditeur est appelé.

@Override
public void onTaskComplete(List<Feed> result) {

    progress.setVisibility(View.GONE);
    progress.setIndeterminate(false);
    list.setVisibility(View.VISIBLE);

    if (isAdded()) {

        adapter = new FeedAdapter(getActivity(), R.layout.feed_item, result);
        list.setAdapter(adapter);
        adapter.notifyDataSetChanged();
    }

}

Si l'on ouvre le fragment, pousser une tâche, puis appuyez rapidement sur retour pour revenir à une activité précédente, lorsque la tâche est terminée, il va tenter d'accéder à l'activité, dans onPostExecute() par un appel à la getActivity() la méthode. Si l'activité est déjà détaché et cette case n'est pas là:

if (isAdded()) 

ensuite, l'application se bloque.

19voto

Pawan M Points 4396

Le meilleur pour se débarrasser de cela est de garder ACTIVITE référence lorsque onAttach est appelé et utiliser la référence de l’activité dans la mesure nécessaire, pour par exemple

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X