Selvarathinam Vinoch
Selvarathinam Vinoch

Reputation: 1100

ListView performance is too slow even though inside the AsyncTask

I'm developing a music player. so I need to load the songs list in a custom ListView which has one ImageView and two textView. It's performance is too slow, so i searched in google and i found a solution that says 'use AsyncTasks and load listView in background'. But still listview's performance is too slow. Please help me to increase performance of listview. Here is my code :

This code loads the custom ArrayList by getting values from song_database and it is inside the Activity class.

private ArrayList getListData() {
    ArrayList results = new ArrayList();
    Song Data = new Song();
    Database_Song db = new Database_Song(this);
    String data = db.getData();
        StringTokenizer token = new StringTokenizer(data,";");

        while(token.hasMoreTokens())
        {
            String dataPart = token.nextToken();
            StringTokenizer tokenPart = new StringTokenizer(dataPart,"|");
            Data = new Song();
            Data.setSongTitle(tokenPart.nextToken());
            mediaInfo.setDataSource(tokenPart.nextToken());

            if (mediaInfo.getEmbeddedPicture() != null){
                byte[] img = mediaInfo.getEmbeddedPicture();
                Data.setImage((BitmapFactory.decodeByteArray(img, 0,img.length)));
            }
            else
                Data.setImage(BitmapFactory.decodeResource(this.getResources(), R.drawable.album_art));

            Data.setAlbumName(tokenPart.nextToken());
            results.add(Data);
        }

        return results;
}

This is my AsyncTasks class which is innerclass of Activity class.

private class LoadListTask extends AsyncTask<String, Void, Integer> {
    ArrayList Song_details;
    protected void onPreExecute() {

    }

    protected Integer doInBackground(String... params) {
        Song_details = getListData();
        return 0;
    }

    protected void onPostExecute(Integer result) {
        adapter = new CustomListAdapter_Song(cont, Song_details);
        lv_song.setAdapter(adapter);

        if (result == 0) {
            adapter.notifyDataSetChanged();
         }
    }
}

this is my adapter class

public class CustomListAdapter_Song extends BaseAdapter{

private ArrayList<Song> listData;
private LayoutInflater layoutInflater;
private ArrayList<Song> arraylist;
private int lastPosition = -1;

public CustomListAdapter_Song(Context context, ArrayList<Song> listData) {
    this.listData = listData;
    layoutInflater = LayoutInflater.from(context);
    this.arraylist = new ArrayList<Song>();
    this.arraylist.addAll(listData);
}

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

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

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

public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder;
    if (convertView == null) {
        convertView = layoutInflater.inflate(R.layout.listview_item_row_for_data, null);
        holder = new ViewHolder();
        holder.songName = (TextView) convertView.findViewById(R.id.Song_songTitle);
        holder.albumName = (TextView) convertView.findViewById(R.id.Song_AlbumName);
        holder.songImage = (ImageView) convertView.findViewById(R.id.Song_AlbumImage);
        convertView.setTag(holder);

    } else {
        holder = (ViewHolder) convertView.getTag();
    }

    holder.songName.setText(listData.get(position).getSongTitle());
    holder.albumName.setText(listData.get(position).getAlbumName());
    holder.songImage.setImageBitmap((listData.get(position).getImage()));
    View view = convertView;
    Animation animation = AnimationUtils.loadAnimation(view.getContext(), (position > lastPosition) ? R.anim.up_from_bottom : R.anim.down_from_top);
    view.startAnimation(animation);
    lastPosition = position;

    return convertView;
}

static class ViewHolder {
    TextView songName;
    TextView albumName;
    ImageView songImage;
}   

}

Upvotes: 3

Views: 845

Answers (2)

Khamchunovog
Khamchunovog

Reputation: 41

Don't use AsyncTask but you need CursorLoader

public class SongsFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor>
{
    SongAdapter adapter;

    @Override
    public View onCreateView( LayoutInflater inflater , ViewGroup container , Bundle savedInstanceState )
    {
        return inflater.inflate(R.layout.fragment_songs, container, false);
    }

    @Override
    public void onCreate( Bundle savedInstanceState )
    {
        super.onCreate(savedInstanceState);
        adapter = new SongAdapter(getActivity(), null);
        setListAdapter(adapter);
        getLoaderManager().initLoader(0, null, this);

    }

    private class SongAdapter extends CursorAdapter
    {
        Uri sArtworkUri = Uri.parse("content://media/external/audio/albumart");

        public SongAdapter( Context context , Cursor c )
        {
            super(context , c , true);
        }

        @Override
        public View getView( int position , View convertView , ViewGroup parent )
        {
            View v;
            Cursor cursor = (Cursor) getItem(position);
            if (convertView == null)
            {
                v = newView(mContext, cursor, parent);
            }
            else
            {
                v = convertView;
            }
            bindView(v, mContext, cursor);
            return v;
        }

        @Override
        public void bindView( View view , Context context , Cursor cursor )
        {
            ViewHolder holder = (ViewHolder) view.getTag();
            holder.songNameTextView.setText(cursor.getString(2));
            holder.artistTextView.setText(cursor.getString(1));
            Uri albumArtUri = ContentUris.withAppendedId(sArtworkUri, cursor.getLong(3));
            Picasso.with(getActivity()).load(albumArtUri).error(R.drawable.placeholder).placeholder(R.drawable.placeholder).into(holder.albumImageView);
        }

        @Override
        public View newView( Context context , Cursor cursor , ViewGroup parent )
        {
            View view = LayoutInflater.from(context).inflate(R.layout.row_song, parent, false);
            ViewHolder holder = new ViewHolder();
            holder.albumImageView = (ImageView) view.findViewById(R.id.albumImg);
            holder.songNameTextView = (TextView) view.findViewById(R.id.songNameTxtView);
            holder.artistTextView = (TextView) view.findViewById(R.id.artistNameTxtView);
            view.setTag(holder);
            return view;
        }
    }

    private static class ViewHolder
    {
        public ImageView    albumImageView;
        public TextView     artistTextView;
        public TextView     songNameTextView;
    }

    @Override
    public Loader<Cursor> onCreateLoader( int arg0 , Bundle arg1 )
    {
        String[] projection = { MediaStore.Audio.Media._ID, MediaStore.Audio.Media.ARTIST, MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.ALBUM_ID };
        String selection = MediaStore.Audio.Media.IS_MUSIC + " != 0";
        return new CursorLoader(getActivity(), MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, selection, null, null);
    }

    @Override
    public void onLoadFinished( Loader<Cursor> arg0 , Cursor data )
    {
        adapter.swapCursor(data);
    }

    @Override
    public void onLoaderReset( Loader<Cursor> arg0 )
    {
        adapter.swapCursor(null);
    }
}

Upvotes: 1

Yury Zaitsev
Yury Zaitsev

Reputation: 126

The idea to use AsyncTask to load data for list view means loading data ONLY for particular items. As probably you know ListView asks his adapter only for items which are visible on the screen (plus a couple above and below of the visible area). As soon as your adapter does not need to provide all data at once you may load it on demand.
Below is just an sckeleton of possible implementation:

    // We need a queue to run only one AsyncTask
    // How to deal with queue you may find here 
    // http://developer.android.com/reference/java/util/concurrent/BlockingQueue.html
    BlockingQueue  loadViewQueue;


    @Override
    public int getCount() {
        return countOfYourData;
    }

    @Override
    public View getView( int position, View convertView, ViewGroup parent ) {
        if( convertView == null ) {
            convertView = LayoutInflater.from(getContext()).inflace( yourresId, parent, false );
        }

        // if you don't use ViewHolder patter you may store position as shown below
        // otherwise create appropriate field in ViewHolder class and store position there
        convertView.setTag(position);
        loadViewQueue.put(convertView);
    }

    class LoadingAsyncTask extends AsyncTask< View, Object, Void > {
        Integer position;
        @Override
        protected Void doInBackground( View... params ) {

            while(true) {
                View view = loadViewQueue.take();
                position = ( Integer )view.getTag();
                Data data = loadDataFromDb(position);
                publishProgress( position, data );
            }
            return null;
        }

        @Override
        protected void onProgressUpdate( Object result ) {
            applyDataToListItem((Integer)result[0], (Data)result[1]);
        }
    }

of course queue must be synchronized and errors checking added.

Upvotes: 0

Related Questions