1

What I'm trying do to is using a custom CursorAdapter, in order to choose which layout to show and also to populate View items such as TextViews and also ImageView.

Now not in all the listview items there gonna be an image.

My CursorAdapter code is -

    private static class ViewHolder {
    TextView mesg;
    TextView mesg2;
    TextView send;
    ImageView myImage;
}

public class ChatCursorAdapter extends CursorAdapter implements OnClickListener {

    public ChatCursorAdapter(Context context, Cursor c) {
        super(context, c, 0);
    }

    @Override
    public int getCount() {
        return getCursor() == null ? 0 : super.getCount();
    }

    @Override
    public int getViewTypeCount() {
        return 2;
    }

    @Override
    public int getItemViewType(int _position) {

        Cursor cursor = (Cursor) getItem(_position);
        return getItemViewType(cursor);
    }

      private int getItemViewType(Cursor cursor) {

            String sender = cursor.getString(2);

                    String saveUser =   user;

                    if (saveUser.equalsIgnoreCase(sender)){

                        return 0;
                    }else{
                        return 1;
                    }

      }



    @Override
    public void bindView(View view, Context context, Cursor cursor) {

    ViewHolder holder = (ViewHolder) view.getTag();

        String msg = cursor.getString(3);
        String msg2 = cursor.getString(4);
        String sender = cursor.getString(2);

        holder.mesg.setText(getSmiledText(Main.this,msg));
        holder.mesg2.setText(getSmiledText(Main.this,msg2));
        holder.send.setText(sender);


        picPath = cursor.getString(8);


        if(picPath == null || picPath.isEmpty()){

                holder.myImage.setVisibility(View.GONE);

             }else{

              File file = new File(picPath);

              if(file.exists()){

                 new AsyncImageSetter(holder.myImage, picPath).execute();
                 holder.myImage.setOnClickListener(this);

            }



             }


    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {
        ViewHolder holder = new ViewHolder();
        View itemLayout = null;
        switch(getItemViewType(cursor)){
        case 0:
            itemLayout = getLayoutInflater().inflate(R.layout.msg_item1,parent, false);
            break;
        case 1:
            itemLayout =  getLayoutInflater().inflate(R.layout.msg_item13, parent,false);
            break;

        }

        itemLayout.setTag(holder);
        holder.mesg = (TextView) itemLayout.findViewById(R.id.text_start);
        holder.mesg2 = (TextView) itemLayout.findViewById(R.id.text_end);
        holder.send = (TextView) itemLayout.findViewById(R.id.text_from);
        holder.myImage = (ImageView) itemLayout.findViewById(R.id.imageView_msgpic);
        return itemLayout;

    }



}

As you can see when there a need to load an image to the ImageView, I'm using asynctask in order to let the flow of the list view scrolling to be much more smoother.

This how the asynctask code is -

     public class AsyncImageSetter extends AsyncTask<Void, Void, Void> {

     private ImageView img;
     private String path;
     private Bitmap bm;

         public AsyncImageSetter(ImageView img, String path) {
         this.img = img;
         this.path = path;


     }

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

        bm = BitmapFactory.decodeFile(path);
        bm = setImageToImageView(path);

         return null;
     }

     @Override
     protected void onPostExecute(Void result) {

         img.setTag(path);
         img.setImageBitmap(bm);
         //img.setVisibility(View.VISIBLE);

         super.onPostExecute(result);
     }

}

Well the thing is that it sure made the scrolling alot more smoother, but it seems to make the app crash a lot of times.

The logcat says the next -

    03-24 17:07:34.125: E/AndroidRuntime(15422): FATAL EXCEPTION: AsyncTask #2
03-24 17:07:34.125: E/AndroidRuntime(15422): java.lang.RuntimeException: An error occured while executing doInBackground()
03-24 17:07:34.125: E/AndroidRuntime(15422):    at android.os.AsyncTask$3.done(AsyncTask.java:299)
03-24 17:07:34.125: E/AndroidRuntime(15422):    at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:352)
03-24 17:07:34.125: E/AndroidRuntime(15422):    at java.util.concurrent.FutureTask.setException(FutureTask.java:219)
03-24 17:07:34.125: E/AndroidRuntime(15422):    at java.util.concurrent.FutureTask.run(FutureTask.java:239)
03-24 17:07:34.125: E/AndroidRuntime(15422):    at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
03-24 17:07:34.125: E/AndroidRuntime(15422):    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
03-24 17:07:34.125: E/AndroidRuntime(15422):    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
03-24 17:07:34.125: E/AndroidRuntime(15422):    at java.lang.Thread.run(Thread.java:841)
03-24 17:07:34.125: E/AndroidRuntime(15422): Caused by: java.lang.OutOfMemoryError
03-24 17:07:34.125: E/AndroidRuntime(15422):    at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
03-24 17:07:34.125: E/AndroidRuntime(15422):    at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:623)
03-24 17:07:34.125: E/AndroidRuntime(15422):    at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:378)
03-24 17:07:34.125: E/AndroidRuntime(15422):    at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:417)
03-24 17:07:34.125: E/AndroidRuntime(15422):    at at.vcity.androidim.MainChat$AsyncImageSetter.doInBackground(MainChat.java:3356)
03-24 17:07:34.125: E/AndroidRuntime(15422):    at at.vcity.androidim.MainChat$AsyncImageSetter.doInBackground(MainChat.java:1)
03-24 17:07:34.125: E/AndroidRuntime(15422):    at android.os.AsyncTask$2.call(AsyncTask.java:287)
03-24 17:07:34.125: E/AndroidRuntime(15422):    at java.util.concurrent.FutureTask.run(FutureTask.java:234)
03-24 17:07:34.125: E/AndroidRuntime(15422):    ... 4 more

So what am I doing wrong here?

Thanks for any kind of help

4this
  • 759
  • 4
  • 13
  • 27

2 Answers2

1

It looks to me like the Bitmap you are trying to store in memory is too large to be stored in your tablet/emulator's memory. Here;

bm = BitmapFactory.decodeFile(path);

See if the code works with a much smaller file than the one that is coming from your current path (also see if it works with a smaller list). This could also be an instance of 'The straw that broke the camel's back'. If your current application is already very memory intensive you might have to go through your current code and optimise for memory management.

Because your are creating an AsyncTask for each item in the ListView, your trying to hold that many images in memory at once. So you might need to find another way to do it. You might need to try loading the image's thumbnail into your ImageViews instead.

I hope this helps.


An Example Implementation


Just to run through a potential way to implement the above linked thumbnail example, what I might do to get the thumbnail is I could store the context passed to your ChatCursorAdapter by adding the following as a class variable (and instantiate it in the constructor);

Context ourContext;

public ChatCursorAdapter(Context context, Cursor c) {       
    super(context, c, 0);
    ourContext = context;
}

@Override
public void bindView(View view, Context context, Cursor cursor) {
    ...
    new AsyncImageSetter(holder.myImage, picPath, ourContext.getContentResolver()).execute();
    ...
}

Then (as above) I might use the Context to get the ContentResolver instance and pass that instance into your AsyncTask via it's constructor. Then we could add the method in the example code to get the thumbnail into the custom AsyncTask, which might look something like this;

ContentResolver cr;

public AsyncImageSetter(ImageView img, String path, ContentResolver cr) {
     this.img = img;
     this.path = path;
     this.cr = cr;
     }
...

@Override
 protected Void doInBackground(Void... params) {
    try{
         bm = getThumbnail(cr, path);
    }catch(Exception e){}

     return null;
 }

private Bitmap getThumbnail(ContentResolver cr, String path) throws Exception {

    Cursor ca = cr.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[] { MediaStore.MediaColumns._ID }, MediaStore.MediaColumns.DATA + "=?", new String[] {path}, null);
    if (ca != null && ca.moveToFirst()) {
        int id = ca.getInt(ca.getColumnIndex(MediaStore.MediaColumns._ID));
        ca.close();
        return MediaStore.Images.Thumbnails.getThumbnail(cr, id, MediaStore.Images.Thumbnails.MICRO_KIND, null );
    }

    ca.close();
    return null;

}
Community
  • 1
  • 1
Rudi Kershaw
  • 12,332
  • 7
  • 52
  • 77
  • I'm running it on a mobile device (galaxy s3), so if it's not working on a real device , it seems like it is useless – 4this Mar 24 '14 at 15:39
  • what do you mean in loading image's thumbnail exactly? – 4this Mar 24 '14 at 15:48
  • Android stores a 'thumbnail' of each image stored. Which is a very low resolution version of each stored image. The resolution is good enough for almost anything your likely to see though (but small enough that it wont eat up all your memory). I added a link with an example. – Rudi Kershaw Mar 24 '14 at 15:49
  • @4this - How many items do you have in your `ListView` by the way? – Rudi Kershaw Mar 24 '14 at 15:55
  • It's a listview that show inputs from database - so the number of the listview items is unlimited, as if to say it shows the number of current DB rows. And btw I'm trying to figure out how to use the thumbnails code - and I'm sure getting stuck in understand a thing or two there – 4this Mar 24 '14 at 16:03
  • @4this - Ah okay. An unlimited list would definitely be a problem for any sizable images. Even with the thumbnails you might have to break the list into pages after so many entries. I'll see if I can throw together some example code for using the thumbnails. – Rudi Kershaw Mar 24 '14 at 16:23
  • Interesting point - how can i create pages for listview? how can I limit the number of items and each page? and how they scrolling between the pages been done? – 4this Mar 24 '14 at 16:30
  • @4this - I've added some example code for getting the thumbnail. I am afraid though this is getting a little out of the scope of the question. You might want to post a new question on limiting the number of items in a `ListView`. Let me know how you get on. – Rudi Kershaw Mar 24 '14 at 16:48
  • First of all thank you for helping, now about the thumbnail example - where should i instance the ContentResolver? What the function gives back? Because if you see at my code i'm setting a bitmap into the Imageview – 4this Mar 24 '14 at 16:51
  • @4this - You get the `ContentResolver` from your `Context`, so from the example above you would call `ourContext.getContentResolver();`. I edited that into the answer. – Rudi Kershaw Mar 24 '14 at 16:57
0

The cause is in your stacktrace: java.lang.OutOfMemoryError. It looks like one of two things -- either your images are too large or you are leaking memory (or both).

My recommendation would be to use one of the several libraries for doing background image loading. Here are a couple of suggestions:

mikejonesguy
  • 9,779
  • 2
  • 35
  • 49