irobotxx
irobotxx

Reputation: 6063

Slow scrolling when populating gridview of images with AsyncTask

good day, i am having a bit of a problem here. i am using and async task to display images from external or internal storage.Now it works but the problem is, it is very slow and slightly jerky in scrolling. I have no idea why? please any solution or an idea how to do this.

import java.io.IOException;
import java.util.ArrayList;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Debug;
import android.os.Environment;
import android.provider.MediaStore;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;

public class Wallpaper extends Activity implements OnItemClickListener{

/*Instance variables*/
 private GridView grid;
 private ImageAdapter imageAdapter;
 private Display display;
 Cursor cursor;
 boolean inInternalStorage = false;



 public void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);

        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        setContentView(R.layout.wallpaper_images);

        display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();

        setupViews();
        setProgressBarIndeterminateVisibility(true); 
        loadImages();
    }

 @Override
 protected void onStop(){
     super.onStop();
 }

 /*methods called from AsyncTask as appropriate when its querying and processing the images from the MediaStore*/

 private void loadImages() {
 final Object data = getLastNonConfigurationInstance();
 if(data == null){
     new LoadImagesFromSDCard().execute();
 }else {
     final LoadedImage[] photos = (LoadedImage[])data;
     if(photos.length == 0){

        new LoadImagesFromSDCard().execute();
     }
     for(LoadedImage photo:photos){
         addImage(photo);
    }
 }

}

private void addImage(LoadedImage... value) {

    for(LoadedImage photo: value){
        imageAdapter.addPhotos(photo);  
        imageAdapter.notifyDataSetChanged();
    }

}

private void setupViews() {

        grid = (GridView)findViewById(R.id.gridview);
        grid.setNumColumns(display.getWidth()/95);
        grid.setClipToPadding(false);
        imageAdapter = new ImageAdapter(getApplicationContext());
        grid.setAdapter(imageAdapter);
        grid.setOnItemClickListener(this);
}

protected void onDestroy() {
        super.onDestroy();
        final GridView gridview = grid;
        final int count = grid.getChildCount();
        ImageView v = null;
        for (int i = 0; i < count; i++) {
            v = (ImageView) grid.getChildAt(i);
            ((BitmapDrawable) v.getDrawable()).setCallback(null); 
        }

        unbindDrawables(findViewById(R.id.gridview));
        System.gc();
    }

/*public Object onRetainNonConfigurationInstance(){
    final GridView gridview = grid;
    final int count = grid.getChildCount();
    final LoadedImage[] list = new LoadedImage[count];

    for(int i = 0; i < count; i++){
        final ImageView v = (ImageView)grid.getChildAt(i);
        list[i] = new LoadedImage(((BitmapDrawable) v.getDrawable()).getBitmap());
    }
    return list;
}*/


/*utility method called that prevents screen from crashing when screen orientation changes*/
private void unbindDrawables(View view){
    if(view.getBackground() != null){
        view.getBackground().setCallback(null);
    }
    if(view instanceof ViewGroup){
        for(int i = 0; i < ((ViewGroup) view).getChildCount(); i++){
            unbindDrawables(((ViewGroup)view).getChildAt(i));
        }
        try{
            ((ViewGroup)view).removeAllViews();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
}

/*AsyncTask thats queries the MediaStore for images and creates thumbnail images from bitmaps*/
class LoadImagesFromSDCard extends AsyncTask<Object, LoadedImage, Object> {

    @Override
    protected Object doInBackground(Object... params) {

        Cursor cursor;
        Bitmap bitmap = null;
        Bitmap newbitmap = null;
        Uri uri = null;

        String [] img = {MediaStore.Images.Media._ID};

        String state = Environment.getExternalStorageState();

        if(Environment.MEDIA_MOUNTED.equals(state)){    

        cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, img, 
          MediaStore.Images.Media.DATA + " like ? " , new String[]{ "%dcim%"}, null);
        } else {
         cursor = getContentResolver().query(MediaStore.Images.Media.INTERNAL_CONTENT_URI, img, null, null, null);
         inInternalStorage = true;
        }

        int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID);
        int size = cursor.getCount();

        if(size == 0){
            Toast.makeText(getApplicationContext(), "There are no Images on the sdcard", Toast.LENGTH_SHORT).show();

        }else {

        }

        for(int i = 0; i < size; i++){
            cursor.moveToPosition(i);
            int ImageId = cursor.getInt(column_index);

            if(inInternalStorage == true){
             uri = Uri.withAppendedPath(MediaStore.Images.Media.INTERNAL_CONTENT_URI, "" + ImageId);
         }else {
             uri = Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "" + ImageId);
         }


         try {
             BitmapFactory.Options options = new BitmapFactory.Options();
             options.inSampleSize=4;


             bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri), null, options);

             if(bitmap != null){
                 newbitmap = Bitmap.createScaledBitmap(bitmap, 100, 100, true);
                 bitmap.recycle();
             }
             if(newbitmap != null){
                 publishProgress(new LoadedImage(newbitmap));
             }
         }catch(IOException e){

         }
        }
        cursor.close();
        return null;    
    }

    @Override
    public void onProgressUpdate(LoadedImage... value){
        addImage(value);
    }

       @Override
        protected void onPostExecute(Object result) {
            setProgressBarIndeterminateVisibility(false);
        }
}

private static class LoadedImage {
    Bitmap mBitmap;

    LoadedImage(Bitmap bitmap) {
        mBitmap = bitmap;
    }

    public Bitmap getBitmap() {
        return mBitmap;
    }
}

/*Image Adapter to populate grid view of images*/

public class ImageAdapter extends BaseAdapter {
    private Context mContext;
    private ArrayList<LoadedImage> photos = new ArrayList<LoadedImage>();


    public ImageAdapter(Context context){
        this.mContext = context;
    }

    public void addPhotos(LoadedImage photo){
        photos.add(photo);
    }

    @Override
    public int getCount() {
        return photos.size();
    }


    @Override
    public Object getItem(int position) {
        return position;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        System.gc();
        ImageView image;
        ViewHolder holder;

        if(convertView == null){
            LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.image_list,null);
            holder = new ViewHolder();
            holder.image = (ImageView)convertView.findViewById(R.id.image_list_id);
            convertView.setTag(holder); 
        } else{
            holder = (ViewHolder)convertView.getTag();
        }   
        holder.image.setLayoutParams(new GridView.LayoutParams(100, 100));
        holder.image.setImageBitmap(photos.get(position).getBitmap());
        return convertView;
    }
}

static class ViewHolder {
   ImageView image;
}

@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    Cursor cursor = null;
    int image_column_index = 0;
    String[] proj = {MediaStore.Images.Media.DATA};

    String state = Environment.getExternalStorageState();

  if(Environment.MEDIA_MOUNTED.equals(state)){

    cursor = managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,proj, MediaStore.Images.Media.DATA + " like ? ", new String[]{"%dcim%"},null);

  }else{
      cursor = managedQuery(MediaStore.Images.Media.INTERNAL_CONTENT_URI,proj,null, null,null);
  }
    cursor.moveToPosition(position);
    image_column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);


    String info = cursor.getString(image_column_index);
    Intent imageviewer  = new Intent(getApplicationContext(), ViewImage.class);
           imageviewer.putExtra("pic_name", info);
           startActivity(imageviewer);

}

}

i think what i am after is a way to allow doBackground() to run for a while before i call publishProgress(). just like how the gallery app in android does. Any ideas on why this could be slow or how to solve this problem will be greatly appreciated as i have been stuck on how to improve the perfomance for a while now.

Upvotes: 4

Views: 2230

Answers (1)

Dororo
Dororo

Reputation: 3440

The biggest bottleneck will be accessing the images from the device (internal or external). Therefore, you'll want to have as many images in memory as possible (within reason of course). You can do this in a number of ways:

  • Load perhaps 18 images first (as you said, prebuffer before displaying). Check to make sure the number of images doesn't exceed whatever number you choose though.
  • Create a 'thumbs.db' type file which will store 100x100 px thumbnails of the bitmaps. This will allow much faster reading since you'd only need to read in one file then extract each bitmap. If the user clicks an image, then request it from the storage. This method requires more work, but will be able to load the thumbnails really fast. You'd probably have to design your own simple file headers, such as:

     <file header (size of file, number of images)>
     <image header (img id, size in bytes)>
     <image bitmap data>
     <image header (img id, size in bytes)>
     <image bitmap data>
     <image header (img id, size in bytes)>
     <image bitmap data>
     ...
    

Upvotes: 1

Related Questions