2015-07-04 12 views
7

Sto creando un RecyclerView per mostrare una griglia di immagini. Quando si seleziona uno di questi, dovrebbe aprire una nuova attività con una transizione.Come risolvere "canvas: provare a utilizzare un errore di bitmap riciclato"?

Sto usando la libreria Glide per caricare le immagini e la transizione sembra orribile perché ricarica l'immagine nella nuova attività. Quindi ho dovuto salvarlo nella cache, e quindi usarlo per la transizione.

Ho il codice, ma a volte se l'immagine non viene caricata, lancia una Canvas RuntimeException.

Questo è il log:

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) 

Questo è il codice per aprire l'altra attività e salvare l'immagine come cache:

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

Che cosa potevo fare per risolvere questo problema? Grazie in anticipo.

+0

Non sono sicuro, ma penso che dovresti salvare la bitmap in un riferimento di oggetto reale e non nella cache. La cache potrebbe essere autorizzata a liberare la bitmap quando non viene più utilizzata. –

risposta

12

Sospetto una volta ogni tanto la tua bitmap entra nello stato riciclato appena prima che lo Canvas abbia la possibilità di disegnarlo qui drawable.draw(canvas);.

Una soluzione rapida dovrebbe essere quella di non chiamare bitmap.recycle();, che non è strettamente necessario per android >2.3.3. Se vuoi ancora recuperare questa memoria con forza, dovrai trovare un modo per verificare quando la bitmap non è più necessaria (ad esempio, Canvas ha la possibilità di completare le operazioni di disegno).

5

Spostare bitmap.recycle(); in un altro punto del codice in cui questa bitmap non è più necessaria.