0

Ma structure de projet ressemble à ceci:ViewPager se bloque sur le changement d'orientation - fragments pile et provoquent OutOfMemoryException

MainActivity-> PagerFragment-> PagerViewAdapter qui détient plusieurs PagerImageFragments

Je reçois un OutOfMemoryException après quelques de l'orientation change mais le pager affiche des bitmaps "efficacement" (j'ai utilisé AsyncTask/LruCache). Ensuite, j'ai remarqué que la mémoire utilisée par l'application se multiplie après chaque changement d'orientation et à partir des journaux, je peux voir que onCreate() dans PagerFragment est appelée deux fois à chaque fois. Je pense qu'à cause de cela, mes fragments sont recréés et empilés, ce qui cause le problème. Comment puis-je empêcher cela de se produire?

J'ai essayé la mise

@Override 
protected void onSaveInstanceState(final Bundle outState) { 
// super.onSaveInstanceState(outState); 
} 

Ce qui n'a rien fait pour moi. Alors j'ai essayé:

setRetainInstance(true); 

qui a rendu mon PagerFragment recréent une seule fois, mais c'est toujours pas ce que je voulais.

EDIT: J'ai légèrement modifié MainActivity de sorte qu'il ne fait que l'initialisation s'il n'y a pas d'instance enregistrée et supprime tous les fragments au démarrage d'un nouveau. J'ai supprimé setRetainInstance (true). Je peux maintenant changer d'orientation plusieurs fois (cela se terminera toujours par une exception de MOO, mais cela prend beaucoup plus de temps pour le déclencher). Mais maintenant il y a un autre problème - si je laisse PagerFragment pour un autre fragment et reviens, il se bloque.


MainActivity:

public class MainActivity extends AppCompatActivity { 
private final String TAG = "LOG"; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.activity_main); 

    if (savedInstanceState==null) { 
    ... 
     FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); 
     PagerFragment fragment = new PagerFragment(); 
     fragmentTransaction.add(R.id.fragment_container, fragment); 
     fragmentTransaction.commit(); 
    ... 
     switchContent(new PagerFragment()); 
    } 
} 

    public void switchContent(Fragment fragment) { 
    FragmentManager fm = getSupportFragmentManager(); 
    List<Fragment> fragmentList = fm.getFragments(); 
    if (fragmentList != null) { 
     for (Fragment frag : fragmentList) { 
      Log.d(TAG, "switchContent: destroying "+frag); 
      fm.beginTransaction() 
       .remove(frag).commit(); 
     } 
    } 

    fm.beginTransaction() 
     .replace(R.id.fragment_container, fragment) 
     .commit(); 
} 

intérieur PagerFragment:

PagerAdapter adapter = new PagerViewAdapter(getFragmentManager(), pdfFactory.getPageCount()); 
    ViewPager pager = (ViewPager) getActivity().findViewById(R.id.pager); 
    pager.setAdapter(adapter); 
    pager.setPageMargin((int) getResources().getDimension(R.dimen.activity_horizontal_margin)); 
    pager.setCurrentItem(position); 
    pager.setOffscreenPageLimit(2); 

PagerViewAdapter:

public class PagerViewAdapter extends FragmentStatePagerAdapter { 
    private final int size; 

    public PagerViewAdapter(FragmentManager fm, int size) { 
     super(fm); 
     this.size = size; 
    } 

    @Override 
    public int getCount() { 
     return size; 
    } 

    @Override 
    public Fragment getItem(int position) { 
     return PagerImageFragment.newInstance(position); 
    } 

} 

PagerImageFragment:

public class PagerImageFragment extends Fragment { 
private PhotoView photoView; 
int position; 
private MyCache<Integer, Bitmap> myCache; 

public static PagerImageFragment newInstance(Integer pos) { 
    final PagerImageFragment f = new PagerImageFragment(); 
    final Bundle args = new Bundle(); 
    args.putInt("position", pos); 
    f.setArguments(args); 

    Log.d("DISPLAY", "Bundling..."); 

    return f; 
} 

public PagerImageFragment() {} 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    //Initialize the cache 
    myCache = new MyCache<>(5 * 1024 * 1024); 

} 

@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, 
         Bundle savedInstanceState) { 
    // Inflate and locate the main ImageView 
    final View v = inflater.inflate(R.layout.pager_element, container, false); 
    photoView = (PhotoView) v.findViewById(R.id.pager_image); 
    Bitmap image = myCache.get(position); 
    if (image != null) { 
     photoView.setImageBitmap(image); 
    } 
    else { 
     new SetImageTask(photoView, myCache).execute(position); 
    } 
    return v; 
} 

Exception (après modification):

java.lang.NullPointerException: Attempt to write to field 'int android.support.v4.app.Fragment.mNextAnim' on a null object reference 
                      at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:770) 
                      at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1677) 
                      at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:536) 
                      at android.os.Handler.handleCallback(Handler.java:739) 
                      at android.os.Handler.dispatchMessage(Handler.java:95) 
                      at android.os.Looper.loop(Looper.java:155) 
                      at android.app.ActivityThread.main(ActivityThread.java:5696) 
                      at java.lang.reflect.Method.invoke(Native Method) 
                      at java.lang.reflect.Method.invoke(Method.java:372) 
                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1028) 
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:823) 

Répondre

0

ajouter dans votre activité dans le manifeste

android: configChanges = "orientation | keyboardHidden | ScreenSize"

<activity android:name="MainActivity" 
     android:configChanges="orientation|keyboardHidden|screenSize"> 
</activity> 

onConfigurationChanged sera appelé quand orien Les modifications de l'activité seront effectuées mais votre activité ne sera pas recréée mais à la place, onConfigurationChanged sera appelée et vous devrez gérer les changements d'orientation s'il y a des. Donc, il peut empêcher OutOfMemoryException problème dû à la récréation de tout sur le changement d'orientation.

@Override 
public void onConfigurationChanged(Configuration newConfig) { 
    super.onConfigurationChanged(newConfig); 
    // Handle orientation changes here 
} 
+0

J'ai posté seulement une partie de mon application. Cette activité modifie les fragments, et seul celui-ci a ce problème. Si j'ajoute configChanges, ne casserai-je pas le reste de l'application? J'ai entendu dire que ce n'est pas recommandé et je suis à la recherche d'une solution propre. – Haruspik

0

Tout d'abord, vous ne devez pas utiliser getFragmentManager pour votre adaptateur de radiomessagerie. Au lieu de cela, utilisez getChildFragmentManager

PagerAdapter adapter = new PagerViewAdapter(getChildFragmentManager(), pdfFactory.getPageCount()); 

Si elle ne va pas aider, ajouter la bibliothèque Leak Canaries à build.gradle dépendances pour détecter les fuites de mémoire https://github.com/square/leakcanary