Bitmap Out of Memory

Scantech

Well-Known Member
Licensed User
Longtime User
java.lang.OutOfMemoryError: bitmap size exceeds VM budget
at android.graphics.BitmapFactory.nativeDecodeAsset(BitmapFactory.java:-2)
at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:447)
at anywheresoftware.b4a.objects.drawable.CanvasWrapper$BitmapWrapper.InitializeSample(CanvasWrapper.java:520)
at Scantech.CarGaugePro.main._analoggaugeadd(main.java:683)
at Scantech.CarGaugePro.main._paneladd_btnaddgauge_click(main.java:3861)
at java.lang.reflect.Method.invokeNative(Method.java:-2)
at java.lang.reflect.Method.invoke(Method.java:521)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:105)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:93)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:89)
at anywheresoftware.b4a.objects.ViewWrapper$1.onClick(ViewWrapper.java:49)
at android.view.View.performClick(View.java:2364)
at android.view.View.onTouchEvent(View.java:4179)
at android.widget.TextView.onTouchEvent(TextView.java:6534)
at android.view.View.dispatchTouchEvent(View.java:3709)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:883)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:883)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:883)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:883)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1659)
at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1107)
at android.app.Activity.dispatchTouchEvent(Activity.java:2061)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1643)
at android.view.ViewRoot.handleMessage(ViewRoot.java:1691)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4338)
at java.lang.reflect.Method.invokeNative(Method.java:-2)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
at dalvik.system.NativeStart.main(NativeStart.java:-2)



I can not duplicate this on my device. Any tips on what to look for?
 
Last edited:

Scantech

Well-Known Member
Licensed User
Longtime User
Here is another one! The bitmap images are 100KB. Not sure how many Gauges they are adding. This error have been reported few times.

java.lang.OutOfMemoryError: bitmap size exceeds VM budget
at android.graphics.Bitmap.nativeCreate(Bitmap.java:-2)
at android.graphics.Bitmap.createBitmap(Bitmap.java:468)
at anywheresoftware.b4a.objects.drawable.CanvasWrapper.Initialize(CanvasWrapper.java:71)
at Scantech.CarGaugePro.main._analoggaugedraw(main.java:730)
at Scantech.CarGaugePro.main._panelcontrol_sbgaugescale_valuechanged(main.java:4105)
at java.lang.reflect.Method.invokeNative(Method.java:-2)
at java.lang.reflect.Method.invoke(Method.java:521)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:105)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:93)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:89)
at anywheresoftware.b4a.objects.SeekBarWrapper$1.onProgressChanged(SeekBarWrapper.java:38)
at android.widget.SeekBar.onProgressRefresh(SeekBar.java:89)
at android.widget.ProgressBar.doRefreshProgress(ProgressBar.java:495)
at android.widget.ProgressBar.refreshProgress(ProgressBar.java:504)
at android.widget.ProgressBar.setProgress(ProgressBar.java:553)
at android.widget.AbsSeekBar.trackTouchEvent(AbsSeekBar.java:337)
at android.widget.AbsSeekBar.onTouchEvent(AbsSeekBar.java:296)
at android.view.View.dispatchTouchEvent(View.java:3709)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:883)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:883)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:883)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:883)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1659)
at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1107)
at android.app.Activity.dispatchTouchEvent(Activity.java:2061)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1643)
at android.view.ViewRoot.handleMessage(ViewRoot.java:1691)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4338)
at java.lang.reflect.Method.invokeNative(Method.java:-2)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
at dalvik.system.NativeStart.main(NativeStart.java:-2)
 
Upvote 0

Scantech

Well-Known Member
Licensed User
Longtime User
You are initializing the canvas each time the progress bar updates? This can result in many bitmaps created.

Yes, I did not know this will be a problem. Progressbar is used to increase/decrease the gauges size and the event does initialize the canvas. I will post the code on those events and tell me what you think.
 
Upvote 0

Scantech

Well-Known Member
Licensed User
Longtime User
B4X:
Sub PanelControl_sbGaugeScale_ValueChanged (Value As Int, UserChanged As Boolean)
   Try
      If Value <= 0 Then Return
      Dim Index As Int: Index = PanelControl_spnIndex.SelectedIndex
   
      'GAUGE SIZE
      imvGauge(Index).Width = value:  imvGauge(PanelControl_spnIndex.SelectedIndex).Height = value
      
      'SET FILL TYPE AND CLEAR IMAGE
      imvGauge(Index).Gravity = Gravity.FILL
      imvGauge(Index).Color=Colors.Transparent
   
      'NEEDLE SIZE
      imvNeedle(Index).Width = value: imvNeedle(PanelControl_spnIndex.SelectedIndex).Height = value
   
      'SET FILL TYPE AND CLEAR IMAGE
      imvNeedle(Index).Gravity = Gravity.FILL
      imvNeedle(Index).Color=Colors.Transparent
      
      If GaugeType(Index) = 0 Then   
         'CALL GAUGE AND NEEDLE DRAW EVENT
         AnalogGaugeDraw(False, Index, Value, GaugeScale_Value(0,Index), GaugeScale_Value(10,Index), GaugeScale_Value(11,Index), GaugeScale_Value(12,Index), GaugeScale_Style(Index), GaugeScale_FontColor(Index), GaugeScalePidUnit_FontColor(Index), GaugePidLabel_Left(Index), GaugePidLabel_Top(Index), GaugeUnitLabel_Left(Index), GaugeUnitLabel_Top(Index))
         AnalogNeedleDraw(False, Index, Value)
      Else
         DigitalGaugeDraw(False, Index, Value, GaugeScale_Value(11,Index), GaugeScale_Value(12,Index), GaugeScale_FontColor(Index), GaugeScalePidUnit_FontColor(Index))
      End If
      
      'SLIDERS MAX VALUE = WIDTH AND HEIGHT OF ACTIVITY
      PanelControl_sbGaugeLeft.Max = Activity.Width + imvGauge(Index).Width
      PanelControl_sbGaugeTop.Max  = Activity.Height + imvGauge(Index).Height
      
      'OFFSET COMPENSATER FOR IMAGES TO GO OFF SCREEN
      OffsetSliders = imvGauge(Index).Width
      
      label5.Text = "Gauge Size " & Value
   Catch
      Msgbox("Gauge Control Scale Error","")
   End Try

End Sub
 
Upvote 0

Scantech

Well-Known Member
Licensed User
Longtime User
B4X:
Sub AnalogGaugeDraw(Demo As Boolean, GaugeIndex As Int, Size1 As Int, ScaleMinValue As Double, ScaleMaxValue As Double, gPidLabel As String, gUnitLabel As String, gScaleStyle As Int,  gScaleFontColor As String, gScalePidUnitColor As String, gPidLabelLeft As Double, gPidLabelTop As Double, gUnitLabelLeft As Double, gUnitLabelTop As Double)
   Try
      GaugeSize(GaugeIndex) = Size1
      
      If Demo = False Then
         'INITIALIZE CANVAS TO GAUGE IMAGE
         csvGauge(GaugeIndex).Initialize(imvGauge(GaugeIndex))
      Else
         'INITIALIZE CANVAS TO DEMO GAUGE IMAGE
         csvGauge(GaugeIndex).Initialize(imvGaugeDemo)
      End If

      SRectGauge(GaugeIndex).Initialize(0, 0, bmpGauge(GaugeIndex).Width, bmpGauge(GaugeIndex).Height)
      DRectGauge(GaugeIndex).Initialize(0, 0, Size1, Size1)
         
      'DRAWS THE GAUGE CONTAINER
      csvGauge(GaugeIndex).DrawBitmap(bmpGauge(GaugeIndex),SRectGauge(GaugeIndex),DRectGauge(GaugeIndex))   
      
      '-----------------------------------------GAUGE TEXT AREA-----------------------------------------------------------
      Dim i As Int
      Dim TotalScale, IncrementValue, SetValue, ts As Float 'ts = text size
         
      'CALCULATE VALUES FOR EACH SCALE LABEL
      TotalScale = ScaleMaxValue - ScaleMinValue
      IncrementValue = TotalScale/10
      For i = 0 To 10 
         GaugeScale_Value(i,GaugeIndex) = NumberFormat(ScaleMinValue + SetValue,1,1)
         SetValue = SetValue + IncrementValue
      Next
      
      GaugeScale_Value(11,GaugeIndex) = gPidLabel
      GaugeScale_Value(12,GaugeIndex) = gUnitLabel
         
      '=============================================CHANGED UNITS AND ADJUSTED=====================================================================
      ts = Size1 / 14 / Density   'USED WITH 0-10 LABELS
   
      Select Case gScaleStyle
         Case 0
            'DRAW GAUGE SCALE LABEL (0-10)
            csvGauge(GaugeIndex).DrawText(GaugeScale_Value(0,GaugeIndex),Size1*.35, Size1*.78,Typeface.DEFAULT,ts,gScaleFontColor,"LEFT")
            csvGauge(GaugeIndex).DrawText(GaugeScale_Value(1,GaugeIndex),Size1*.25, Size1*.70,Typeface.DEFAULT,ts,gScaleFontColor,"LEFT")
            csvGauge(GaugeIndex).DrawText(GaugeScale_Value(2,GaugeIndex),Size1*.18, Size1*.56,Typeface.DEFAULT,ts,gScaleFontColor,"LEFT")
            csvGauge(GaugeIndex).DrawText(GaugeScale_Value(3,GaugeIndex),Size1*.21, Size1*.41,Typeface.DEFAULT,ts,gScaleFontColor,"LEFT")
            csvGauge(GaugeIndex).DrawText(GaugeScale_Value(4,GaugeIndex),Size1*.34, Size1*.28,Typeface.DEFAULT,ts,gScaleFontColor,"CENTER")
            csvGauge(GaugeIndex).DrawText(GaugeScale_Value(5,GaugeIndex),Size1*.50, Size1*.22,Typeface.DEFAULT,ts,gScaleFontColor,"CENTER")
            csvGauge(GaugeIndex).DrawText(GaugeScale_Value(6,GaugeIndex),Size1*.66, Size1*.28,Typeface.DEFAULT,ts,gScaleFontColor,"CENTER")
            csvGauge(GaugeIndex).DrawText(GaugeScale_Value(7,GaugeIndex),Size1*.79, Size1*.41,Typeface.DEFAULT,ts,gScaleFontColor,"RIGHT")
            csvGauge(GaugeIndex).DrawText(GaugeScale_Value(8,GaugeIndex),Size1*.82, Size1*.56,Typeface.DEFAULT,ts,gScaleFontColor,"RIGHT")
            csvGauge(GaugeIndex).DrawText(GaugeScale_Value(9,GaugeIndex),Size1*.75, Size1*.70,Typeface.DEFAULT,ts,gScaleFontColor,"RIGHT")
            csvGauge(GaugeIndex).DrawText(GaugeScale_Value(10,GaugeIndex),Size1*.65,Size1*.78,Typeface.DEFAULT,ts,gScaleFontColor,"RIGHT")
            
            'DRAW GAUGE PID LABEL (PID NAME)
            csvGauge(GaugeIndex).DrawText(GaugeScale_Value(11,GaugeIndex), Size1/gPidLabelLeft, Size1/gPidLabelTop, Typeface.DEFAULT, Size1/13/Density, gScalePidUnitColor,"CENTER")
            'DRAW GAUGE UNIT LABEL (UNITS)
            csvGauge(GaugeIndex).DrawText(GaugeScale_Value(12,GaugeIndex), Size1/gUnitLabelLeft, Size1/gUnitLabelTop, Typeface.DEFAULT, Size1/13/Density, gScalePidUnitColor,"CENTER")
   
         Case 1
            'DRAW GAUGE SCALE LABEL (0-10)
            csvGauge(GaugeIndex).DrawTextRotated(GaugeScale_Value(0,GaugeIndex),Size1*.40, Size1*.80,Typeface.DEFAULT,ts,gScaleFontColor,"CENTER",25)
            csvGauge(GaugeIndex).DrawTextRotated(GaugeScale_Value(1,GaugeIndex),Size1*.25, Size1*.69,Typeface.DEFAULT,ts,gScaleFontColor,"CENTER",50)
            csvGauge(GaugeIndex).DrawTextRotated(GaugeScale_Value(2,GaugeIndex),Size1*.23, Size1*.53,Typeface.DEFAULT,ts,gScaleFontColor,"CENTER",270)
            csvGauge(GaugeIndex).DrawTextRotated(GaugeScale_Value(3,GaugeIndex),Size1*.26, Size1*.38,Typeface.DEFAULT,ts,gScaleFontColor,"CENTER",300)
            csvGauge(GaugeIndex).DrawTextRotated(GaugeScale_Value(4,GaugeIndex),Size1*.36, Size1*.26,Typeface.DEFAULT,ts,gScaleFontColor,"CENTER",325)
            csvGauge(GaugeIndex).DrawTextRotated(GaugeScale_Value(5,GaugeIndex),Size1*.50, Size1*.22,Typeface.DEFAULT,ts,gScaleFontColor,"CENTER",0)
            csvGauge(GaugeIndex).DrawTextRotated(GaugeScale_Value(6,GaugeIndex),Size1*.64, Size1*.26,Typeface.DEFAULT,ts,gScaleFontColor,"CENTER",35)
            csvGauge(GaugeIndex).DrawTextRotated(GaugeScale_Value(7,GaugeIndex),Size1*.74, Size1*.38,Typeface.DEFAULT,ts,gScaleFontColor,"CENTER",60)
            csvGauge(GaugeIndex).DrawTextRotated(GaugeScale_Value(8,GaugeIndex),Size1*.77, Size1*.53,Typeface.DEFAULT,ts,gScaleFontColor,"CENTER",90)
            csvGauge(GaugeIndex).DrawTextRotated(GaugeScale_Value(9,GaugeIndex),Size1*.75, Size1*.69,Typeface.DEFAULT,ts,gScaleFontColor,"CENTER",310)
            csvGauge(GaugeIndex).DrawTextRotated(GaugeScale_Value(10,GaugeIndex),Size1*.60,Size1*.80,Typeface.DEFAULT,ts,gScaleFontColor,"CENTER",335)
            
            'DRAW GAUGE PID LABEL (PID NAME)
            csvGauge(GaugeIndex).DrawText(GaugeScale_Value(11,GaugeIndex), Size1/gPidLabelLeft, Size1/gPidLabelTop, Typeface.DEFAULT, Size1/11/Density, gScalePidUnitColor,"CENTER")
            'DRAW GAUGE UNIT LABEL (UNITS)
            csvGauge(GaugeIndex).DrawText(GaugeScale_Value(12,GaugeIndex), Size1/gUnitLabelLeft, Size1/gUnitLabelTop, Typeface.DEFAULT, Size1/11/Density, gScalePidUnitColor,"CENTER")
   
      End Select
      '----------------------------------------------------------------------------------------------------------------------
      'REFRESH
      If Demo = False Then
         imvGauge(GaugeIndex).Invalidate2(DRectGauge(GaugeIndex))
      Else
         imvGaugeDemo.Invalidate2(DRectGauge(GaugeIndex))
      End If
   Catch
      Log("Draw Analog Gauge Exception Error")
      If blnLogDebug = True Then LogBuilder.Append("Draw Analog Gauge Exception Error: " & Size1 & Chr(10))
      Msgbox(LastException.Message, "Drawing Analog Gauge Error")
   End Try
End Sub
 
Upvote 0

Scantech

Well-Known Member
Licensed User
Longtime User
B4X:
Sub AnalogGaugeAdd(TypeGauge As String, WhatPanel As Int, GaugeIndex As Int, PosX As Int, PosY As Int, Size1, ScaleMinValue As Double, ScaleMaxValue As Double, gPidLabel As String, gUnitLabel As String, GFileIndex As Int, GFileName As String, NFileIndex As Int, NFileName As String, PidName As String, gScaleStyle As Int, gScaleFontColor As String, gScalePidUnitColor As String, gPidLabelLeft As Double, gPidLabelTop As Double, gUnitLabelLeft As Double, gUnitLabelTop As Double)
   Try
      Dim GaugeFilePath, NeedleFilePath As String

      GaugeFileName(GaugeIndex)  = GFileName                        'GAUGE FILE NAME
      NeedleFileName(GaugeIndex) = NFileName                        'NEEDLE FILE NAME

      'DETERMINES THE FILES PATH BY INDEX: 0,1 = FILE.DirAssets:  2 or MORE = FILE.DirRootExternal
      If GFileIndex > 1 Then
         GaugeFilePath = File.DirRootExternal
         GFileName = GaugeDir & GFileName
      Else
         GaugeFilePath = File.DirAssets
      End If

      If NFileIndex > 1 Then
         NeedleFilePath = File.DirRootExternal
         NFileName = NeedleDir & NFileName
      Else
         NeedleFilePath = File.DirAssets
      End If
         
      GaugeScale_Style(GaugeIndex)            = gScaleStyle            'GAUGE SCALE STYLE
      GaugeScale_FontColor(GaugeIndex)        = gScaleFontColor         'GAUGE SCALE FONT COLOR
      GaugeScalePidUnit_FontColor(GaugeIndex) = gScalePidUnitColor      'GAUGE PID/UNIT FONT COLOR
      GaugePidLabel_Left(GaugeIndex)          = gPidLabelLeft          'PID LEFT
      GaugePidLabel_Top(GaugeIndex)           = gPidLabelTop             'PID TOP
      GaugeUnitLabel_Left(GaugeIndex)         = gUnitLabelLeft         'UNIT LEFT
      GaugeUnitLabel_Top(GaugeIndex)          = gUnitLabelTop            'UNIT TOP
      GaugeAtPanel(GaugeIndex)                = WhatPanel               'WHAT PANEL IS THE GAUGE AT?
      GaugeType(GaugeIndex)                   = TypeGauge               'WHAT TYPE OF GAUGE
      
      GaugeNb = GaugeNb + 1                                    'NUMBER OF GAUGES COUNTER
      
      Dim wp As Int: wp = WhatPanel + 1
      PanelControl_spnIndex.Add(gPidLabel & " (" & wp & ")")            'ADD TO SPINNER   (THIS IS USED IN CONTROL GAUGE PANEL)
      PanelControl_spnIndex.SelectedIndex = GaugeIndex               'SET FOCUS (CONTROL PANEL)
      
      PanelAdd_spnAddedSensors.Add(gPidLabel)                        'ADD TO SPINNER (THIS IS USED IN ADD GAUGE PANEL)
      PanelAdd_spnAddedSensors.SelectedIndex = GaugeIndex               'SET FOCUS (ADD GAUGE)
   
      'INITIALIZE IMAGES
      bmpGauge(GaugeIndex).InitializeSample (GaugeFilePath, GFileName, SampleSize, SampleSize)
      bmpNeedle(GaugeIndex).InitializeSample(NeedleFilePath,NFileName, (SampleSize * sRatio), SampleSize)

      'PID IDENTIFIER
      imvGauge(GaugeIndex).Tag = PidName
      
      'SET FILL TYPE AND CLEAR IMAGE
      imvGauge(GaugeIndex).Gravity = Gravity.FILL
      imvGauge(GaugeIndex).Color=Colors.Transparent
      
      'SET FILL TYPE AND CLEAR IMAGE
      imvNeedle(GaugeIndex).Gravity = Gravity.FILL
      imvNeedle(GaugeIndex).Color=Colors.Transparent
   
      'ADD BOTH NEEDLE AND GAUGE TO THE PANEL
      Panels(WhatPanel).AddView(imvGauge(GaugeIndex),  PosX, PosY, Size1, Size1)
      Panels(WhatPanel).AddView(imvNeedle(GaugeIndex), PosX, PosY, Size1, Size1)
      
      'DRAW GAUGE CONTAINER AND NEEDLE
      AnalogGaugeDraw(False, GaugeIndex, Size1, ScaleMinValue, ScaleMaxValue, gPidLabel, gUnitLabel, gScaleStyle,  gScaleFontColor, gScalePidUnitColor, gPidLabelLeft, gPidLabelTop, gUnitLabelLeft, gUnitLabelTop)
      AnalogNeedleDraw(False, GaugeIndex, Size1)

   Catch
      Log("Add Gauge Exception Error")
      If blnLogDebug = True Then LogBuilder.Append("Draw Analog Gauge Exception Error" & Chr(10))
      Msgbox(LastException.Message, "Adding Gauge Error")
   End Try
End Sub
 
Upvote 0

Scantech

Well-Known Member
Licensed User
Longtime User
Each time you initialize a canvas object, a new bitmap is created for the drawing. You should reuse the same one.

Every time the ImageView gets resized then Canvas has to be reinitialized, right? The gauge containing drawing is not displayed correctly if I do not reinitialize the canvas. The codes was based on the Graphic Compass tutorial with slight modification.

Another Question.
Does Canvas.removeclip free up memory?

Thank you
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
Does Canvas.removeclip free up memory?
No.

Every time the ImageView gets resized then Canvas has to be reinitialized, right?
More or less correct. There is a way around it. Create a mutable bitmap with the largest expected size of the ImageView and use Canvas.Initialize2 to draw on the bitmap. Set this bitmap as the ImageView background. This will allow you to initialize the canvas object only once.
 
Upvote 0

Scantech

Well-Known Member
Licensed User
Longtime User
No.


More or less correct. There is a way around it. Create a mutable bitmap with the largest expected size of the ImageView and use Canvas.Initialize2 to draw on the bitmap. Set this bitmap as the ImageView background. This will allow you to initialize the canvas object only once.

Thanks Erel!
 
Upvote 0
Top