15
votes

Use case:

I need to draw hundred lines and a few pieces of text on my view. I need to give a scrolling effect, for which I capture the ACTION_MOVE event and redraw all the lines with the updated points. To get the desire result I tried with different approaches but none works as intended.

Approach 1

I made a custom class which extends View. All the drawing and calculation is done directly in my onDraw() method. Since there is so much of operation done in onDraw() method, the performance of the app is very poor. I even checked the performance using Profile GPU rendering and I can see the lines are very tall.

Approach 2

I created a Bitmap and after drawing all the lines onto my bitmap in another thread, I used postInvalidate() to draw the bitmap in onDraw() method:

mBufferedBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);   
mBufferedBitmap.eraseColor(Color.TRANSPARENT);    
Canvas mBufferedCanvas = new Canvas(mBufferedBitmap);               
drawLines(mBufferedCanvas)    
postInvalidate();

Since I erase all the previous drawing on the bitmap and draw new lines with updated points, there is a flickering on the screen.

Approach 3

I tried extending my custom class to SurfaceView and performing all the operations on canvas object in another thread. But since SurfaceView uses CPU for drawing operations, the performance will be poor in low configuration mobiles.

Can anyone guide me how to achieve this task with better performance?

2
Try looking into RenderScript. I have not used the framework myself, but this seems like a good use-case for it.Bryan
RenderScript wont be needed for my use case.VijayRaj
Don't allocate new bitmap in onDraw(), thats why you have fps drop.Nikola Despotoski
Don't use bitmap operation you can achieve this with Views itself if there is only lines are there.Chetan Joshi

2 Answers

8
votes

It is possible to use your approach 1 to achieve good performance.

An example that sounds close to your use case (drawing lines, a little text and having these update on gesture movement) is MPAndroidChart. This is an open source library that achieves high performance (see the following comparison if you want stats)

The classes to examine are the Renderer classes as these contain code draw in the onDraw(Canvas c) of the chart subtypes. You can see some of the tricks used to achieve high performance there:

  1. Don't allocate in a render loop. Instead, allocate outside the loop and reuse/recycle variables. See LineChartRenderer line 199
  2. Use buffering. For example, in MPAndroidChart, the points for the four corners of the bars in the bar chart are buffered and the buffer array is reused. See the BarBuffer class.
  3. Use the native Canvas drawing functions (drawPath, drawLine etc.)

A complete list of tips for optimising rendering can be found in the Android Performance Slow Rendering Guide

2
votes

Approach 2 is the best one. If you see flickering, it means that the bitmap is drawn to screen after you erase it and before you draw all the lines. If this is the case use another bitmap and do double buffering:

ScreenBitmap is what is drawn to screen OffScreenBitmap is used for drawing in background.

Draw all your lines and text to OffScreenBitmap, and once finished, copy it to ScreenBitmap. in onDraw, draw the ScreenBitmap.

Create these bitmaps once (typically in onSizeChanged), so that there is no alloocation in onDraw