Wednesday, September 12, 2012

Fixing an Android Memory Leak

One of the most dreaded bugs in Android is a memory leak. They are nasty because one piece of code causes an issue and in some other piece of code, your application crashes. The general rules of causality are violated. My adventure begins in the Android Developer console's Application Error Reports section, there I found several crash reports labelled, OutOfMemoryError.

Out of memory errors can be divided into two basic type: type one is the obvious - you are actually out of memory. You are trying to allocate more memory than your app's heap has. Type two is a memory leak, this type is harder to find. Looking at the crash reports it was easy to tell that I had a leak. The first clue is that the crashes are not coming from one place but from several. This is part of what makes memory leaks tough.

At this point I decided to do a low level check for the issue. I hooked up a phone to my PC and ran DDMS. Android's garbage collector writes information to LogCat as it is running. This information is short but sweet.

GC_CONCURRENT freed 1837K, 41% free 6232K/10503K, external 6427K/7571K, paused 3ms+4ms
GC_FOR_MALLOC freed 486K, 41% free 6372K/10695K, external 6764K/7459K, paused 47ms
GC_EXTERNAL_ALLOC freed 1147K, 41% free 6414K/10823K, external 7002K/7459K, paused 60ms

The most important piece information above is the highlighted percentage free. This number will fluctuate as you use your application but it should hover around certain values. You can never use 100% of the memory. Below 30% the system will begin to respond sluggishly and the time needed to allocate memory will go from less than 10ms to above 100ms. Below 20% and you will start seeing messages like:

Clamp target GC heap from 32.222MB to 32.000MB
GC_FOR_MALLOC freed 0K, 18% free 19695K/24007K, external 7950K/8765K, paused 171ms

These messages are an indication that the system is getting ready to crash.

As I stepped through the app, everything looked fine. I would watch the memory chunk downward and just when I thought I had found the culprit, it would suddenly shoot back up. Then I realized what I might be doing wrong. We have an active base of over 500,000 users. In the field, users are not doing a tour of all of the apps features as I was doing. If they were we would see a lot more than the average of six reports per week over this issue. Instead, I reasoned this was probably more a case of a user doing something excessively, such as navigating between two pages or repeatedly tapping a button.

I knew that the app is usually trying to allocate memory for bitmaps from the crash report. I knew the area of the app where the most bitmaps are allocated. And I reasoned the type of behavior the user might be doing before the crash. Now all I needed was a bit of luck.

Eureka

One section of our app consist of about eight pages hooked up to a ViewFlipper. On the last page of the flipper, the user is taken back to the first page. It is a loop and it goes in both directions. What would happen if the user kept looping through the pages, I wondered? The answer: memory would slowly decrease. I almost didn't notice it, but each loop through the pages would decrement a percent or so from the free memory. Now I was sure I had a memory leak but I still didn't know the cause.

(initial)
GC_CONCURRENT freed 1803K, 43% free 5747K/9991K, external 6289K/7624K, paused 3ms+4ms
(first pass)
GC_CONCURRENT freed 1486K, 32% free 8375K/12295K, external 12760K/12988K, paused 3ms+5ms
(second pass)
GC_EXTERNAL_ALLOC freed 919K, 30% free 10014K/14279K, external 11183K/11636K, paused 93ms
(fourth pass)
GC_EXTERNAL_ALLOC freed 6K, 29% free 11111K/15559K, external 9558K/10063K, paused 94ms

DDMS and the Eclipse Memory Analyzer Tool

The DDMS has some built-in and rudimentary heap tools, but when paired with the free Eclipse Memory Analyzer Tool or MAT, they shine. MAT can take the heap dumps that DDMS creates and fully analyze them. It does way more things than I have the time to learn, but I did figure out enough to have it help me find my issue. All I needed to do is generate the Leak Suspects report.

Note: Memory profiling in the DDMS is very resource intensive, so much so that I would only recommend doing it on a device and never on the emulator.

To generate the Leak Suspects Report:
  • Once you have the app in the area of the suspected memory leak, click the Update Heap icon
  • Do some activity to cause some memory to leak
  • Click the heap dump icon
  • On the Getting Started Wizard, click Leak Suspects Report, then click Finish.
  • After a delay, the Leak Suspects report will appear




The Leak Suspects Report

The report will generate a graph indicating things it thinks may be memory leaks. Be careful here. Not everything it thinks may be a leak actually is. Remember your program needs to have some objects allocated in order to function. What you really need to be suspicious of is things that have multiple instances allocated or things which are using huge amounts of memory. In my case, it was problem suspect #2. It showed 10 instances of the Dealer object allocated. As I looped through the pages the number of these objects grew and so did their size. But why? The Dealer object was a very simple class. It held the name, address, telephone number, etc. of a dealer. Yet according to MAT it had a shallow heap size of 64 bytes, but a retained heap size of 1.2 MB - what?

Leak Suspects

Leak Suspects

It is All in the Details

In order to determine why a simple object, which should be no more that a few hundred bytes was hanging onto 1.2 MB, I clicked on the Details link. This causes three more reports to appear: Shortest Paths To the Accumulation Point, Accumulated Objects, Accumulated Objects by Class.

Each of these reports told me lots of information. The Shortest Paths To the Accumulation Point report show me that my simple Dealer object was holding onto a lot of stuff including an activity. This is very common cause of memory leaks in Android. An object is somehow holding onto a reference to an activity causing it to not be garbage collected. The Accumulated Objects report showed me that there were Linear Layouts involved in this too. And finally the Accumulated Objects by Class report told me the final key pieces of information I needed. There were TextViews and DealerInfoView objects involved too.

Shortest Paths To the Accumulation Point


Accumulated Objects

Accumulated Objects by Class

The DealerInfoView object is only new'ed up in one place, in the adapter for the Dealer ListView. Once I looked at the getView() method I saw the problem.

@Override
public View getView(int position, View convertView, ViewGroup parent) {
 View dealersRow = inflater.inflate(R.layout.carhubdealersrow, null);
 Dealer dealer = dealers.get(position);
 new DealerInfoView(dealer, dealersRow);
 Button getQuote = (Button) dealersRow.findViewById(R.id.ButtonGetQuote);
 getQuote.setOnClickListener(new DealerQuoteDialog(activity, dealer, getFromHub()));
 ImageButton buttonDirections = (ImageButton) dealersRow.findViewById(R.id.ImageButtonDirections);
 buttonDirections.setTag(dealer);
 buttonDirections.setOnClickListener(new Directions(activity));
 return dealersRow;
}

The dealers object is an ArrayList of Dealer objects. It exist outside of this adapter. The adapter is managed by Android. So when the garbage collector tries to release this view it can't because it doesn't control the dealer object. The solution to this issue was relatively simple, create a local copy of the dealer object. The changed method is below. Only one small change was necessary to fix a painful bug.

@Override
public View getView(int position, View convertView, ViewGroup parent) {
 View dealersRow = inflater.inflate(R.layout.carhubdealersrow, null);
 Dealer dealer = new Dealer(dealers.get(position));
 new DealerInfoView(dealer, dealersRow);
 Button getQuote = (Button) dealersRow.findViewById(R.id.ButtonGetQuote);
 getQuote.setOnClickListener(new DealerQuoteDialog(activity, dealer, getFromHub()));
 ImageButton buttonDirections = (ImageButton) dealersRow.findViewById(R.id.ImageButtonDirections);
 buttonDirections.setTag(dealer);
 buttonDirections.setOnClickListener(new Directions(activity));
 return dealersRow;
}

Because the Dealer object is no longer a pointer to a foreign object, the garbage collector is now free to collect it. The memory leak is now plugged.

Tuesday, September 11, 2012

PhoneGap Android, jQuery Mobile, HTML5 Canvas, and Touch-based Drawing


This is the PhoneGap version of the jQuery Mobile touch-based drawing programing. For the most part all of the HTML and JavaScript remains the same between the two versions. There are only a few difference which should be explained. If you haven't read the previous post, you should before continuing.


jQuery Mobile, HTML5 Canvas, & Touch-based Drawing.

Orientation

The easiest way to handle orientation in an Android application is not to,simply supporting portrait would be enough. So I made the following change to the <activity> tag in the manifest:


  <activity
    android:name=".MainActivity"
    android:screenOrientation="portrait"
    android:label="@string/title_activity_main" >

Since we don't seem to receive orientationchange events from within the app's code I removed the event handler for it and the resize event. 

Other than those two changes the rest of the code was lifted as is from the web version of the application. The complete source code is in my GitHub repo. It is for Android and the Eclipse IDE. Hopefully a little later in the week I will do a Intellij IDEA version and also the iOS version.



Monday, September 10, 2012

Free jQuery Mobile Quick Start Webcast

I will be teaming up with the good people at AppDev on September 26th to do a free jQuery Mobile Quick Start webcast. Here is the official description:

The mobile web is one of the fastest growing segments of the Internet, but where do you start? With jQuery Mobile of course. jQuery Mobile is a open source, mobile web framework built on top of jQuery. With it you can easily create expressive and responsive web sites which support a wide variety of mobile devices including all of the lastest smartphones and tablets. In this Live Learning event you will see how to get started with jQuery Mobile today.

This is the perfect way to begin developing your mobile web skills, from your own computer, anywhere you happen to be in the world. Since this is a webcast you will also be able to ask me questions. This is a free event, but you must register in advance. jQuery Mobile Quick Start @AppDev

Sunday, September 9, 2012

So Cal Code Camp @ USC

If you live any where in the Southern California area, get ready! So Cal Code Camp is coming October 13th & 14th from the beautiful campus of the University of Southern California. If you are any level developer this is the event for you. Lots of sessions, from top notch speakers including me. Best of all, this event is FREE! I will be giving a four talks this time around. Please show your support by coming to my talks and saying "hi!" in person.

My talks:

Beginning HTML5 Mobile Game Programming with jQuery Mobile

Want to create a game for your hot new phone or tablet but not up to slogging through chapter after chapter of Objective-C or Java? Then this is the session for you. Take the web skills you already have and enhance them with HTML5 and jQuery Mobile to build fun games which run on all of the latest mobile devices.

In this session, I will present a sample HTML5 game engine, explain the various pieces of its architecture, and most of all explain how you can further expand it. All of the source code and slides shown will be available for download after the session.

This session will also be the basis of a Google+ Hangout On Air on Tuesday, October 16th at 6 PM, PDT. For more info:

http://therockncoder.blogspot.com/2012/09/free-beginning-html5-game-programming.html

jQuery Mobile, Backbone, and ASP.NET MVC: A Framework for Making Mobile Web Applications

Websites designed for smart phones are one of the fastest growing segments of the internet. JQuery Mobile makes it easy to create sites for mobile devices, but it is mostly about UI. It doesn’t address the needs of an enterprise application. Things like how to load data from and persist data to the server, how to tie the JavaScript and HTML together, and minimize the amount of data transmitted. In this session I will show how to combine JQuery Mobile with Backbone.js and a ASP.NET MVC back-end. The end result is a website which is fast, efficient, and easier to enhance and maintain.

Enterprise Strength Mobile JavaScript:
Unit Testing and Debugging Mobile JavaScript in the Real World

JavaScript has a well deserved reputation of be hard to write and debug. Put it on a mobile device and the problems increase exponentially. Mobile browsers lack all of the niceties that developers rely on to do testing and debugging including the most fundamental tool, the debugger. But it is possible to write quality JavaScript on a mobile device without relying on blind luck. In this talk I will show all of the tools and tricks that I learned in my 14 month development of the new KBB.com mobile site.


5 Quick JavaScript Performance Improvement Tips

JavaScript is arguably the most important language in the world. It comes included in nearly every desktop and mobile browser. It powers the client-side of apps like Facebook and GMail. It is the language of choice for mobile development environments like Apcelerator's Titanium and Apache's Cordova (aka Adobe's PhoneGap). It is even on the server now in Node.js. Yet when programmer's run into performance issue with JavaScript their first inclination is to blame its interpreted nature, not realizing that simple changes in the structure of their code can result in sometimes significant improvements in performance. In this session I will show five quick changes you can make to your JavaScript code to improve its performance and explain why they work.

All of the source code for all of my talks will be available for download after the session ends. For more information please visit the So Cal Code Camp website. Be sure to click the Interested button for all of the talks you are interested in, including some of mine. The talks with the most interest will get the biggest rooms.


Enterprise Strength Mobile JavaScript: The Video



Here is the video of my talk to the SoCal.NET User's Group on September 5, 2012. The topic: Enterprise Strength Mobile JavaScript. The sound quality is a bit iffy, but I think bearable. I apologize for the length, it is nearly two hours long, but I cover a lot of material: JavaScript best practices, Unit Testing, and mobile debugging. 

jQuery Mobile, HTML5 Canvas, & Touch-based Drawing

There is a PhoneGap version of this tutorial at:




One of the reasons that I like jQuery Mobile (JQM) is that it is a framework and not a straight-jacket. Anything not provided by jQuery or jQuery Mobile can  simply be created regular using HTML. Which brings me to the HTML5 canvas object. It is allows for the dynamic creation of drawings. It seems like a natural fit to combine it with the touch ability that most mobile devices have built-in. 

Unfortunately JQM doesn't have any built in support for the canvas object. But this isn't a problem. Since we can use our knowledge of HTML5 to added it. In this demo I will use some information from two prior tutorials which you may want to read before proceeding:




The basic framework of the application is a slightly modified form of my jQuery Mobile Skeleton. The only real difference is that I am now wrapping nearly all JavaScript code in an anonymous/immediate function to minimize my pollution of the global space.

The Canvas Object

<canvas id="myCanvas" width="200" height="100"></canvas>

The HTML5 canvas object has two important attributes, width and height. Normally these are set in the <canvas> tag and left alone. This would make our drawing application fairly boring. We would be forced to choose a size which would be less than ideal on nearly every platform we ran our web app on. If it looked good on a phone, it would be way too small on a tablet. If it looked right on a tablet, phones would be screwed. Luckily it isn't too difficult for us to determine the size of the content area and then size our canvas accordingly.

The Dimensions function determines the size of the first of the window object then subtracts heights of either a header, a footer, both or neither. Note: I am  using window object and not the document object. The two are different. The window refers to the viewport, while the document refers to the HTML document. It is very easy to have a document which exceeds the size of the viewport, hence why documents sometimes have scrollbars.

// size the content area
RocknCoder.Dimensions = (function () {
  var get = function () {
    var isFirstPass = false,
        isIPhone = (/iphone/gi).test(navigator.appVersion),
width = $(window).width(),
height = $(window).height() + (isIPhone ?  60 : 0),
hHeight = $('header').outerHeight() || 0,
fHeight = $('footer').outerHeight() || 0;
    return {
      width: width,
      height: height - hHeight - fHeight
    };
  };
  return {
    get: get
  };
}());

Once we have the size of the content area we use it to dynamically updated the width and height attributes of the canvas object. Luckily, jQuery has all the tools we need to change the attributes of any HTML element. Below, we just set the width and height by sending a object literal to attr method of the canvas selector.

reSizeCanvas = function () {
  var dims = RocknCoder.Dimensions.get();
  $canvas.attr({
    width: dims.width - 4,
    height: dims.height - 4
  });
  return dims;
},

Note: I have cached the jQuery selector for the canvas object ($canvas). This is an important performance boost for any jQuery app. It takes quite a bit of time to scan the DOM for any element. It is always smart to save the results to avoid a future hit.

Touch Events

The final bit of code which needs explanation are the event handlers for touchstart and touchmove. This is core of our drawing program. When we receive a touchstart event we begin a new line. When receive a touchmove, we draw a new line segment from the previous point to the current one. That's it nothing super difficult going on here.

// start tracking the touches, move the pen to the beginning of a line
$canvas.bind('touchstart', function (event) {
  var xy = extractXY(event.originalEvent.touches[0]);
  ctx.moveTo(xy.x, xy.y);
  event.preventDefault();
  return false;
});
// draw a line from the last point to this one
$canvas.bind('touchmove', function (event) {
  var xy = extractXY(event.originalEvent.touches[0]);
  ctx.lineTo(xy.x, xy.y);
  ctx.stroke();
  event.preventDefault();
  return false;
});

Go To The Demo
Next Steps

Our drawing program is very simple. There is no support for non-touch devices such as mice. It only supports the drawing of thin yellow lines. There is no support for more colors, thicker lines, easing or anything else. It wouldn't be too tough to add more features to this program and I hope that someone will.

The complete source for this tutorial is on GitHub, as always. A working demo is now on my demo site, just tap on TouchPaint.

Friday, September 7, 2012

jQuery Mobile and the Inland Empire .NET User's Group

I will be giving a presentation titled: jQuery Mobile, Backbone, and ASP.NET MVC - A Framework for Making Mobile Web Applications, to the fine people of the Inland Empire .NET User's Group on Tuesday, September 11th. If you are a .NET programmer looking for a good introduction to the mobile web, this is the presentation for you. 

The meeting will be at:


New Horizons

451 Vanderbilt Way, San Bernardino, CA 
http://binged.it/O7nj7t

Doors open at 6 PM. I hope I will see you there.


Please Rate This Talk!