Dragon Creature
Dragon Creature

Reputation: 1995

Loading bitmaps in an other thread without get new memory addresses

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);
}
}

Upvotes: 2

Views: 720

Answers (3)

stoilkov
stoilkov

Reputation: 1686

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).

Upvotes: 7

gaborsch
gaborsch

Reputation: 15758

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);

Upvotes: 0

Michał Z.
Michał Z.

Reputation: 4119

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

Upvotes: 0

Related Questions