Android Fragments memory leak related to Bitmap usage - android

I am trying to create some Picture Album app. But When creating new Fragment , old one stays and Bitmaps it contains are quickly filling my memory resulting in out of memory error.
Here is what I am trying to do:
Let say I have 3 categories. Wedding pictures , Summer holidays and other. After clicking on each category I want to display photos in a fragment next to it.
For main view I am using FragmentActivity from MasterDetailFlow template (When you create new Android project in Eclipse). So basically it have one fragment with listView (here are mine categories) and framelayout to put my other fragment.
Because I wanted to have ViewPager for pictures I created Fragment that puts
android.support.v4.view.ViewPager
inside the framelayout (in Activity onItemSelected :) :
fragment = new PagerFragment();
fragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction()
.replace(R.id.item_detail_container, fragment).commit();
This fragment is basically just used to set up :
onCreateView ....
rootView = inflater.inflate(R.layout.pager, container, false);
myFragmentStatePagerAdapter = new MyFragmentStatePagerAdapter(
getActivity().getSupportFragmentManager(), pictures);
mViewPager = (ViewPager) rootView.findViewById(R.id.pager);
mViewPager.setAdapter(myFragmentStatePagerAdapter);
return rootView;
Adapter on the other hand creates new PictureFragment in getItem method.
PictureFragment uses BitmapFactory and LruCache to fetch pictures. I store WeakReference to bitmap in PictureFragment and in onDestroyView() I call:
if (bitmapReference !=null){
bitmapReference.get().recycle();
bitmapReference = null;
}
Now to the problem. Within one PagerFragment it all looks ok. PicturesFragments are created and destroyed. Memory seems to be under control. But the problem starts when I click in the activity on other list element. I can see in logs that two new fragments are being created for pager View , I can see that onDestroyView() method was called on the PagerFragment but not on the PictureFragment which I assume result in Bitmaps not being recycled. Hence out of memory error. So what do I do wrong? How to remove PicturesFragment and bitmaps inside?
Thanks for help
W

Not sure if it is the same problem, but some time ago I had a problem with viewpager where the fragment lifecycle methods were never called and the problem was with the fragment manager.
So, when you create the adapter in:
myFragmentStatePagerAdapter = new MyFragmentStatePagerAdapter(
getActivity().getSupportFragmentManager(), pictures);
try using childFragmentManager instead of supportFragmentManager and see if it fixes your issue.

Related

Fragment is not attched to parent when application goes to background for long time

I have a fragmentA which has ViewPager which contains couple fragments lets say FragmentA-1 and FragmentA-2. If i keep app in background for more than 10 minutes and open app again, Fragment inside(i.e. FragmentA-1, FragmentA-2) viewpager get detached.
But click events of the view inside FragmentA-1 and FragmentA-2 works fine and when I try to update UI from parent fragment i.e. FragmentA then it shows view as null;
mFrontPhotoFragment = AddCardPhotosFragment.newInstance();
mBackPhotoFragment = AddCardPhotosFragment.newInstance();
mCardsFragment.add(mFrontPhotoFragment);
mCardsFragment.add(mBackPhotoFragment);
PagerAdapter pagerAdapter = new PagerAdapter(getChildFragmentManager(), mCardsFragment);
mCardsViewPager.setAdapter(pagerAdapter);
This happens on specific devices with low configuration. How can i fix this
Thanks in advance.
In view pager, Fragment might be recycled it some times. Try setting page limit.
You can call viewpager.setOffscreenPageLimit(k) to cache all entities of ViewPager from n-k to n+k.

How do I access elements in multiple nested fragment from an activity in android

I am working on a "guided tour" feature for my android application and need to dim and brighten certain elements on the current screen. I am using fragments for each screen and when I try to access elements in those fragments from the activity they are always null. I have been through countless questions and none seem to help me.
The structure of the fragments is as follows:
I'm using a Fragment TabHost that has only two tabs.
Each tab widget has a container fragment that I use to switch between a fragment A and a fragment B within one tab.
I'm going to start the tour from Tab one, fragment b but everytime I try to get the elements they are null even if I create a new fragment the line before let alone try to drill down into nested fragments.
I am trying to find the fragment using getSupportFragmentMangager() but since I have to get elements 3 fragments deep I end up trying to do an operation on an element that is null. I hope I am not missing something simple here. I am unsure where to start.
Why not store a static reference to the variables you need global access to in the Activity, then access them like MyActivity.class.myVariable and use getSupportFragmentManager#replace() to swap out the fragments dynamically?
You can use getChildFragmentManager() instead of getSupportFragmentMangager()
I discovered my problem was that the view being created by the fragment was simply not finished and therefore not displayed when I called my code to get elements from the fragment layout. I needed to wait until it was done being created and then I could find any element including elements in nested fragments. The nesting of views didn't matter.
This code fixed my problem:
import android.os.Handler;
//After doing replace transaction
final FragmentManager fragmentManager = getSupportFragmentManager();
final Handler handler = new Handler();
//Wait for fragment view to be created
handler.postDelayed(new Runnable() {
#Override
public void run(){
//Do stuff after 100 miliseconds
TabHostParent_Fragment myFrag = (TabHostParent_Fragment) fragmentManager.findFragmentByTag("tabhost_frag");
Switch auto_switch = (Switch) findViewById(R.id.auto_switch);
auto_switch.performClick();
}
}, 100);

What is the correct way to restore a fragment?

First of all I'm sorry if this explanation seems unclear, I'm new to Android.
I have a ViewPager in main activity showing fragments added dynamically by user. Fragments are created initially on activity start up and are added to ViewPager via Adapter i.e. adapter simply returns proper fragment and as I understand correctly fragment's content is created at this time when ViewPager 'retrieves' a fragment first time.
The problem is when main activity gets restored after orientation changing all fragments are resurrected as well and when Adapter tries to return newly created by user Fragment method createView() is no longer called and it fails with NullPointerException. It seems ViewPager retains fragments attached to it initially and doesn't call createView() for newly added ones for the same position.
I have a feeling I'm missing vital point on the Fragment lifecycle. I wouldn't like to change the design. My main question is what the correct way is to return a Fragment added to ViewPage after activity is restored? Is there any way to locate recently attached fragments?
If the fragment already exists, it will be re-used. However the state of the fragment will not. You should take a look at http://developer.android.com/training/basics/activity-lifecycle/recreating.html for more information.
In particular you should look at overriding onSavedInstanceState and onRestoreInstanceState as a method to repopulate the field that is generating that nullpointerexception
To determine if an instance of a fragment has already been created you can use 'findFragmentByTag' like so:
String fragmentTag = MyFramgment.getClass().getName();
MyFragment frag = getFragmentManager().findFragmentByTag(fragmentTag);
if(frag == null)
frag = new MyFragment();
Whatever ends up being referenced in 'frag', show this to the user.

android ViewPager how to list all current fragments and execute method for them

I'm Android programmer making first steps. In my app I decided to use ViewPager (support.v4).
I create all fragments in very typical way:
#Override
public void onCreate(Bundle savedInstanceState) {
(...)
//let's initialize view pager
List<Fragment> fragments = new ArrayList<Fragment>();
fragments.add(new MainActivitySelectDatabase());
fragments.add(new MainActivityDetails());
mainActivityPageAdapter = new MainActivityPageAdapter(getSupportFragmentManager(), fragments);
mainActivityViewPager = (ViewPager) findViewById(R.id.main_activity_view_pager);
mainActivityViewPager.setAdapter(mainActivityPageAdapter);
(...)
}
Next I would like to inform all fragments about one event in the following way:
protected void informAllFragmentsWhenDatabaseStatusChanged () {
for (int i=0; i<mainActivityPageAdapter.getCount(); i++) {
((InterfaceMainDatabaseStatusChanged) mainActivityPageAdapter.getItem(i)).onMainDatabaseStatusChanged();
}
}
And everything seems to work ok if I don't change screen orientation. When I change it everything becomes crazy. Methods like getActivity or getApplication in Fragment returns null. I'm guessing that for unknown reasons the method is executed for old version of my fragments, not for the new ones. How should it be solved? How to list current versions of all Fragments from main Activity?
Thanks in advance for help.
I'm guessing that for unknown reasons the method is executed for old
version of my fragments, not for the new ones.
Those unknown reasons are that the ViewPager only calls the getItem() method only when it doesn't have a valid instance for that page's fragment(and after a configuration change, the ViewPager will have references to those fragments through the FragmentManager). Your options are to get rid of the list of fragments and simply instantiate the proper fragment directly in the getItem() method(and also use a different method to access the fragments ) or to further extend the adapter so your list will always have the proper references to the fragments of the ViewPager.
For option one have a look at this question. You can also implement the communication by interfaces, registering each of the fragments of the ViewPager in their onAttach methods with the activity in some sort of WeakReference structure(and using this when needing to access the fragments).
To further extend the adapter have a look at the instantiateItem() method.

Using ViewPager caches Fragments after screen rotation?

I initially create my fragments inside the Activity onCreate(). Than I go about creating the ViewPager and setting up the adapter. I keep a global reference to the fragments so I can update them as needed. Also, these fragments are accessed by the adapter.
My issue is that I notice once the screen is rotated the Fragments are recreated but the ViewPager still contains the original fragments created...??
How am I supposed to handle the life-cycle of my fragment? I need to be able to call directly to the fragment from the activity. Is a singleton for a fragment a good idea? Or just a memory leaker?
protected void onCreate (Bundle savedInstanceState)
{
...
...
// set up cards
mFrag1 = new Frag1();
mFrag1.setOnActionEventListener(mOnActionEvents);
mFrag2 = new Frag2();
mFrag3 = new Frag3();
mFragPager = (ViewPager) findViewById(R.id.vpPager);
mFragAdapter = new FragAdapter(getSupportFragmentManager());
mFragPager.setAdapter(mCardAdapter);
mFragPager.setOnPageChangeListener(mOnCardChange);
}
Global instances and static fragments are definitely a bad idea. Unless you call setRetainInstance() on a fragment, it will be serialized to a Bundle and recreated when an the parent activity is re-created (on screen rotate, etc.). That will, of course, produce new fragment instances, and your old references will be invalid. You should get references to fragments via FragmentManager.findFragmentById/Tag() as needed and definitely not store those in static variables.
You may need to show more of our code, but as long as you create the fragments in onCreate() the references should be valid for the lifetime of the activity. Check the compatibility library sample code for more on using fragments with ViewPager.

Categories

Resources