web-dev-qa-db-ja.com

「キャンバス:リサイクルされたビットマップエラーを使用しようとしています」を修正するにはどうすればよいですか?

写真のグリッドを表示するためにRecyclerViewを作成しています。それらの1つを選択すると、トランジション付きの新しいアクティビティが開きます。

Glideライブラリを使用して画像を読み込んでいますが、新しいアクティビティで画像が再読み込みされるため、遷移がひどく見えます。そのため、キャッシュに保存してから、移行に使用する必要がありました。

コードはありますが、画像が読み込まれない場合、CanvasRuntimeExceptionがスローされることがあります。

これはログです:

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 Android.graphics.Bitmap@29f09d20
            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)

これは、他のアクティビティを開き、画像をキャッシュとして保存するためのコードです。

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;
    }

この問題を解決するにはどうすればよいですか?前もって感謝します。

7
Jahir Fiquitiva

Canvasがここdrawable.draw(canvas);に描画する機会を得る直前に、ビットマップがリサイクル状態になることがあると思います。

簡単な解決策は、bitmap.recycle();を呼び出さないことです。これは、 Android> 2.3. では厳密には必要ありません。それでもこのメモリを強制的に再利用したい場合は、ビットマップが実際に不要になったときを確認する方法を見つける必要があります(つまり、Canvasは描画操作を終了する機会がありました)。

13
AndroidEx

bitmap.recycle();を、このビットマップが実際には不要になったコード内の別の場所に移動します。

5
sakiM

キャンバスについてはよくわかりませんが(アニメーションはあまり使用しません)、これを修正する方法が見つからない場合は、代わりにこのライブラリを使用してみてください: https://github.com/codepath/Android_guides/wiki/shared-element-activity-transition

0
James Fenn