2

I'm trying to better understand how Java variable scope works and what exactly happens to the underlying data when we do something like the following in a method:

this.variable = variable

What exactly does this line do? Here is my actual problem:

I'm loading Bitmaps to apply as textures in my (Android) OpenGL ES 2.0 project. It goes something like this:

public loadBitmapsForTextures(){

    myBitmap = BitmapFactory.decodeResource(view.getResources(), R.drawable.testbmp, Options);

    myObject.setTexture(view, myBitmap);

    Log.v("NewTag","Recycled: Again: "+myBitmap);

    myBitmap.recycle(); //All done - no longer required.  But why is myBitmap still valid here?
}

within my Sprite class (of which myObject is an object), I have the following:

public void setTexture(GLSurfaceView view, Bitmap imgTexture){
        
        this.imgTexture=imgTexture;  //What exactly is this line doing?  Copying the actual data?  Just making another 'pointer' to the original data?

        iProgId = Utils.LoadProgram(strVShader, strFShader);
        iBaseMap = GLES20.glGetUniformLocation(iProgId, "u_baseMap");
        iPosition = GLES20.glGetAttribLocation(iProgId, "a_position");
        iTexCoords = GLES20.glGetAttribLocation(iProgId, "a_texCoords");
        //Return usable texture ID from Utils class
        texID = Utils.LoadTexture(view, imgTexture);
                    
        Log.v("NewTag","Recycled: Before: "+imgTexture);
        imgTexture.recycle();
        imgTexture=null;
        Log.v("NewTag","Recycled: After"+imgTexture);           
        
}

The logs in the setTexture method give the results I am expecting. The first one names the bitmap:

Recycled: Before: android.graphics.Bitmap@1111111

Recycled: After: null

However, the log statement in the initial loadBitmapsForTextures() method give something I wasn't expecing:

Recycled: Again: android.graphics.Bitmap@1111111

Why am I allowed to (seemingly) recycle this bitmap again? I can only assume that my understanding of the following line is flawed:

this.imgTexture=imgTexture;

So, this line does what exactly? As far as I can tell, it applies the class variable the same value as the local variable (which was passed into the method), however, obviously something more is happening. Does it actually create a whole new bitmap? If so, why is the name the same when logging?

Community
  • 1
  • 1
Zippy
  • 3,826
  • 5
  • 43
  • 96
  • Nothing more is happening. You are calling `recycle` twice. Look at your code. Inside `setTexture` you call `imgTexture.recycle();` and then you call `myBitmap.recycle();` inside `loadBitmapsForTextures()`. – Max Jun 07 '15 at 09:20
  • @Max, yes I know, I did that on purpose, that's what I'm asking. If I recycle the bitmap in setTexture(), why is it still valid in loadBitmapForTextures()? Cheers. – Zippy Jun 07 '15 at 09:22
  • Because `myBitmap` and `imgTexture` are two separate variables pointing to the same object. So, when you say `imgTexture = null` this means that `imgTexture` now points to nothing. No where did you tell `myBitmap` to point to nothing, so it still points to the original texture. Make sense? – Max Jun 07 '15 at 09:23

2 Answers2

1

This line sets the instance member imgTexture to refer to the same object whose reference was passed to the method.

this.imgTexture=imgTexture;   

This line sets the reference passed to the method to null, which doesn't change this.imgTexture.

imgTexture=null;

Perhaps you wish to replace it with

this.imgTexture=null;

If you want the object not to contain a reference to that bitmap anymore.

Eran
  • 387,369
  • 54
  • 702
  • 768
  • OK @Eran, this makes sense, however, I'm still confused. I've placed (in addition to imgTexture.recycle() & imgTexture = null), this.imgTexture.recycle() and this.imgTexture = null. Now, when I run the code, it still allows me to recycle from loadBitmapsForTextures() - and it still has the same name even though logging confirms (from setTexture()) that both imgTexture and this.imgTexture are both now null? Am I missing something? Thanks – Zippy Jun 07 '15 at 09:39
  • @Zippy loadBitmapsForTextures assigns the bitmap to a variable called `myBitmap` (I'm not sure where that variable is declared), so even after you call `setTexture`, `myBitmap` still has a reference to the bitmap (`setTextture` can't change it to null, unless `myBitmap` is also an instance variable, in which case you can write `this.myBitmap = null;` . The question is why call `recycle()` twice on the same bitmap. – Eran Jun 07 '15 at 09:49
  • OK, so myBitmap is declared in a class called Resources (which is where loadBitmapsForTextures is being called from incidentally). Obviously the loadTexture method is being called from a different class (Sprite in my case), Regarding calling recycle twice, this is only for testing, I wanted to be sure the recycle call within setTexture was actually recycling the original bitmap which is isn't. I was expecting a crash when calling recycle from loadBitmapFromTextures as I was hoping it would already be recycled and null. – Zippy Jun 07 '15 at 09:55
  • @Zippy You are calling `recycle` on the original bitmap twice. `This will not free the pixel data synchronously; it simply allows it to be garbage collected if there are no other references. The bitmap is marked as "dead", meaning it will throw an exception if getPixels() or setPixels() is called, and will draw nothing`. Nothing says that a second call to `recycle` will throw an exception. And I explained why `myBitmap` is not null after the call to setTexture. – Eran Jun 07 '15 at 10:00
  • Ah OK - got it @Eran, yes I do recall a while back having problems with calling getPixels() on a bitmap that had been recycled. Thanks, I understand. I'll read through your answer a few more times to make sure I'm getting it all. – Zippy Jun 07 '15 at 10:08
  • @Zippy BTW, the Javadoc of `recycle` also says - `This is an advanced call, and normally need not be called, since the normal GC process will free up this memory when there are no more references to this bitmap.`. – Eran Jun 07 '15 at 10:10
  • Have accepted your answer as I think I understand it although strangely, even with my original code in-place, I checked my app's heap size and it's a lot smaller even without using this.imgTexture. so it does seem to be recycling the bitmap anyway! (if I remove all recycle reference the heap grows ten-fold). Cheers – Zippy Jun 07 '15 at 10:34
0

For anything other than simple boolean and numeric values, things get passed by reference. So

Object o1 = new Object();

creates a new Object and assigns a reference called o1 to the area in memory.

Object o2 = o1;

assigns a new reference named o2 to the same area in memory. The same thing happens when you call methods with parameters; you are dealing with the same entity, not a copy.