Reputation: 1100
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
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
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