9

I am having a dialog where users can freely draw inside the dialog.

The dialog is extending a view, and the drawing area is created by

bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);

However, the drawing need not to be kept, and when the user close the dialog and reopen again, his previous drawing is not needed to be kept and redraw from null.

Detailed codes:

DoodleView:

   // DoodleView constructor initializes the DoodleView
   public DoodleView(Context context, AttributeSet attrs) 
   {
      super(context, attrs); // pass context to View's constructor

      paintScreen = new Paint(); // used to display bitmap onto screen

      // set the initial display settings for the painted line
      paintLine = new Paint();
      paintLine.setAntiAlias(true); // smooth edges of drawn line
      paintLine.setColor(Color.BLACK); // default color is black
      paintLine.setStyle(Paint.Style.STROKE); // solid line
      paintLine.setStrokeWidth(25); // set the default line width
      paintLine.setStrokeCap(Paint.Cap.ROUND); // rounded line ends
      pathMap = new HashMap<Integer, Path>();
      previousPointMap = new HashMap<Integer, Point>();
   } // end DoodleView constructor

   // Method onSizeChanged creates BitMap and Canvas after app displays
   @Override
   public void onSizeChanged(int w, int h, int oldW, int oldH)
   {
      bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
      bitmapCanvas = new Canvas(bitmap);
      bitmap.eraseColor(Color.parseColor("#80FFFFFF")); // erase the BitMap with white
   } // end method onSizeChanged

   // clear the painting
   public void recycling()
   {
       bitmap.recycle();
   }

Writing board:

public void write_board () 
{
    writing_dialog = new Dialog(Apple.this, android.R.style.Theme_Translucent_NoTitleBar);
    WindowManager.LayoutParams lp = writing_dialog.getWindow().getAttributes();
    lp.dimAmount = 0.5f;
    writing_dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);

    Window window = writing_dialog.getWindow();
    window.setGravity(Gravity.CENTER);

    writing_dialog.setContentView(R.layout.alert_drawing_pad);
    writing_dialog.setCancelable(true);
    writing_dialog.show();
    writing_dialog.setVolumeControlStream(AudioManager.STREAM_MUSIC);   

    doodleView = (DoodleView) writing_dialog.findViewById(R.id.doodleView);

        alert_close.setOnClickListener(new OnClickListener() 
        {
            public void onClick(View v) 
            {
                writing_dialog.dismiss();
                doodleView.recycling();
                return;
            }
        });

Question:

I discover that after opening the dialog several times (7 to 8 times using Samsung Note2), the dialog would lag for a few second and the phone without response, and then further press again some while later, the dialog can appear again and everything is ok.

The logcat at this time reports E/OpenGLRenderer(25296): Out of memory!

and it goes on without hanging for some time.

Yet if further press out for the drawing board, it goes with a serious memory error and this time the system hangs.

FATAL EXCEPTION: main
java.lang.OutOfMemoryError
    at android.graphics.Bitmap.nativeCreate(Native Method)
    at android.graphics.Bitmap.createBitmap(Bitmap.java:726)
    at android.graphics.Bitmap.createBitmap(Bitmap.java:703)
    at android.graphics.Bitmap.createBitmap(Bitmap.java:670)
    at com.abc.abc.DoodleView.onSizeChanged(DoodleView.java:60)
    at android.view.View.sizeChange(View.java:15326)
    at android.view.View.setFrame(View.java:15290)
    at android.view.View.layout(View.java:15201)
    at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1076)
    at android.view.View.layout(View.java:15204)
    at android.view.ViewGroup.layout(ViewGroup.java:4793)
    at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1076)
    at android.view.View.layout(View.java:15204)
    at android.view.ViewGroup.layout(ViewGroup.java:4793)
    at android.widget.FrameLayout.onLayout(FrameLayout.java:448)
    at android.view.View.layout(View.java:15204)
    at android.view.ViewGroup.layout(ViewGroup.java:4793)
    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1677)
    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1531)
    at android.widget.LinearLayout.onLayout(LinearLayout.java:1440)
    at android.view.View.layout(View.java:15204)
    at android.view.ViewGroup.layout(ViewGroup.java:4793)
    at android.widget.FrameLayout.onLayout(FrameLayout.java:448)
    at android.view.View.layout(View.java:15204)
    at android.view.ViewGroup.layout(ViewGroup.java:4793)
    at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2263)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2009)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1251)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6379)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:791)
    at android.view.Choreographer.doCallbacks(Choreographer.java:591)
    at android.view.Choreographer.doFrame(Choreographer.java:561)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:777)
    at android.os.Handler.handleCallback(Handler.java:730)
    at android.os.Handler.dispatchMessage(Handler.java:92)
    at android.os.Looper.loop(Looper.java:137)
    at android.app.ActivityThread.main(ActivityThread.java:5493)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:525)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1209)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1025)
    at dalvik.system.NativeStart.main(Native Method)

I have researched and added
bitmap.recycle(); whenever the user closes the dialog. But it still reports the same error.

Are there any way to dispose the bitmap whenever the user closes the dialog?

Thanks!

JJD
  • 50,076
  • 60
  • 203
  • 339
pearmak
  • 4,979
  • 15
  • 64
  • 122
  • Please post the code that shows how you are invoking the dialog and handling drawing the contents of the dialog. You are probably keeping a reference to the dialog or bitmap, or some other object preventing GC. – broschb Dec 21 '13 at 04:38
  • thanks for your prompt help! I have addded detailed relevant code to the question... thanks! – pearmak Dec 21 '13 at 04:45

4 Answers4

21

You should call this on dismiss of your dialog

bitmap.recycle();
bitmap = null;

bitmap.recycle(); releases the native heap that is used in bitmaps. And setting it to null is to assist the GC to quickly collect your reference.

Pang
  • 9,564
  • 146
  • 81
  • 122
Rizwan
  • 1,461
  • 12
  • 26
  • thanks for your help! I have tried adding bitmap = null yet it still pops `java.lang.OutOfMemoryError`... – pearmak Dec 21 '13 at 09:32
10

First invalidate the reference that canvas or any other object has to bitmap

if (bitmapCanvas != null) {
    bitmapCanvas.setBitmap(null);
    bitmapCanvas = null;
}

Second release bitmap resources

if (bitmap != null) {
    bitmap.recycle();
    bitmap = null;
}

If needed, Third, keep the bitmap as WeakReference

WeakReference<Bitmap> bitmap;
if (bitmap != null) {
    bitmap.get().recycle();
    bitmap.clear();
    bitmap= null;
}
JJD
  • 50,076
  • 60
  • 203
  • 339
Brcinho
  • 321
  • 5
  • 10
3

It looks like there should only be one bitmap in memory at a time. A few thoughts.

First when recycling the bitmap set it to null, also set the canvas to null. If this does not solve the issue, i would be curious to see the code you are using to actually paint the lines. Also getting a heap dump after invoking the dialog and closing it will show you which objects are still in memory, and allow you to quickly track down what is holding on to the bitmap, and it's kinda fun :)

This post is a great start on investigating a heap dump if you have not done it before.

UPDATE

To set the canvas to null, you can do something like this

public void recycling()
{
   bitmap.recycle();
   bitmap = null;
   bitmapCanvas = null;
}

However upon closer inspection of your code, i see that you are creating a new dialog everytime and then dismissing the dialog. This will not free up the dialog from memory. Try updating your code so you only initialize the dialog if it is null.

public void write_board () 
 {
  if(writing_dialog == null){
    writing_dialog = new Dialog(Apple.this,         android.R.style.Theme_Translucent_NoTitleBar);
    WindowManager.LayoutParams lp = writing_dialog.getWindow().getAttributes();
    lp.dimAmount = 0.5f;
    writing_dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);

    Window window = writing_dialog.getWindow();
    window.setGravity(Gravity.CENTER);

    writing_dialog.setContentView(R.layout.alert_drawing_pad);
    writing_dialog.setCancelable(true);
    writing_dialog.setVolumeControlStream(AudioManager.STREAM_MUSIC);
 }
 //now do stuff with your dialog
 writing_dialog.show();


 doodleView = (DoodleView) writing_dialog.findViewById(R.id.doodleView);
broschb
  • 4,976
  • 4
  • 35
  • 52
  • how to set the canvas to null? – pearmak Dec 21 '13 at 11:06
  • many thanks! I have just tried your suggested updates by adding `bitmapCanvas = null`, it is now performing better, in a way that it still triggers the less serious `E/OpenGLRenderer(25296): Out of memory!` but will not trigger fatal `java.lang.OutOfMemoryError`...yet for further information, actually besides apple class, for example, I have some more classes (eg, apple, banana, cake) that also pops up a writing board. within the same class, it will not trigger `out of memory` but running across class A then B then C till K it will trigger...the dialog should have recycled across activities? – pearmak Dec 21 '13 at 17:44
0

Bitmap memory issues a pretty common problem. Try out some of the solutions in this thread

Strange out of memory issue while loading an image to a Bitmap object

Community
  • 1
  • 1
AJD
  • 1,017
  • 1
  • 9
  • 14
  • Thanks i know about if getting bitmap image it is necessary to decode by calculating min sample pixels...yet now here seems a bit different, it is a drawing plate creating from null based on plate height and width, without loading any image, and i would like to kick off 'dispose' or 'recycle' the memory whenever the user closes the dialog.. – pearmak Dec 21 '13 at 05:12