title: How to optimize drawing text on canvas
body: <p>I have window with 3 circles, they are rotating simultaneously. Everything is good until a Add text to the circles, then the rotation starts to lagging.</p>
  
  <p><img src="https://i.stack.imgur.com/ZFiTr.png" alt="main window"></p>
  
  <p>How can i optimize drawing on canvas ?
  This is my code:</p>
  
  <pre><code>@Override
  protected void onDraw(final Canvas canvas) {
      if (mPaint == null) {
          mPaint = new Paint();
          mPaint.setTextSize(20f);
      }       
      drawUpperCircle(canvas);
      drawBottomCircle(canvas);
      drawMainCircle(canvas);
  
      try {
          Thread.sleep(1, 1);
          invalidate();
          mRotation += 0.9;
      } catch (InterruptedException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
      }
  
      super.onDraw(canvas);
  }
     private void drawUpperCircle(Canvas canvas) {
      canvas.save();
      canvas.rotate(mRotation, 0, mUpperCircleCentr);
      mPaint.setColor(Color.CYAN);
      canvas.drawCircle(0, mUpperCircleCentr, mUpperCirclRadius, mPaint);
      mPaint.setColor(Color.BLACK);
      for (int i = 0; i &lt; SEG_COUNT; i++) {
          canvas.rotate(SEG_IN_GRAD, 0, mUpperCircleCentr);
          canvas.drawLine(0, mUpperCircleCentr, mUpperCirclRadius, mUpperCircleCentr, mPaint);
          //          canvas.drawText("my text" + String.valueOf(i), mUpperCirclRadius * 2 / 3, mUpperCircleCentr - 4, mPaint);
      }
      canvas.restore();
  }
  
  private void drawBottomCircle(Canvas canvas) {
      canvas.save();
      canvas.rotate(mRotation, 0, mBottomCircleCentr);
      mPaint.setColor(Color.RED);
      canvas.drawCircle(0, mBottomCircleCentr, mBottomCirclRadius, mPaint);
      mPaint.setColor(Color.BLACK);
      for (int i = 0; i &lt; SEG_COUNT; i++) {
          canvas.rotate(SEG_IN_GRAD, 0, mBottomCircleCentr);
          canvas.drawLine(0, mBottomCircleCentr, mBottomCirclRadius, mBottomCircleCentr, mPaint);
          //          canvas.drawText("my text" + String.valueOf(i), mBottomCirclRadius * 2 / 3, mBottomCircleCentr - 4, mPaint);
      }
      canvas.restore();
  }
  
  private void drawMainCircle(Canvas canvas) {
      canvas.save();
      canvas.rotate(mRotation, 0, mMainCircleCentr);
      mPaint.setColor(Color.argb(100, 100, 100, 100));
      canvas.drawCircle(0, mMainCircleCentr, mMainCirclRadius, mPaint);
      mPaint.setColor(Color.BLACK);
      for (int i = 0; i &lt; SEG_COUNT; i++) {
          canvas.rotate(SEG_IN_GRAD, 0, mMainCircleCentr);
          canvas.drawLine(0, mMainCircleCentr, mMainCirclRadius, mMainCircleCentr, mPaint);
          canvas.drawText("my text" + String.valueOf(i), mMainCirclRadius * 2 / 3, mMainCircleCentr - 4, mPaint);
      }
      canvas.restore();
  }
  </code></pre>
  
  <p><b>EDIT</b>
  To improve performance and remove drawing from UI thread I have Used Double Buffering With <code>SurfaceView</code> and implement @Morgans optimizations. That is how it realized.</p>
  
  <p><em>DrawView.java</em></p>
  
  <pre><code>public class DrawView extends SurfaceView implements SurfaceHolder.Callback {
  
  ...............................................................
  
  public DrawView(Context context, AttributeSet attrs) {
      super(context, attrs);
      getHolder().addCallback(this);
  }
  
  @Override
  public boolean onTouchEvent(MotionEvent event) {
      float currentX = event.getX();
      float currentY = event.getY();
      float deltaX, deltaY;
      switch (event.getAction()) {
      case MotionEvent.ACTION_MOVE:
          // Modify rotational angles according to movement
          deltaX = currentX - previousX;
          deltaY = currentY - previousY;
          mDrawThread.mRotation += deltaY * 180 / getHeight();
      }
      // Save current x, y
      previousX = currentX;
      previousY = currentY;
      return true; // Event handled
  }
  
  @Override
  public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
  
  }
  
  @Override
  public void surfaceCreated(SurfaceHolder surfaceHolder) {
      mDrawThread = new DrawThread(getHolder(), this);
      mDrawThread.setRunning(true);
      mDrawThread.start();
  }
  
  @Override
  public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
      boolean retry = true;
      mDrawThread.setRunning(false);
      while (retry) {
          try {
              mDrawThread.join();
              retry = false;
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      }
  }
   }
  </code></pre>
  
  <p>And the main work is done in the <em>DrawThread.java</em></p>
  
  <pre><code>public class DrawThread extends Thread {
  
  private ArrayList&lt;Path&gt; mMainCirclePaths = new ArrayList&lt;Path&gt;(SEG_COUNT);
  private ArrayList&lt;Path&gt; mUpperCirclePaths = new ArrayList&lt;Path&gt;(SEG_COUNT);
  private ArrayList&lt;Path&gt; mCenterCirclePaths = new ArrayList&lt;Path&gt;(SEG_COUNT);
  private ArrayList&lt;Path&gt; mBottomCirclePaths = new ArrayList&lt;Path&gt;(SEG_COUNT);
  
  private boolean mRun = false;
  private SurfaceHolder mSurfaceHolder;
  private DrawView mDrawView;
  private Paint mPaint;
  
  private CirclesModel mCirclesModel;
  public float mRotation = 0;
  
  public DrawThread(SurfaceHolder surfaceHolder, DrawView drawView) {
      mSurfaceHolder = surfaceHolder;
      mDrawView = drawView;
      mCirclesModel = new CirclesModel(mDrawView.getHeight());
      mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
      mPaint.setTextSize(18f);
      initPaths();
  }
  
  public void setRunning(boolean b) {
      mRun = b;
  }
  
  @Override
  public void run() {
      while (mRun) {
          Canvas canvas = null;
          try {
              canvas = mSurfaceHolder.lockCanvas(null);
              synchronized (mSurfaceHolder) {
                  drawMainCircle(canvas);
                  mPaint.setColor(Color.WHITE);
                  canvas.drawCircle(mCirclesModel.mMainCircleCentr[CirclesModel.X], mCirclesModel.mMainCircleCentr[CirclesModel.Y],
                          mCirclesModel.mSmallCirclesRadius, mPaint);
                  drawCenterCircle(canvas);
                  drawUpperCircle(canvas);
                  drawBottomCircle(canvas);
                  //mRotation += 0.5f;
  
              }
          } finally {
              if (canvas != null) {
                  mSurfaceHolder.unlockCanvasAndPost(canvas);
              }
          }
      }
  }
  
  private void drawMainCircle(Canvas canvas) {
      canvas.save();
      canvas.rotate(mRotation, mCirclesModel.mMainCircleCentr[CirclesModel.X], mCirclesModel.mMainCircleCentr[CirclesModel.Y]);
      float rot = mRotation;
      mPaint.setColor(Color.LTGRAY/* argb(100, 255, 255, 255) */);
      canvas.drawCircle(mCirclesModel.mMainCircleCentr[CirclesModel.X], mCirclesModel.mMainCircleCentr[CirclesModel.Y],
              mCirclesModel.mBigCirclesRadius, mPaint);
      mPaint.setColor(Color.BLACK);
      for (int i = 0; i &lt; SEG_COUNT; i++) {
          canvas.rotate(SEG_IN_GRAD, mCirclesModel.mMainCircleCentr[CirclesModel.X], mCirclesModel.mMainCircleCentr[CirclesModel.Y]);
          rot += SEG_IN_GRAD;
          float absRot = Math.abs(rot % 360);
          if (absRot &gt; mCirclesModel.mMainCircleSegment[0] &amp;&amp; absRot &lt; mCirclesModel.mMainCircleSegment[1]) {
              continue;
          }
          canvas.drawLine(mCirclesModel.mMainCircleCentr[CirclesModel.X], mCirclesModel.mMainCircleCentr[CirclesModel.Y],
                  mCirclesModel.mBigCirclesRadius, mCirclesModel.mMainCircleCentr[CirclesModel.Y], mPaint);
          canvas.drawPath(mMainCirclePaths.get(i), mPaint);
          // canvas.drawText("my text" + String.valueOf(i),
          // mMainCirclRadius * 2 / 3, mMainCircleCentr - 4, mPaint);
      }
      canvas.restore();
  }
     .................................................................
  }
  </code></pre>
  
  <p>Double Buffering is implemented in the two lines of code</p>
  
  <p><code>canvas = mSurfaceHolder.lockCanvas(null);</code> here I take from surface view canvas in which i will draw next frame.</p>
  
  <p><code>mSurfaceHolder.unlockCanvasAndPost(canvas);</code> here I am overlaping current image on SurfaceView with new canwas, this is the moment where image changes. Be aware if you have transparent elements then the previous image will be still visible, Image is not replaced, but overlaped.</p>