Saturday, 31 January 2015

DateTimeFormat comparison (Java vs Joda vs Apache commons)

Shit happens! Yes..Always, Developers like me are used to this but this time, It was not me, Hmmm... I Swear! Lets go back into those bad times and try to make it good.

A little flashback -:

Scenario was, We need full day timings in the interval of 2 seconds in the format of "hh:mm" so we traverse from 9AM to 4PM and convert all the milliseconds in the required format that we need to show on UI.So simple, right.! Just a loop. Developers have left simplistic life decades ago! So after launching this code on Samsung S3, my eyes literally dropped on my table. It was taking around 2.8 seconds time to convert 12600 timestamps into required format which is horrible. After googling & stakoverflowed it, I decided to change Java's default Dateformatting method and let's have a look at libraries of such guys who are calculating this smartly.
Finally got two popular libararies to convert time which are
  • Joda Time
  • Apache commons lang
I tasted these libraries One by one, and Guess What! got back my dropped eyes. The formatting time was improved fantastically by using both libraries .Here are the statistics taken using Samsung Galaxy S3 & average of 5 readings -:

Main loop of 12600 records -:

    private Calendar calendar = Calendar.getInstance();
    while (calendar.getTimeInMillis() <= fourPmTimeStamp) {
            calendar.add(Calendar.SECOND, 2);
            list.add(getFormattedLabel(calendar.getTimeInMillis())); // Here is all SHIT!!
    }

Formatting timstamp using different libraries -:

  • Java's DateTimeFormat 

      /* This one took 2821 ms, an average of 5 reading */
      public String getFormattedLabel(long timeStamp) {
          return DateFormat.format("hh:mm", timestamp).toString();
      }
    
  • Apache Commons-lang 2.6

      /* This one tasted like pastry & took 937 ms ,an average of 5 reading */
      private static FastDateFormat fastdateformat = FastDateFormat.getInstance("hh:mm");
      private static Date date = new Date();
      public String getFormattedLabel(long timeStamp) {
          date.setTime(timeStamp);
          return fastdateformat.format(date);
      }
    
  • Joda-Time v2.3

      /* This tasted really Yummy,buttery! It took 612 ms, an average of 5 readings */    
      private static final DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern("hh:mm");
      private static DateTime dateTime = new DateTime();
      public String getFormattedLabel(long timeStamp) {
          dateTime = dateTime.withMillis(timeStamp);
          return dateTime.toString(dateTimeFormatter);
      }
    
Finally, made our application's life more responsive by Joda-Time.I would like to thanks Joda-Time developers for such optimization in there library and made our life easier. Peace!
- Abhishek Birdawade

Monday, 13 January 2014

Tricks to boost performance of list view

If you want to make your list view scroll very smoothly on each type of devices especially greater than api level 2.3 then there are several things in which you should look at -:


1) Use minimal conditions in getView of adapter.
2) Avoid garbage collector warnings in log (max as you can).
3) On scrolling do not load images to avoid load on UI thread.
4) Set scrollingCache and animateCache to false.
5) Keep Hierarchy of list view row layout as minimum as possible.
6) Use view holder pattern.

I was having a problem of lag so i will explain with the example of how i improved the performance and made the list view scroll like a butter!!.There is not a simple solution for each case because the solution of improving varies on the basis of your code and where you have mistaken.Someone might get solved by just adding view holder or some might get solved by flattened the hierarchy of layout.So we have to try out each solution and here are which i have tried and got it worked.


1) Use minimal conditions in getView of adapter -: Do not put so many complex conditions (excellent if you do not put any) instead you can add all those conditions in the main activity class  and get those values through any other class(mostly i use serializable).There were more complex conditions in this example but i removed to make it easier to understand.


Previous GetView =
@Override
public View getView(int position, View convertView, ViewGroup paramViewGroup) {
        Object current_event = mObjects.get(position);
        ViewHolder holder = null;
        if (convertView == null) {
                holder = new ViewHolder();
                convertView = inflater.inflate(R.layout.row_event, null);
                holder.ThreeDimension = (ImageView) convertView.findViewById(R.id.ThreeDim);
                holder.EventPoster = (ImageView) convertView.findViewById(R.id.EventPoster);
                convertView.setTag(holder);

        } else {
                holder = (ViewHolder) convertView.getTag();
        }

       // If you have conditions like below then you may face problem
        if (doesSomeComplexChecking()) {
                holder.ThreeDimention.setVisibility(View.VISIBLE);
        } else {
                holder.ThreeDimention.setVisibility(View.GONE); 
        }

        // This method is setting layout parameters each time when getView is getting called which is wrong.
        RelativeLayout.LayoutParams imageParams = new RelativeLayout.LayoutParams(measuredwidth, rowHeight);
        holder.EventPoster.setLayoutParams(imageParams);

        return convertView;
}
New GetView =
@Override
public View getView(int position, View convertView, ViewGroup paramViewGroup) {
    Object current_event = mObjects.get(position);
    ViewHolder holder = null;

    if (convertView == null) {
            holder = new ViewHolder();
            convertView = inflater.inflate(R.layout.row_event, null);
            holder.ThreeDimension = (ImageView) convertView.findViewById(R.id.ThreeDim);
            holder.EventPoster = (ImageView) convertView.findViewById(R.id.EventPoster);
            // This will now execute only for the first time of each row
            RelativeLayout.LayoutParams imageParams = new RelativeLayout.LayoutParams(measuredwidth, rowHeight);
            holder.EventPoster.setLayoutParams(imageParams);
            convertView.setTag(holder);
    } else {
            holder = (ViewHolder) convertView.getTag();
    }

    // This way you can simply avoid conditions
    holder.ThreeDimension.setVisibility(object.getVisibility());

    return convertView;
}

2) Garbage collector -: GC occurs frequently when you're creating lots of objects and then destroying them. So the general advice here would be to not create a lot of objects in getView(), and an even better advice - to not create objects at all except the view holder.If you are seeing "GC has freed some memory" message in your Log VERY frequently then you are doing something grievously wrong.You will have take a look at one of the following -:

a) Hierarchy of list row layout.
b) Check your GetView adapter.
c) List view properties in layout.


3) Loading images -: If you are downloading images then you can use the ImageLoader library from google IO 2013 open source app which is really fast in loading images.They are using volley library for image loader. We should not load images when fling motion event occurs because in fraction of seconds list view can not load all rows smoothly and without any lag.It may load but not on all devices which has low configuration.Here In this image loader is when a fling motion event occurs it stops its queue of an image loader and when scrolling stops then again it resumes its queue, for that we need to add scroll listener as -:

listView.setOnScrollListener(new OnScrollListener() {

            @Override
            public void onScrollStateChanged(AbsListView listView, int scrollState) {
                    // Pause disk cache access to ensure smoother scrolling
                    if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) {
                            imageLoader.stopProcessingQueue();
                    } else {
                            imageLoader.startProcessingQueue();
                    }
            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
                    // TODO Auto-generated method stub

            }
    });
Here is the link of google IO 2013 src - http://code.google.com/p/iosched/

4) Scrolling and animate cache properties of listview-:  Scrolling cache is basically a drawing cache.In android, you can ask a View to store its drawing in a cache called drawing cache (basically a bitmap). By default, a drawing cache is disabled because it takes up memory but you can ask the View to explicitly to create one either via setDrawingCacheEnabled or through hardware layers (setLayerType). Drawing cache make your animation smooth compared to redrawing the view at every frame.


This type of animation can also be hardware accelerated because the rendering system can take this bitmap and upload it to the GPU as a texture (if using hardware layers) and do fast matrix manipulations on it (like change alpha, translate, rotation). Compare that to doing animation were you are redrawing (onDraw gets called) on every frame.In the case of a listview, when you scroll by flinging, you are in essence animating the views of your list (either moving them up or down). The listview uses the drawing cache of its visible children (and some potentially visible children near the edges) to animate them very quickly.


Is there a disadvantage to using drawing cache? Yes it consumes memory which is why by default it is turned off for in a View. In the case of ListView, the cache automatically created for you as soon as you touch the ListView and move a little (to differentiate a tap from scroll). In other words, as soon as ListView thinks you are about to scroll/fling it will create a scroll cache for you to animate the scroll/fling motion. This detailed information is from Numan Salati (http://stackoverflow.com/questions/15570041/scrollingcache)


AnimationCache -: Defines whether layout animations should create a drawing cache for their children. Enabling the animation cache consumes more memory and requires a longer initialization but provides better performance. The animation cache is enabled by default.You can set false values to these properties as it causes to invoke GC.


Previous Listview =
<ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:cacheColorHint="#00000000"
        android:divider="@color/list_background_color"
        android:dividerHeight="0dp"
        android:listSelector="#00000000"
        android:smoothScrollbar="true"
        android:visibility="gone" /> 
 New Listview = 
<ListView
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:divider="@color/list_background_color"
        android:dividerHeight="0dp"
        android:listSelector="#00000000"
        android:scrollingCache="false"
        android:animationCache="false"
        android:smoothScrollbar="true"
        android:visibility="gone" />

5) Hierarchy of list view row layout -: Try to keep hierarchy of list view row layout as minimum as possible because it directly related with measuring and drawing time of the layout which can cause in the lag while scrolling.You can remove unnecessary layouts of it.


6) View holder pattern -: This is the huge boost for the scroll as it avoids constant call to the findViewById() which slows down your performance when list scrolls.As lots of information has already been published on this topic so i recommend you to go through links below -:



AM I MISSING ANYTHING HERE THEN COMMENTS ARE WELCOME!! WE CAN EXPLORE MORE.