1

I am building an sms app which lists the contact pic,name,message,date of a conversation. I'm using a custom adapter to list them.Below is the code

public class ConvRowAdapter extends ArrayAdapter<ConvItem> {
private Context my_context;
// View lookup cache
private static class ViewHolder {
    TextView name,body,date;
    ImageView img;
}
public ConvRowAdapter(Context context, ArrayList<ConvItem> ConvItems) {
   super(context, R.layout.convitem, ConvItems);
   my_context=context;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
   // Get the data item for this position
   ConvItem ConvItem = getItem(position);    
   // Check if an existing view is being reused, otherwise inflate the view
   ViewHolder viewHolder; // view lookup cache stored in tag
   if (convertView == null) {
      viewHolder = new ViewHolder();
      LayoutInflater inflater = LayoutInflater.from(getContext());
      convertView = inflater.inflate(R.layout.convitem, null);
      viewHolder.img = (ImageView) convertView.findViewById(R.id.iv_photo);
      viewHolder.name = (TextView) convertView.findViewById(R.id.tv_name);
      viewHolder.date = (TextView) convertView.findViewById(R.id.tv_date);
      viewHolder.body = (TextView) convertView.findViewById(R.id.tv_body);
      convertView.setTag(viewHolder);
   } else {
       viewHolder = (ViewHolder) convertView.getTag();
   }
   // Populate the data into the template view using the data object
   if(ConvItem.getPhotoUri()!=null)
   {
       Bitmap bitmap=BitmapFactory.decodeStream(my_context.getContentResolver().openInputStream(ConvItem.getPhotoUri()));
       viewHolder.img.setImageBitmap(bitmap);//Image is set
   }
   else
   {
       Bitmap bitmap=BitmapFactory.decodeResource(my_context.getResources(),R.drawable.contact_blue);
       viewHolder.img.setImageBitmap(bitmap);
   }
   if(ConvItem.getDisplayName()==null)
   viewHolder.name.setText(ConvItem.getAddress());//Phone number set
   else
   viewHolder.name.setText(ConvItem.getDisplayName());//If phone number exists in contacts display contact name
   viewHolder.date.setText(ConvItem.getDate());//set date
   viewHolder.body.setText(ConvItem.getBody());//setting the body of the message
   // Return the completed view to render on screen
   return convertView;
}
}

It takes 8-9 seconds to display the list. Is there a way to improve the speed? If so please post the correct way to do so.

Thanks in advance :)

Edited:

To check if the delay in loading is caused by the loading of images i tried deleting all photo setting statements and ran it.Still the loading takes the same time. By using a simplecursoradapter i could display the body,date and message in no delay.But to display the photo with them i tried using the custom adapter. This is the function which returns all sms into an arraylist.Please check the efficiency of the code:

public ArrayList<ConvItem> getSMS(){
    ArrayList<ConvItem> ConvItems = new ArrayList<ConvItem>();
    Uri uriSMSURI = Uri.parse("content://mms-sms/conversations?simple=true");
    Cursor cur = getActivity().getContentResolver().query(uriSMSURI, null, null, null,
            "date desc");
    cur.moveToFirst();
    while (!cur.isAfterLast()) {
        ConvItem ConvItem = new ConvItem();
        String address = null, body = null, dname = null,res=null,id_dummy=null;
        Long date=null;
        Uri uriphotoid=null;
        //to obtain address from canonical-addresses
        res = cur.getString(cur.getColumnIndex("recipient_ids"));
        Uri ad =Uri.parse("content://mms-sms/canonical-addresses/");
        Cursor curad=getActivity().getContentResolver().query(ad, null,null, null, null);
        curad.moveToFirst();
        while(!curad.isAfterLast())
        {
            id_dummy=curad.getString(curad.getColumnIndexOrThrow("_id"));
            if(id_dummy.equals(res))
            address=curad.getString(curad.getColumnIndexOrThrow("address"));
            curad.moveToNext();
        }
        curad.close();
        body = cur.getString(cur.getColumnIndexOrThrow("snippet"));
        date = cur.getLong(cur.getColumnIndexOrThrow("date"));
        Date datenew=new Date(date);
        String formatted_date=new SimpleDateFormat("  "+"dd/MM/yyyy").format(datenew);
        thread_id = cur.getString(cur.getColumnIndexOrThrow("_id"));
        Long ContactID = fetchContactIdFromPhoneNumber(address);
        //uriphotoid = getPhotoUri(ContactID);
        dname = getcontactname(address);
        ConvItem.setDisplayName(dname);
        ConvItem.setThreadId(thread_id);
        ConvItem.setAddress(address);
        //ConvItem.setPhotoUri(uriphotoid);
        ConvItem.setDate(formatted_date);
        ConvItem.setBody(body);
        ConvItems.add(ConvItem);
        cur.moveToNext();
    }
    cur.close();
    return ConvItems;
    }

This function is running on my onCreate method in the main activity class.


I used loader.callbacks to correct the delay issue.

public class InboxLoaderFragment extends Fragment implements LoaderCallbacks<Cursor>{

private static final int LOADER_ID = 1;//identify which loader
LoaderManager lm;
SimpleCursorAdapter mAdapter;
ListView lv;
private LoaderManager.LoaderCallbacks<Cursor> mCallbacks;

public InboxLoaderFragment(){}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    View rootview=inflater.inflate(R.layout.inboxloaderfragment,container,false);  
    lv=(ListView)rootview.findViewById(R.id.list);
    // Create an empty adapter we will use to display the loaded data.

    String[] uiBindFrom = {"recipient_ids","recipient_ids","snippet"};
    int[] uiBindTo = {R.id.iv_photo,R.id.tv_name,R.id.tv_body};

    mAdapter = new SimpleCursorAdapter(getActivity(),R.layout.convitem,null,uiBindFrom,uiBindTo, 0);

    mAdapter.setViewBinder(new SimpleCursorAdapter.ViewBinder() {

        @Override
        public boolean setViewValue(View view, Cursor cursor, int columnIndex) {

            switch(view.getId()) 
            {
            case R.id.iv_photo:
                String res=null,address=null;
                //to obtain address from canonical-addresses
                res = cursor.getString(cursor.getColumnIndex("recipient_ids"));
                address=getadd(res);
                Long ContactID = fetchContactIdFromPhoneNumber(address);
                Uri uriphotoid = getPhotoUri(ContactID);
                setcontactimage(uriphotoid,view);
                //set photo using uri
                return true;

            case R.id.tv_name:
                String res1=null,address1=null;
                res1 = cursor.getString(cursor.getColumnIndex("recipient_ids"));
                address1=getadd(res1);//to obtain address from canonical-addresses
                String dname = getcontactname(address1);
                if(dname!=null)//contact exits
                ((TextView)view).setText(dname);//contact exists
                else
                ((TextView)view).setText(address1);//set display name
                return true;

            case R.id.tv_body:
                String body = cursor.getString(cursor.getColumnIndexOrThrow("snippet"));
                ((TextView)view).setText(body);//set message body
                return true;

            default:
            return false;
            }
        }
    });
    lv.setAdapter(mAdapter);
    mCallbacks=this;
    lm = getLoaderManager();
    //Initiating the loader
    lm.initLoader(LOADER_ID, null,mCallbacks);
    return rootview;
}
@Override
public android.support.v4.content.Loader<Cursor> onCreateLoader(
        int arg0, Bundle arg1) {
    Uri baseUri = Uri.parse("content://mms-sms/conversations?simple=true");
    return new CursorLoader(getActivity(), baseUri,
               null, null, null,"date desc");
}
@Override
public void onLoadFinished(
        android.support.v4.content.Loader<Cursor> arg0, Cursor arg1) {
    switch (arg0.getId()) {
      case LOADER_ID:
        mAdapter.swapCursor(arg1);
        break;
    }
    // The listview now displays the queried data
}
@Override
public void onLoaderReset(android.support.v4.content.Loader<Cursor> arg0) {

    mAdapter.swapCursor(null);
}

}///main activity

App loads very fast now.But scrolling is laggy.How to implement viewholder design pattern inside a custom simplecursoradapter or a cursoradapter?

Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
  • don't use an arrayadapter, for starters. use a cursoradapter, as the select query will be faster than loading all the `ConvItem` from db into memory. – njzk2 May 28 '14 at 18:07
  • 1
    Another question with relevant answers: [Lazy load of Images in ListView](http://stackoverflow.com/q/541966/1590950) – indivisible May 28 '14 at 18:11
  • you are talking about simplecursoradapter right?But how can you set photo for each row? –  May 28 '14 at 18:16

1 Answers1

2

Is there a way to improve the speed?

Move your photo loading to a background thread, perhaps by using an image management library like Picasso.

Beyond that, use Traceview and other tools to determine specifically where your problems lie.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • can you exemplify the moving photo loading to background thread? –  May 28 '14 at 18:13
  • @user3370397: You can click the link in my answer and read the Picasso documentation. Their GitHub repo has a sample app IIRC, and here is another (also demonstrating the use of their Retrofit library): https://github.com/commonsguy/cw-omnibus/tree/master/HTTP/Picasso – CommonsWare May 28 '14 at 18:14
  • thanks for the comment.but when i tried deleting all the photo setting statements and ran the code,it takes same amount of delay.Please check the edited post :) –  May 28 '14 at 18:51
  • @user3370397: Then, as I noted in my answer, use Traceview to determine where your time is being spent. You will probably find that it is all in your N `query()` calls. – CommonsWare May 28 '14 at 22:34
  • I get to understand that failing to manage query effectively is the problem residing over here.It can be corrected using Loader.Callbacks. Can some help me to implement loaders here in this code? –  Jun 02 '14 at 17:49
  • Finally i get to run the app fast using loaders,cursor adapters.Now the app opens without any delay.But scrolling is somewhat laggy compared to the custom adapter.Can listview optimization done for simple cursor adapter like viewholder design pattern?If so, how? –  Jun 03 '14 at 16:38