webentwicklung-frage-antwort-db.com.de

Wie behebt man "canvas: Versuch, einen recycelten Bitmap-Fehler zu verwenden"?

Ich erstelle eine RecyclerView, um ein Gitter mit Bildern anzuzeigen. Wenn Sie eine davon auswählen, sollte eine neue Aktivität mit einem Übergang geöffnet werden.

Ich verwende die Glide-Bibliothek, um die Bilder zu laden, und der Übergang sieht schrecklich aus, weil das Bild in der neuen Aktivität neu geladen wird. Also musste ich es im Cache speichern und dann für den Übergang verwenden.

Ich habe den Code, aber manchmal, wenn das Bild nicht geladen wird, löst es eine Canvas RuntimeException aus.

Dies ist das Protokoll:

07-03 15:19:58.633  28461-28461/jahirfiquitiva.project E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: jahirfiquitiva.project, PID: 28461
    Java.lang.RuntimeException: Canvas: trying to use a recycled bitmap [email protected]
            at Android.graphics.Canvas.throwIfCannotDraw(Canvas.Java:1282)
            at Android.view.GLES20Canvas.drawBitmap(GLES20Canvas.Java:599)
            at Android.graphics.drawable.BitmapDrawable.draw(BitmapDrawable.Java:538)
            at Android.widget.ImageView.onDraw(ImageView.Java:1176)
            at Android.view.View.draw(View.Java:15239)
            at Android.view.View.updateDisplayListIfDirty(View.Java:14175)
            at Android.view.View.getDisplayList(View.Java:14197)
            at Android.view.GhostView.onDraw(GhostView.Java:52)
            at Android.view.View.draw(View.Java:15239)
            at Android.view.View.updateDisplayListIfDirty(View.Java:14175)
            at Android.view.View.getDisplayList(View.Java:14197)
            at Android.view.View.draw(View.Java:14967)
            at Android.view.ViewGroup.drawChild(ViewGroup.Java:3406)
            at Android.view.ViewGroup.dispatchDraw(ViewGroup.Java:3199)
            at Android.view.View.updateDisplayListIfDirty(View.Java:14170)
            at Android.view.View.getDisplayList(View.Java:14197)
            at Android.view.View.draw(View.Java:14967)
            at Android.view.ViewGroup.drawChild(ViewGroup.Java:3406)
            at Android.view.ViewGroup.dispatchDraw(ViewGroup.Java:3199)
            at Android.view.ViewOverlay$OverlayViewGroup.dispatchDraw(ViewOverlay.Java:219)
            at Android.view.View.draw(View.Java:15248)
            at Android.widget.FrameLayout.draw(FrameLayout.Java:598)
            at com.Android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.Java:2906)
            at Android.view.View.updateDisplayListIfDirty(View.Java:14175)
            at Android.view.View.getDisplayList(View.Java:14197)
            at Android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.Java:273)
            at Android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.Java:279)
            at Android.view.ThreadedRenderer.draw(ThreadedRenderer.Java:318)
            at Android.view.ViewRootImpl.draw(ViewRootImpl.Java:2536)
            at Android.view.ViewRootImpl.performDraw(ViewRootImpl.Java:2352)
            at Android.view.ViewRootImpl.performTraversals(ViewRootImpl.Java:1982)
            at Android.view.ViewRootImpl.doTraversal(ViewRootImpl.Java:1061)
            at Android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.Java:5891)
            at Android.view.Choreographer$CallbackRecord.run(Choreographer.Java:767)
            at Android.view.Choreographer.doCallbacks(Choreographer.Java:580)
            at Android.view.Choreographer.doFrame(Choreographer.Java:550)
            at Android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.Java:753)
            at Android.os.Handler.handleCallback(Handler.Java:739)
            at Android.os.Handler.dispatchMessage(Handler.Java:95)
            at Android.os.Looper.loop(Looper.Java:135)
            at Android.app.ActivityThread.main(ActivityThread.Java:5289)
            at Java.lang.reflect.Method.invoke(Native Method)
            at Java.lang.reflect.Method.invoke(Method.Java:372)
            at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:904)
            at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:699)

Dies ist der Code, um die andere Aktivität zu öffnen und das Bild als Cache zu speichern:

private void openViewer(WallpapersAdapter.WallsHolder wallsHolder, int index, final HashMap<String, String> data) {

        final Intent intent = new Intent(wallsActivity, ViewerActivity.class);
        intent.putExtra("wallUrl", data.get(WallpapersActivity.WALL));
        intent.putExtra("wallName", data.get(WallpapersActivity.NAME));
        intent.putExtra("transitionName", ViewCompat.getTransitionName(wallsHolder.wall));

        //save image from drawable
        //get its path and send it to activity
        Bitmap bitmap = drawableToBitmap(wallsHolder.wall.getDrawable());
        //Convert to byte array and send to the other activity

        Log.e("Resolution", bitmap.getWidth() + "x" + bitmap.getHeight());
        try {
            //Write file
            String filename = "bitmap.png";
            FileOutputStream stream = this.openFileOutput(filename, Context.MODE_PRIVATE);
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);

            //Cleanup
            stream.close();
            bitmap.recycle();

            //Pop intent
            intent.putExtra("image", filename);
        } catch (Exception e) {
            e.printStackTrace();
        }

        ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
                this, wallsHolder.wall, ViewCompat.getTransitionName(wallsHolder.wall));
        startActivity(intent, options.toBundle());

    }

    public static Bitmap drawableToBitmap (Drawable drawable) {
        Bitmap bitmap = null;
        if (drawable instanceof BitmapDrawable) {
            BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
            if(bitmapDrawable.getBitmap() != null) {
                return bitmapDrawable.getBitmap();
            }
        }

        if(drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
            bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888); // Single color bitmap will be created of 1x1 pixel
        } else {
            bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        }

        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
        drawable.draw(canvas);
        return bitmap;
    }

Was kann ich tun, um dieses Problem zu beheben? Danke im Voraus.

7
Jahir Fiquitiva

Ich vermute, dass Ihre Bitmap gelegentlich in den recycelten Zustand gerät, bevor Canvas hier die Gelegenheit hat, darauf zu ziehen drawable.draw(canvas);.

Eine schnelle Lösung sollte sein, bitmap.recycle(); nicht aufzurufen, was für Android> 2.3.3 nicht unbedingt erforderlich ist. Wenn Sie diesen Speicher dennoch zwangsweise zurückgewinnen möchten, müssen Sie einen Weg finden, zu überprüfen, wann die Bitmap tatsächlich nicht mehr benötigt wird (d. H. Canvas hatte die Möglichkeit, die Zeichenoperationen zu beenden).

12
AndroidEx

Verschieben Sie bitmap.recycle(); an eine andere Stelle im Code, an der diese Bitmap wirklich nicht mehr benötigt wird.

6
sakiM

Ich weiß nicht viel über die Leinwand (häufig verwende ich keine Animationen), aber wenn Sie keine Möglichkeit finden, das Problem zu beheben, können Sie stattdessen diese Bibliothek verwenden: https://github.com/codepath/Android_guides/Wiki/Shared-Element-Aktivitätsübergang

0
James Fenn