title: Android- Method compress must be called from worker thread
body: <p>I am trying to implement an image view which displays the signature I draw in the SignatureActivity class. I am receiving an error on line:</p>
  
  <pre><code>bitmap.compress(CompressFormat.PNG, 90, mFileOutStream);
  </code></pre>
  
  <pre><code>public class SignatureActivity extends AppCompatActivity {
      Button mClear, mGetSign, mCancel;
      File file;
      LinearLayout mContent;
      View view;
      signature mSignature;
      Bitmap bitmap;
  
      // Creating Separate Directory for saving Generated Images
      String DIRECTORY = Environment.getExternalStorageDirectory().getPath() + "/UserSignature/";
      String pic_name = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
      String StoredPath = DIRECTORY + pic_name + ".png";
  
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_signature);
  
          mContent = (LinearLayout) findViewById(R.id.canvasLayout);
          mSignature = new signature(getApplicationContext(), null);
          mSignature.setBackgroundColor(Color.WHITE);
          // Dynamically generating Layout through java code
          mContent.addView(mSignature, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
          mClear = (Button) findViewById(R.id.clear);
          mGetSign = (Button) findViewById(R.id.getsign);
          mGetSign.setEnabled(false);
          mCancel = (Button) findViewById(R.id.cancel);
          view = mContent;
          mGetSign.setOnClickListener(onButtonClick);
          mClear.setOnClickListener(onButtonClick);
          mCancel.setOnClickListener(onButtonClick);
  
          // Method to create Directory, if the Directory doesn't exists
          file = new File(DIRECTORY);
          if (!file.exists()) {
              file.mkdir();
          }
      }
  
      Button.OnClickListener onButtonClick = new Button.OnClickListener() {
          @Override
          public void onClick(View v) {
              if (v == mClear) {
                  Log.v("log_tag", "Panel Cleared");
                  mSignature.clear();
                  mGetSign.setEnabled(false);
              } else if (v == mGetSign) {
                  Log.v("log_tag", "Panel Saved");
                  if (Build.VERSION.SDK_INT &gt;= 23) {
                      isStoragePermissionGranted();
                  } else {
                      view.setDrawingCacheEnabled(true);
                      mSignature.save(view, StoredPath);
                      Toast.makeText(getApplicationContext(), "Successfully Saved", Toast.LENGTH_SHORT).show();
                      // Calling the same class
                      recreate();
                  }
              } else if(v == mCancel){
                  Log.v("log_tag", "Panel Canceled");
                  // Calling the BillDetailsActivity
                  Intent intent = new Intent(SignatureActivity.this, MainActivity.class);
                  startActivity(intent);
              }
          }
      };
  
  
      public boolean isStoragePermissionGranted() {
          if (Build.VERSION.SDK_INT &gt;= 23) {
              if (getApplicationContext().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
                  return true;
              } else {
                  ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
                  return false;
              }
          } else {
              return true;
          }
      }
  
      @Override
      public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
          super.onRequestPermissionsResult(requestCode, permissions, grantResults);
          if(grantResults.length &gt; 0 &amp;&amp; grantResults[0] == PackageManager.PERMISSION_GRANTED){
  
              view.setDrawingCacheEnabled(true);
              mSignature.save(view, StoredPath);
              Toast.makeText(getApplicationContext(), "Successfully Saved", Toast.LENGTH_SHORT).show();
              // Calling the same class
              recreate();
          }
          else
          {
              Toast.makeText(this, "The app was not allowed to write to your storage. Hence, it cannot function properly. Please consider granting it this permission", Toast.LENGTH_LONG).show();
          }
      }
  
      public class signature extends View {
  
          private static final float STROKE_WIDTH = 5f;
          private static final float HALF_STROKE_WIDTH = STROKE_WIDTH / 2;
          private Paint paint = new Paint();
          private Path path = new Path();
  
          private float lastTouchX;
          private float lastTouchY;
          private final RectF dirtyRect = new RectF();
  
          public signature(Context context, AttributeSet attrs) {
              super(context, attrs);
              paint.setAntiAlias(true);
              paint.setColor(Color.BLACK);
              paint.setStyle(Paint.Style.STROKE);
              paint.setStrokeJoin(Paint.Join.ROUND);
              paint.setStrokeWidth(STROKE_WIDTH);
          }
  
          public void save(View v, String StoredPath) {
              Log.v("log_tag", "Width: " + v.getWidth());
              Log.v("log_tag", "Height: " + v.getHeight());
              if (bitmap == null) {
                  bitmap = Bitmap.createBitmap(mContent.getWidth(), mContent.getHeight(), Bitmap.Config.RGB_565);
              }
              Canvas canvas = new Canvas(bitmap);
              try {
                  // Output the file
                  FileOutputStream mFileOutStream = new FileOutputStream(StoredPath);
                  v.draw(canvas);
  
                  // Convert the output file to Image such as .png
                  bitmap.compress(CompressFormat.PNG, 90, mFileOutStream);
                  Intent intent = new Intent(SignatureActivity.this, MainActivity.class);
                  intent.putExtra("imagePath", StoredPath);
                  startActivity(intent);
                  finish();
                  mFileOutStream.flush();
                  mFileOutStream.close();
  
              } catch (Exception e) {
                  Log.v("log_tag", e.toString());
              }
  
          }
  
          public void clear() {
              path.reset();
              invalidate();
              mGetSign.setEnabled(false);
          }
  
          @Override
          protected void onDraw(Canvas canvas) {
              canvas.drawPath(path, paint);
          }
  
          @Override
          public boolean onTouchEvent(MotionEvent event) {
              float eventX = event.getX();
              float eventY = event.getY();
              mGetSign.setEnabled(true);
  
              switch (event.getAction()) {
                  case MotionEvent.ACTION_DOWN:
                      path.moveTo(eventX, eventY);
                      lastTouchX = eventX;
                      lastTouchY = eventY;
                      return true;
  
                  case MotionEvent.ACTION_MOVE:
  
                  case MotionEvent.ACTION_UP:
  
                      resetDirtyRect(eventX, eventY);
                      int historySize = event.getHistorySize();
                      for (int i = 0; i &lt; historySize; i++) {
                          float historicalX = event.getHistoricalX(i);
                          float historicalY = event.getHistoricalY(i);
                          expandDirtyRect(historicalX, historicalY);
                          path.lineTo(historicalX, historicalY);
                      }
                      path.lineTo(eventX, eventY);
                      break;
  
                  default:
                      debug("Ignored touch event: " + event.toString());
                      return false;
              }
  
              invalidate((int) (dirtyRect.left - HALF_STROKE_WIDTH),
                      (int) (dirtyRect.top - HALF_STROKE_WIDTH),
                      (int) (dirtyRect.right + HALF_STROKE_WIDTH),
                      (int) (dirtyRect.bottom + HALF_STROKE_WIDTH));
  
              lastTouchX = eventX;
              lastTouchY = eventY;
  
              return true;
          }
  
          private void debug(String string) {
  
              Log.v("log_tag", string);
  
          }
  
          private void expandDirtyRect(float historicalX, float historicalY) {
              if (historicalX &lt; dirtyRect.left) {
                  dirtyRect.left = historicalX;
              } else if (historicalX &gt; dirtyRect.right) {
                  dirtyRect.right = historicalX;
              }
  
              if (historicalY &lt; dirtyRect.top) {
                  dirtyRect.top = historicalY;
              } else if (historicalY &gt; dirtyRect.bottom) {
                  dirtyRect.bottom = historicalY;
              }
          }
  
          private void resetDirtyRect(float eventX, float eventY) {
              dirtyRect.left = Math.min(lastTouchX, eventX);
              dirtyRect.right = Math.max(lastTouchX, eventX);
              dirtyRect.top = Math.min(lastTouchY, eventY);
              dirtyRect.bottom = Math.max(lastTouchY, eventY);
          }
      }
  }
  </code></pre>
  
  <p>Any help would be appreciated. I have created a thread however it still is not working. </p>
  
  <pre><code>new Thread() {
                      @Override
                      public void run() {
                          try {
                              Thread.sleep(3000);
                          } catch (InterruptedException e) {
                              e.printStackTrace();
                          }
                          bitmap.compress(CompressFormat.PNG, 90, mFileOutStream);
                      }}.start();
  </code></pre>
  
  <p>Fixed this error by changing code for granting permission to device </p>
  
  <pre><code>Button.OnClickListener onButtonClick = new Button.OnClickListener() {
          @Override
          public void onClick(View v) {
              if (v == mClear) {
                  Log.v("log_tag", "Panel Cleared");
                  mSignature.clear();
                  mGetSign.setEnabled(false);
              } else if (v == mGetSign) {
                  Log.v("log_tag", "Panel Saved");
                  if (Build.VERSION.SDK_INT &gt;= 23 &amp;&amp; checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
                      isStoragePermissionGranted();
                  } else {
                      view.setDrawingCacheEnabled(true);
                      mSignature.save(view, StoredPath);
                      Toast.makeText(getApplicationContext(), "Successfully Saved", Toast.LENGTH_SHORT).show();
                      // Calling the same class
                      recreate();
                  }
              } else if(v == mCancel){
                  Log.v("log_tag", "Panel Canceled");
                  // Calling the BillDetailsActivity
                  Intent intent = new Intent(SignatureActivity.this, MainActivity.class);
                  startActivity(intent);
              }
          }
      };
  </code></pre>