0

I'm developing an application which receives events from other application (login attempts), and then it shows the result of the login process in a dialog fragment. The login is done with Fingerprint recognition so in the result I need to show the fingerprint image (which is a bitmap).

The dialog works well and I get no errors, but the problem is: if the user attempts to log in very fast sometimes I'm getting this error while displaying the image:

W/dalvikvm( 1830): threadid=1: thread exiting with uncaught exception (group=0x2bf4efc0)
E/AndroidRuntime( 1830): FATAL EXCEPTION: main
E/AndroidRuntime( 1830): java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@2cd4a720
E/AndroidRuntime( 1830):    at android.graphics.Canvas.throwIfRecycled(Canvas.java:1026)
E/AndroidRuntime( 1830):    at android.graphics.Canvas.drawBitmap(Canvas.java:1127)
E/AndroidRuntime( 1830):    at android.graphics.drawable.BitmapDrawable.draw(BitmapDrawable.java:393)
E/AndroidRuntime( 1830):    at android.view.View.draw(View.java:13443)
E/AndroidRuntime( 1830):    at android.view.View.draw(View.java:13342)
E/AndroidRuntime( 1830):    at android.view.ViewGroup.drawChild(ViewGroup.java:2929)
E/AndroidRuntime( 1830):    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2799)
E/AndroidRuntime( 1830):    at android.view.View.draw(View.java:13461)
E/AndroidRuntime( 1830):    at android.view.View.draw(View.java:13342)
E/AndroidRuntime( 1830):    at android.view.ViewGroup.drawChild(ViewGroup.java:2929)
E/AndroidRuntime( 1830):    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2799)
E/AndroidRuntime( 1830):    at android.view.View.draw(View.java:13461)
E/AndroidRuntime( 1830):    at android.view.View.draw(View.java:13342)
E/AndroidRuntime( 1830):    at android.view.ViewGroup.drawChild(ViewGroup.java:2929)
E/AndroidRuntime( 1830):    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2799)
E/AndroidRuntime( 1830):    at android.view.View.draw(View.java:13340)
E/AndroidRuntime( 1830):    at android.view.ViewGroup.drawChild(ViewGroup.java:2929)
E/AndroidRuntime( 1830):    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2799)
E/AndroidRuntime( 1830):    at android.view.View.draw(View.java:13340)
E/AndroidRuntime( 1830):    at android.view.ViewGroup.drawChild(ViewGroup.java:2929)
E/AndroidRuntime( 1830):    at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2799)
E/AndroidRuntime( 1830):    at android.view.View.draw(View.java:13461)
E/AndroidRuntime( 1830):    at android.widget.FrameLayout.draw(FrameLayout.java:467)
E/AndroidRuntime( 1830):    at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:2188)
E/AndroidRuntime( 1830):    at android.view.ViewRootImpl.drawSoftware(ViewRootImpl.java:2326)
E/AndroidRuntime( 1830):    at android.view.ViewRootImpl.draw(ViewRootImpl.java:2221)
E/AndroidRuntime( 1830):    at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2089)
E/AndroidRuntime( 1830):    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1864)
E/AndroidRuntime( 1830):    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1032)
E/AndroidRuntime( 1830):    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4282)
E/AndroidRuntime( 1830):    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)
E/AndroidRuntime( 1830):    at android.view.Choreographer.doCallbacks(Choreographer.java:555)
E/AndroidRuntime( 1830):    at android.view.Choreographer.doFrame(Choreographer.java:525)
E/AndroidRuntime( 1830):    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)
E/AndroidRuntime( 1830):    at android.os.Handler.handleCallback(Handler.java:615)
E/AndroidRuntime( 1830):    at android.os.Handler.dispatchMessage(Handler.java:92)
E/AndroidRuntime( 1830):    at android.os.Looper.loop(Looper.java:137)
E/AndroidRuntime( 1830):    at android.app.ActivityThread.main(ActivityThread.java:4745)
E/AndroidRuntime( 1830):    at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime( 1830):    at java.lang.reflect.Method.invoke(Method.java:511)
E/AndroidRuntime( 1830):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
E/AndroidRuntime( 1830):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
E/AndroidRuntime( 1830):    at dalvik.system.NativeStart.main(Native Method)

My code to show / recycle the image is this:

public class FingerprintSettableImage implements SettableResultImage {

    private final FingerPrintImageModel image;

    private static Object lock = new Object();


    private static final String TAG = FingerprintSettableImage.class
            .getCanonicalName();

    private static Bitmap previousFPImage = null;

    public FingerprintSettableImage(final FingerPrintImageModel image) {
        this.image = image;
    }

    @SuppressLint("NewApi")
    @Override
    public void bindToView(final ImageView view) {

        synchronized (lock) {

            if (image == null) {
                view.setImageResource(R.drawable.drw_login_finger);
            } else {
                final Bitmap aux = ImageHelper.renderCroppedGreyscaleBitmap(
                        image.image, image.width, image.height);
                final int fpImageDisplayRule = FPReaderSettings
                        .getImageDisplayRules(view.getContext());
                if (fpImageDisplayRule == FPReaderSettings.DISPLAY_FP_IMAGE_FOR_PUNCH_AND_ENROLLMENT) {

                    Drawable d = new BitmapDrawable(view.getContext()
                            .getResources(), aux);
                    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
                        view.setBackgroundDrawable(d);
                    } else {
                        view.setBackground(d);
                    }
                } else {
                    view.setBackgroundResource(R.drawable.drw_login_default_finger_image);
                }
                view.setImageResource(R.drawable.drw_login_finger_transparent);

                recycle();
                previousFPImage = aux;
            }
        }

    }

    @Override
    public void recycle() {
        if (previousFPImage != null && !previousFPImage.isRecycled()) {
            Log.d(TAG, "Recycling image " + previousFPImage.hashCode());
            previousFPImage.recycle();
        }
    }
}

To display the image we use:

    public class ImageHelper {

    /**
     * Using the array generate a Bitmap object
     * 
     * @param yuvData
     * @param width
     * @param height
     * @return
     */
    public static Bitmap renderCroppedGreyscaleBitmap(byte[] yuvData,
            final int width, final int height) {
        int[] pixels = new int[width * height];
        int inputOffset = 0;

        for (int y = 0; y < height; y++) {
            final int outputOffset = y * width;
            for (int x = 0; x < width; x++) {
                final int grey = yuvData[inputOffset + x] & 0xff;
                pixels[outputOffset + x] = 0xFF000000 | (grey * 0x00010101);
            }
            inputOffset += width;
        }

        final Bitmap bitmap = Bitmap.createBitmap(width, height,
                Bitmap.Config.RGB_565);
        bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
        yuvData = null;
        pixels = null;
        return bitmap;
    }
}

I use the lock to try to ensure that only one image is loading at a time.

My relevant code of the dialog fragment is this:

@Override
public View onCreateView(final LayoutInflater inflater,
            final ViewGroup container, final Bundle savedInstanceState) {
        ...
        setResultImage();
        ...
    }

private void setResultImage() {
    FingerPrintImageModel fpImageModel = curLC.getFingerImage();
    image = new FingerprintSettableImage(fpImageModel);
    Log.d(TAG, "binding image" + aux.getTimestamp());
    image.bindToView(imageFP1);
}

Maybe I'm doing some silly mistake, but I can't find it, can somebody see what's wrong?

Guillermo Merino
  • 3,197
  • 2
  • 17
  • 34
  • can you post ImageHelper.renderCroppedGreyscaleBitmap? – Blackbelt Mar 31 '14 at 09:52
  • I'm on my mobile, so cant track down the exact mistake right know but Im pretty sure you are recycling your Bitmap in the wrong place in your code. Debug your code and set breakpoints where you recycle and create your Bitmap – A Honey Bustard May 08 '15 at 22:24

0 Answers0