Monday 24 June 2013

Get current visible fragment page in a ViewPager

There are two ways to get current visible fragment page in ViewPager.

FIRST APPROACH

You can set a unique tag on each fragment page like below

etSupportFragmentManager().beginTransaction().add(myFragment, "Some Tag").commit();

then retrieve the fragment page

getSupportFragmentManager().findFragmentByTag("Some Tag");

 You need to keep track of the string tags and link them with the fragments. Use a HashMap to store each tag along with the current page index, which is set at the time when the fragment page is instantiated.


MyFragment myFragment = MyFragment.newInstance();
mPageReferenceMap.put(index, "Some Tag");
getSupportFragmentManager().beginTransaction().add(myFragment, "Some Tag").commit();

To get current visible page's tag you need to call

int index = mViewPager.getCurrentItem();
String tag = mPageReferenceMap.get(index);

and then to get current visible fragment call

Fragment myFragment = getSupportFragmentManager().findFragmentByTag(tag);


SECOND APPROACH

Second approach is also similar to the first one, here also we have to keep track of all the active fragments. In this case, you keep track of the fragments in the FragmentStatePagerAdapter.

public Fragment getItem(int index) {
    Fragment myFragment = MyFragment.newInstance();
    mPageReferenceMap.put(index, myFragment);
    return myFragment;
}

You should not keep the reference of inactive fragments, to remove inactive fragments from mPageReferenceMap we need to implement FragmentStatePagerAdapter's destoryItem(...) method and do like below.

public void destroyItem(View container, int position, Object object) {
    super.destroyItem(container, position, object);
    mPageReferenceMap.remove(position);
}

and to access the current visible fragment page, you then call


int index = mViewPager.getCurrentItem();
MyAdapter adapter = ((MyAdapter)mViewPager.getAdapter());
MyFragment fragment = adapter.getFragment(index);

MyAdapter's getFragment(int) method should be like this

public MyFragment getFragment(int key) {
    return mPageReferenceMap.get(key);
}

I prefer the second approach.

Sunday 10 February 2013

OutOfMemoryError: bitmap size exceeds VM budget - Android

Bitmap out of memory is a common problem in android, it took lot of time for me to solve this issue.
Like i tried catching this error, which did work to some extent but couldn't solve my problem totally.

Hence, i started digging into it from low level. Basically android system allocates memory for each application, which varies from device to device, min16 MB to max 90MB(google Nexus 7). 

Now, when ever free memory out of allocated memory for an app is less then Bitmap size, System throws Out of Memory error, if bitmap size is more then free memory then we should not load the image. So I ended up finding the size of bitmap and free memory available, which can be checked using below code.

  public static boolean checkBitmapFitsInMemory(long bmpwidth, long bmpheight, int bitdepth) {
    long reqsize = 
bmpwidth x bmpheight x (bitdepth) / 8;
    long allocNativeHeap = Debug.getNativeHeapAllocatedSize();

    final long heapPad = (long) Math.max(3 * 1024 * 1024, Runtime.getRuntime()
        .maxMemory() * 0.1);
    if ((reqsize + allocNativeHeap + heapPad) >= Runtime.getRuntime()
        .maxMemory()) {
      return false;
    }
    return true;

  }


Here calculating image size properly is very important, if a image file size is 250kb it dose not mean that it will take 250kb in memory, that is the file size in disk. When the image is loaded in memory(RAM) it will calculate the pixels of the image and for example if you take RGB image for each color it will take 8 bit of memory that meant red + green + blue = 24 bit for each pixel, convert 24 bit into byte it is 3 byte for single pixel now calculate how many pixels are there in image (width pixels x height pixels) you will get total pixels of the image then multiply total pixels of the image with 3 if it is RGB image and you will get total memory required to load the image in memory if there is less memory then don't load the image to avoid OutOfMemoryError.
if your image have alpha channel (RGBA) then for each pixel it will take 4 byte. For Gray scale (black and white) image it will take 2 byte.

Sunday 20 January 2013

NetworkOnMainThreadException

One of the recent issues I found while working with Android 4.0 is NetworkOnMainThreadException.
This is only thrown for applications targeting the Honeycomb SDK (3.0) or higher. Applications targeting earlier SDK versions are allowed to do networking on their main but it's heavily discouraged.

There are some other operations that jelly bean, ICS, HoneyComb won't allow to perform on the UI thread like:
  • HTTP requests (i.e. HTTPClient and HTTPUrlConnection).
  • Opening a Socket connection (i.e. new Socket()).
  • Attempting to connect to a remote MySQL database.
  • Downloading a file (i.e. Downloader.downloadFile()).
 It is recommended not to performing long operations such as network access, database queries or accessing disk on the UI thread.

Solution:
There are three ways to solve the problem of NetworkOnMainThreadException.
  • If you look at android document you will find that from Honeycomb (3.0) they have introduced StrictMode which will not allow network operations on UI thread. So one simple solution is disabling the StrictMode. 
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);

But I will not recommend this solution as it will increase load on UI thread with expensive operations like network operation which may freeze your app's UI and freezing UI or lagging in UI will give bad user experience.

  • Second solution is to use AsyncTask<Params, Progress, Result> , using async task you can do all the long running operations which may result in UI lagging if you do those operations on UI thread.
  • Third solution is to use Service. for network operations. Where you required to download data once only when user starts the app in that case I recommend IntentService.
 I hope this will help you, if any body wants to know more about how to use AsyncTask, IntentService or bind Service then please let me know, I will try to help on these.