My app supports API level 10 (Gingerbread) upwards. One of the activities draws a chart which works perfectly on later versions, but when running a level 10 emulator I get an extra call to View.onDraw with the wrong canvas ID and this causes the screen to go blank. (It's not just the emulator - the problem was reported by someone running on their Gingerbread phone.)
Normal operation is for onDraw to be called twice - the first time from the framework, where I take the canvas ID from, and the second time from my call to invalidate(), which passes the same canvas ID. These two calls happen with the level 10 emulator, but then there is a third call with a different canvas ID - i.e. not belonging to the view, and this blanks it out.
The activity is derived from SherlockActivity to provide an action bar, and I believe this is what is causing the problem.
Relevant code from my activity class:
public class Chart extends SherlockActivity implements OnGestureListener, OnDoubleTapListener, OnScaleGestureListener
public static boolean mDataSet = false;
private ChartView mView;
private Menu mMenu;
private GestureDetector mDetector;
private ScaleGestureDetector mScaleDetector;
private ActionBar mActionBar;
/** Called when the activity is first created. */
public void onCreate(Bundle savedInstanceState)
mDataSet = false;
mActionBar = getSupportActionBar();
mView = new ChartView(this, mCentreLat, mCentreLong, mRadius);
Context context = getApplicationContext();
mDetector = new GestureDetector(context, this);
mScaleDetector = new ScaleGestureDetector(context, this);
public void onConfigurationChanged(Configuration config)
mDataSet = false;
// Menu handling
public boolean onCreateOptionsMenu(Menu menu)
mMenu = menu;
MenuInflater inflater = getSupportMenuInflater();
public boolean onOptionsItemSelected(MenuItem item)
boolean handled = true;
mDataSet = false;
Intent i = new Intent(getBaseContext(), ChartSettings.class);
// Other menu options here...
handled = false;
Relevant code from my View class:
public class ChartView extends View
public ChartView(Context context, float centreLat, float centreLong, float radius)
mContext = context;
mCentreLat = centreLat;
mCentreLong = centreLong;
mRadius = radius;
protected void onDraw(Canvas canvas)
// First time
// (We pick up the canvas id mCanvas from the parameter.)
// (Nothing much else relevant here, except this is where stuff
// gets initialized, then in setDataInfo(), mDataSet is set true
// and invalidate() is called at the end.)
mCanvas = canvas;
setDataInfo(); // invalidate() called at end
// Second call to onDraw() with same canvas id comes here,
// then an unwanted third call with a different canvas id, only
// with the Level 10 (Gingerbread) emulator.
// This is where the various lines and shapes are plotted
// on the canvas (mCanvas)
Please can anyone explain why the third call happens with the Gingerbread emulator (or phone)? The effect is that the screen is blanked (completely white).
By the time it gets to this function it's too late and the call cannot just be ignored - the screen goes blank as the call stack unwinds.
There is a workround - if the user chooses view options from the menu, then immediately returns to the chart, it is redrawn and behaves normally from then on.
The problem is that you are keeping a reference to the Canvas you receive as a parameter. There is no guarantee whatsoever that this Canvas instance will be valid after the current frame is done. You could for instance receive a different Canvas instance on every frame. You will typically receive a different Canvas when the View is rendered into a Bitmap (View.setDrawingCacheEnabled() for instance.)
Instead of keeping a reference in mCanvas, just pass the canvas you receive to plotLinesAndShapes().