Reputation: 3953
I am using cropping image library from https://github.com/jdamcd/android-crop for getting images from gallery and camera. It is working fine on camera if i dont change orientation after getting image from camera. But when i change the orientation after clicking image like before cropping then application crashes. Few blogs told to use android:configChanges="orientation|keyboardHidden|screenSize"
but is won't work. Even i have locked the orientation from Manifest file on the activity from where i am calling cropping method. My code and Error stack is given below:
package eu.janmuller.android.simplecropimage.example;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import eu.janmuller.android.simplecropimage.CropImage;
public class MainActivity extends Activity {
public static final String TAG = "MainActivity";
public static final String TEMP_PHOTO_FILE_NAME = "temp_photo.jpg";
private File mFileTemp;
public static final int REQUEST_CODE_GALLERY = 0x1;
public static final int REQUEST_CODE_TAKE_PICTURE = 0x2;
public static final int REQUEST_CODE_CROP_IMAGE = 0x3;
private ImageView mImageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); //To change body of overridden methods use File | Settings | File Templates.
setContentView(R.layout.main);
findViewById(R.id.gallery).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
openGallery();
}
});
findViewById(R.id.take_picture).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
takePicture();
}
});
mImageView = (ImageView) findViewById(R.id.image);
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
mFileTemp = new File(Environment.getExternalStorageDirectory(), TEMP_PHOTO_FILE_NAME);
}
else {
mFileTemp = new File(getFilesDir(), TEMP_PHOTO_FILE_NAME);
}
}
private void takePicture() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
try {
Uri mImageCaptureUri = null;
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
mImageCaptureUri = Uri.fromFile(mFileTemp);
}
else {
/*
* The solution is taken from here: http://stackoverflow.com/questions/10042695/how-to-get-camera-result-as-a-uri-in-data-folder
*/
mImageCaptureUri = InternalStorageContentProvider.CONTENT_URI;
}
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, mImageCaptureUri);
intent.putExtra("return-data", true);
startActivityForResult(intent, REQUEST_CODE_TAKE_PICTURE);
} catch (ActivityNotFoundException e) {
Log.d(TAG, "cannot take picture", e);
}
}
private void openGallery() {
Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
photoPickerIntent.setType("image/*");
startActivityForResult(photoPickerIntent, REQUEST_CODE_GALLERY);
}
private void startCropImage() {
Intent intent = new Intent(this, CropImage.class);
intent.putExtra(CropImage.IMAGE_PATH, mFileTemp.getPath());
intent.putExtra(CropImage.SCALE, true);
intent.putExtra(CropImage.ASPECT_X, 3);
intent.putExtra(CropImage.ASPECT_Y, 3);
startActivityForResult(intent, REQUEST_CODE_CROP_IMAGE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != RESULT_OK) {
return;
}
Bitmap bitmap;
switch (requestCode) {
case REQUEST_CODE_GALLERY:
try {
InputStream inputStream = getContentResolver().openInputStream(data.getData());
FileOutputStream fileOutputStream = new FileOutputStream(mFileTemp);
copyStream(inputStream, fileOutputStream);
fileOutputStream.close();
inputStream.close();
startCropImage();
} catch (Exception e) {
Log.e(TAG, "Error while creating temp file", e);
}
break;
case REQUEST_CODE_TAKE_PICTURE:
startCropImage();
break;
case REQUEST_CODE_CROP_IMAGE:
String path = data.getStringExtra(CropImage.IMAGE_PATH);
if (path == null) {
return;
}
bitmap = BitmapFactory.decodeFile(mFileTemp.getPath());
mImageView.setImageBitmap(bitmap);
break;
}
super.onActivityResult(requestCode, resultCode, data);
}
public static void copyStream(InputStream input, OutputStream output)
throws IOException {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = input.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
}
}
Error stack is given below:
12-22 23:11:03.372: E/AndroidRuntime(3617): java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@41bd8728
12-22 23:11:03.372: E/AndroidRuntime(3617): at android.graphics.Canvas.throwIfRecycled(Canvas.java:1026)
12-22 23:11:03.372: E/AndroidRuntime(3617): at android.graphics.Canvas.drawBitmap(Canvas.java:1096)
12-22 23:11:03.372: E/AndroidRuntime(3617): at android.graphics.Bitmap.createBitmap(Bitmap.java:625)
12-22 23:11:03.372: E/AndroidRuntime(3617): at eu.janmuller.android.simplecropimage.CropImage$1.prepareBitmap(CropImage.java:572)
12-22 23:11:03.372: E/AndroidRuntime(3617): at eu.janmuller.android.simplecropimage.CropImage$1.run(CropImage.java:578)
12-22 23:11:03.372: E/AndroidRuntime(3617): at eu.janmuller.android.simplecropimage.CropImage$6.run(CropImage.java:285)
12-22 23:11:03.372: E/AndroidRuntime(3617): at eu.janmuller.android.simplecropimage.Util$BackgroundJob.run(Util.java:175)
12-22 23:11:03.372: E/AndroidRuntime(3617): at java.lang.Thread.run(Thread.java:838)
Thanks in advance.!
Upvotes: 1
Views: 316
Reputation: 93561
You're trying to draw a Bitmap that's already been recycled. Looks like its int he intent you're using to crop an image- its rewriting a bitmap already destroyed. My guess is that library has a bug where it recycles a bitmap for efficiency in its onStop, but doesn't realize that Bitmap can still be being used by an AsyncTask or Thread in the library, and they aren't checking to see if its recycled first. You can probably fix his library by adding some isRecycled() checks, but my guess is there's other race conditions too. You may be able to fix it by adding that configChanges line to his library's activities, so they don't undergo Activity recreation on rotation.
But the problem is in his library, not your code.
Upvotes: 1