Slow scrolling when filling an image grid with AsyncTask

Good day, I have a problem here. I use an asynchronous task to display images from external or internal storage. Now it works, but the problem is that it is very slow and slightly jerky in scrolling. I have no idea why? please any decision or 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 I'm in favor of letting doBackground () run for a while before I call publishProgress (). just like the gallery app in android. Any ideas on why this might be slow or how to solve this problem would be much appreciated since I was already stuck on how to improve performance for a while.

+4
source share
1 answer

The biggest bottleneck will be access to images from the device (internal or external). Thus, you want to have as many images in memory as possible (within reasonable limits). You can do this in several ways:

  • First upload 18 images (as you said, prebuffer before displaying). Make sure that the number of images does not exceed any number that you have selected.
  • Create a file of type "thumbs.db", which will store thumbnails of 100x100 pixels of bitmap images. This will allow you to read much faster, since you will only need to read in one file, and then extract each bitmap. If the user clicks on the image, request it from the repository. This method requires more work, but it will be able to load thumbnails very quickly. You may have to create your own simple file headers, for example:

      <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> ... 
+1
source

Source: https://habr.com/ru/post/1380904/


All Articles