2

I'm trying to load in images from the disk in an other thread to not slow down my draw thread however the problem is that when the object that hold the bitmap is transfered to the new thread they have different pointers. I'm not exactly sure how Java handles pointers across multipliable threads but I'm looking for a solution that will have both threads working with the same object (shallow copy) and not depth copy of the object between threads.

Here is my code

new LoadImageTask().execute(ThisTimeDrawBitmaps.indexOf(bitmapWrapper), gameEngine);

private class LoadImageTask extends AsyncTask {
    protected BitmapWrapper LoadImage(BitmapWrapper bitmapWrapper, GameEngine gameEngine) {
        //loading the image in a temp object to avoid  fatal error 11
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inScaled = true;
        Bitmap tempImage = BitmapFactory.decodeResource(Sceptrum.GetResources, bitmapWrapper.getID(), options);
        tempImage = Bitmap.createScaledBitmap(bitmapWrapper.getBitmap(), (int) (bitmapWrapper.getBitmap().getWidth() * gameEngine.getScale()), (int) (bitmapWrapper.getBitmap().getHeight() * gameEngine.getScale()), false);
        //so that the image can be gc.
        Bitmap RemovePointer = bitmapWrapper.getBitmap();
        //to avoid fatal error 11
        bitmapWrapper.setBitmap(tempImage);
        //removing the old image.
        RemovePointer.recycle();
        return bitmapWrapper;
     }
    @Override
    /**
     * add the bitmapwrapper you want to load and make sure to add the GameEngine as the last parameter.
     */
    protected Object doInBackground(Object... params) {
        for (int i = 0; i < params.length - 1; i++) {
            return LoadImage(ThisTimeDrawBitmaps.get((Integer) params[i]), (GameEngine) params[params.length - 1]);
        }
        return null;
    }
 }

public class BitmapWrapper{
private Bitmap bitmap;
/**
 * Use only to send the bitmap as a parameter. To modify or read data to/from the bitmap use the methods provided by BitmapWrapper.
 * @return
 */
public Bitmap getBitmap() {
    return bitmap;
}
public void setBitmap(Bitmap bitmap) {
    this.bitmap = bitmap;
}
private int id;
public int getID()
{
    return id;
}
private Point originalSize;
public Point getOriginalSize() {
    return originalSize;
}
public BitmapWrapper(Bitmap bitmap, int ID) {
    this.setBitmap(bitmap);
    id = ID;
    originalSize = new Point(bitmap.getWidth(), bitmap.getHeight());
}
public int getWidth()
{
    return bitmap.getWidth();
}
public int getHeight()
{
    return bitmap.getHeight();
}
public void createScaledBitmap(GameEngine gameEngine)
{
    bitmap = Bitmap.createScaledBitmap(bitmap, (int) (originalSize.x * gameEngine.getScale()), (int) (originalSize.y * gameEngine.getScale()), false);
}
public void CreateBitmap()
{
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inScaled = false;
    bitmap = BitmapFactory.decodeResource(Sceptrum.GetResources, id, options);
}
}
Icy Creature
  • 1,875
  • 2
  • 28
  • 53
  • 2
    What do you mean by "different pointers"? – Javier Feb 20 '13 at 20:20
  • 1
    I'm not really sure what you're trying to accomplish with the current `doInBackground()` code. It will only ever load one image(returns immediately), so why use a loop at all? – Geobits Feb 20 '13 at 20:42
  • 1
    The short answer is you don't care, Java is always pass-by-value, [this Q&A](http://stackoverflow.com/questions/40480/is-java-pass-by-reference) has some good explanation on this topic. – yorkw Feb 21 '13 at 00:11
  • (i) The for loop makes no sense as already pointed out. (ii) You should try to clarify your question if you want to get the most out of your bounty: what object has "different pointers"? how do you know (since Java does not give you access to memory)? (iii) It helps to follow general coding conventions (like variables starting in lower case). – assylias Feb 21 '13 at 17:38

3 Answers3

7

This is not a pointer problem (there are rearely such problems in Java:) ). If you want to load the images in a background thread you should create an (static) AsyncTask, which holds a weak reference to the views in your layout, where you actually want the Bitmaps to be placed. In doInBackground you should load the images and return them. In postExecute (which is called on the UI Thread) you will receive the result of doInBackground (the bitmaps). After checking if the referenced views are still there (activity is still alive) you can use the bitmaps (place them in the views).

You cannot modify UI elements from any other threads, except from the UI Thread (this holds true for any UI framework).

stoilkov
  • 1,686
  • 1
  • 11
  • 18
  • 1
    There are rarely pointer problems in Java because Java does not use pointers :) What you pass around are *references* to objects (not the objects themselves or pointers i.e address of memory location). Furthermore, as is also the case for primitives, these are always pass-by-value, Java always passes a copy of the reference not the reference itself. – Vasile Jureschi Feb 22 '13 at 11:54
  • 1
    @VasileJureschi didn't want to write never, feels too strong, thats why i wrote "rarely" :) – stoilkov Feb 22 '13 at 21:38
  • I assumed you knew I was just clarifying for the initial poster. Should have made that obvious I guess :) – Vasile Jureschi Feb 23 '13 at 16:18
0

You can try with volatile. I mean:

private volatile Bitmap bitmap;

"a variable declared volatile must have it's data synchronized across all threads" from: http://www.javaperformancetuning.com/news/qotm030.shtml

Michał Z.
  • 4,119
  • 1
  • 22
  • 32
0

Try to introduce a Container object.

public class Container<T> {
    private volatile boolean ready = false;
    private T object;

    public boolean isReady() { 
        return ready; 
    }

    public void setReady(boolean ready) {
        this.ready = ready;
    }

    public T get() {
        return (ready) ? object : null;
    }

    public void set(T object) {
        this.object = object;
    }
}

You can instantiate this Container with any type:

Container<Bitmap> logoContainer = new Container<Bitmap>();
Bitmap logo = logoContainer.get(); // returns null if not yet ready

In the background thread you can initialize:

Container<Bitmap> logoContainer = ... // 
Bitmap b = ... // load
logoContainer.set(b);
logoContainer.setReady(true);
gaborsch
  • 15,408
  • 6
  • 37
  • 48