Search code examples
javaandroidbitmapandroid-asynctaskwallpaper

App UI becomes unresponsive when Bitmap is being loaded


I have a CountDownTimer which calls the ChangeWallpaper() method every 15 seconds. The Wallpaper changes as it should but when I try and open the App, it makes the App Drawer screen unresponsive for few seconds. When the App finally opens, everything that I select takes 5-10 seconds to respond. I read about AsyncTask in Android Developer which is supposed to load the Bitmaps outside the UI thread and prevent the App from hanging, but it doesn't seem to be working.

The following code is inside my Activity class:

/** changeWallpaper() **/ - called by CountDownTimer every 15 seconds
protected void changeWallpaper() throws IOException {
    Integer totalimages = finallist.size();

    if (lastloopcount == totalimages) { // if end of the list of images is reached, it resets and goes back to top.
        loopcount = 0;
        lastloopcount = 0;
    }

    for (String imagepath : finallist) { // "finallist" is global variable with all the image's paths in an array list. The Loop count is to select the next image in the array list every 15 seconds.
        loopcount++;
        if (loopcount > lastloopcount) {
            lastloopcount = loopcount;
            loopcount = 0;

            WallpaperManager wm = WallpaperManager.getInstance(this);
            wm.setBitmap(decodeImageFromPath(imagepath));

            break;
        }
    }
}

/** AsyncTask Wallpaper Load **/
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> {
    public BitmapWorkerTask(ImageView imageView) {
        new WeakReference<ImageView>(imageView);
    }

    @Override
    protected Bitmap doInBackground(Integer... params) {
        return null;
    }
}

/** decodeImageFromPath() **/
public Bitmap decodeImageFromPath(String imagepath) {
    DisplayMetrics displayMetrics = new DisplayMetrics();
    getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
    int height = displayMetrics.heightPixels;
    int width = displayMetrics.widthPixels << 2;

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeFile(imagepath, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, width, height);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeFile(imagepath, options);
}

/** WallpaperManager (Method) **/
public static int calculateInSampleSize(
    BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // ... Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;

    int stretch_width = Math.round((float)width / (float)reqWidth);
    int stretch_height = Math.round((float)height / (float)reqHeight);

    if (stretch_width <= stretch_height) return stretch_height;
    else return stretch_width;
}
  1. Have I used the AsyncTask function correctly?
  2. Is there an easier way to write this?

Thanks in advance.

EDIT:

/** Spinner **/
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
    String chosenTime = parent.getItemAtPosition(position).toString();
    int chosenTimeNew = 0;
    if (chosenTime.contains("sec")) {
        chosenTime = chosenTime.replace(" sec","");
        chosenTimeNew = Integer.parseInt(chosenTime) * 500;
    } else if (chosenTime.contains("min") ) {
        chosenTime = chosenTime.replace(" min","");
        chosenTimeNew = Integer.parseInt(chosenTime) * 30000;
    } else if (chosenTime.contains("hour")) {
        chosenTime = chosenTime.replace(" hour","");
        chosenTimeNew = (Integer.parseInt(chosenTime) * 30000) * 60;
    } else if (chosenTime.contains("day")) {
        chosenTime = chosenTime.replace(" day","");
        chosenTimeNew = ((Integer.parseInt(chosenTime) * 30000) * 60) * 24;
    }
    rSpeed = chosenTimeNew;
}

EDIT 2:

Called by CountDownTimer():

new BitmapWorkerTask(null).execute(imagepath);

Then:

    /** AsyncTask Wallpaper Load **/
class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap> {

    public BitmapWorkerTask(ImageView imageView) {
        new WeakReference<ImageView>(imageView);
    }

    @Override
    protected Bitmap doInBackground(String... params) {

        DisplayMetrics displayMetrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        int height = displayMetrics.heightPixels;
        int width = displayMetrics.widthPixels << 2;

        // First decode with inJustDecodeBounds=true to check dimensions
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFile(params[0], options);

        // Calculate inSampleSize
        options.inSampleSize = calculateInSampleSize(options, width, height);

        // Decode bitmap with inSampleSize set
        options.inJustDecodeBounds = false;

        Bitmap bmp = BitmapFactory.decodeFile(params[0], options);
        return bmp;
    }

    protected void onPostExecute(Bitmap bmp) {

        Context context = getApplicationContext();
        WallpaperManager wm = WallpaperManager.getInstance(context);
        try {
            wm.setBitmap(bmp);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

    /** WallpaperManager (Method) **/
public static int calculateInSampleSize(
    BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // ... Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;

    int stretch_width = Math.round((float)width / (float)reqWidth);
    int stretch_height = Math.round((float)height / (float)reqHeight);

    if (stretch_width <= stretch_height) return stretch_height;
    else return stretch_width;
}

Solution

  • Your main code(processing the bitmap) should be called from within the doInBackground method. Else it's the same as a synchronous call here.

        @Override
        protected Bitmap doInBackground(String... params) {
            Bitmap bmp = decodeImageFromPath(params[0]);
            return bmp;
        }
    
         protected void onPostExecute(Bitmap bmp) {
            wm.setBitmap(bmp)
         }
    
    new BitmapWorkerTask ().execute(imagePath);
    

    http://developer.android.com/reference/android/os/AsyncTask.html

    You can refer to the example on this link.