Je crée une RecyclerView
pour afficher une grille d'images. Lors de la sélection de l'un d'entre eux, une nouvelle activité doit être ouverte avec une transition.
J'utilise la bibliothèque Glide pour charger les images et la transition est affreuse, car elle recharge l'image dans la nouvelle activité. J'ai donc dû l'enregistrer en cache, puis l'utiliser pour la transition.
J'ai le code, mais parfois, si l'image ne se charge pas, une exception Canvas RuntimeException est levée.
Ceci est le journal:
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)
C'est le code pour ouvrir l'autre activité et enregistrer l'image en 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;
}
Que puis-je faire pour résoudre ce problème? Merci d'avance.
Je soupçonne que de temps en temps votre bitmap passe à l'état recyclé juste avant que la variable Canvas
ait la possibilité de l'utiliser ici drawable.draw(canvas);
.
Une solution rapide devrait être de ne pas appeler bitmap.recycle();
, qui n'est pas strictement requis pour Android> 2.3.3 . Si vous souhaitez toujours récupérer cette mémoire avec force, vous devrez trouver un moyen de vérifier si le bitmap n'est plus nécessaire (c'est-à-dire que Canvas
a eu la possibilité de terminer ses opérations de dessin).
Déplacez bitmap.recycle();
vers un autre endroit du code où ce bitmap n'est vraiment plus nécessaire.
Je ne connais pas beaucoup de choses sur canvas (je n'utilise pas souvent d'animations), mais si vous ne trouvez aucun moyen de résoudre ce problème, vous pouvez utiliser cette bibliothèque à la place: https://github.com/codepath/Android_guides/wiki/shared-element-activity-transition