0

I'm curious that is it normal for CustomAdapter to keep looping the same thing? For example, i have 3 rows of record from database and i would like to display them on my list. Once i use customAdapter to display, it will loop the same thing 3 times. Means if I have 10 row if records, it will loop 10 times*10 records = 100times of looping. If like this, my program sure will run out of memory. Any solution for this? I have attach my log as below. If you notice "~~ row id~~ 0", it keeps looping 3 times the same.

 public class ViewMsgMain extends ListActivity{

// Retrieve member id from local database
String memberid = FypGeneral.LOGINMEMBERID;
SharedPreferences sp_memberid;
int memid;

String sender_id="", content_type = "", content_path = "", content_id = "", imageaudio_id = "", content_date="";
String over_contentid = "", uploaded_content_id = "", receiver_id = "", read_status = "", 
        sender_member_image="", imageAudio_image="";
InputStream inputstream = null;
StringBuilder stringbuilder = null;
String result = null;

//  retrieved message
String retrieveMsgId[] , uploaded_content_type[];
private LayoutInflater mInflater;
private Vector<RowData> data;
RowData rd;
CustomAdapter adapter;
int pos=1;
String content_type_split[];
String content_date_split[];
String sender_member_image_split[], uploaded_content_id_split[];
String content_path_split[], content_id_split[];
String imageAudio_image_split[];

Bitmap bitmap;

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.viewmsglist);

    //===================================================
    // Get member id from local database
    sp_memberid = getSharedPreferences(memberid, MODE_PRIVATE);
    if(sp_memberid.contains("memberid")==true)
    {memid = sp_memberid.getInt("memberid", 0);}
    Log.e("homepage member id == ", "~ "+memid);
    //===================================================

    try{
        //http post
        HttpClient httpclient = new DefaultHttpClient();
        HttpPost httppost = new HttpPost("http://localhost/project/viewmessage.php?memberid="+memid);
        //httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs)); //encode a list of NameValuePair objects suitable for HTTP calls
        HttpResponse response = httpclient.execute(httppost); // to make an HTTPPOST call with the HttpClient
        HttpEntity entity = response.getEntity();
        inputstream = entity.getContent();
    }
    catch(Exception e){
        Toast.makeText(getBaseContext(),e.toString() ,Toast.LENGTH_LONG).show();
    }

    //Convert response to string  
    try
    {
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputstream,"UTF-8"));

        stringbuilder = new StringBuilder();

        String line = null;

        while ((line = reader.readLine()) != null) 
        {
            stringbuilder.append(line + "\n");
        }

        inputstream.close();

        result = stringbuilder.toString();
    }
    catch(Exception e)
    {
        Toast.makeText(getBaseContext(),e.toString() ,Toast.LENGTH_LONG).show();
    }
    //END Convert response to string   
    try{
        JSONArray jArray = new JSONArray(result);
        JSONObject json_data=null;
        if(jArray.length() == 0)
        {
            Toast.makeText(getApplicationContext(), "Tiada mesej", Toast.LENGTH_SHORT).show();
        }
        else
        {
            for(int i=0;i<jArray.length();i++)
            {
                json_data = jArray.getJSONObject(i);
                content_type = content_type + json_data.getString("uploadedcontenttype")+",";
                content_path = content_path+"http://localhost/project/"+(String) json_data.getString("contentpath")+",";
                imageAudio_image = imageAudio_image + json_data.getString("imageaudiopath")+",";
                //r.add(json_data.getString("member_id") + json_data.getString("member_name") + json_data.getString("member_usernamepath"));
                content_id = content_id + json_data.getString("contentid")+",";
                imageaudio_id = imageaudio_id + json_data.getString("imageaudioid")+",";
                content_date = content_date + json_data.getString("contentdate")+",";
                over_contentid = over_contentid + json_data.getString("overallid")+",";
                uploaded_content_id = uploaded_content_id +  json_data.getString("uploadedcontentid")+",";
                sender_id = sender_id +  json_data.getString("senderid")+",";
                receiver_id = receiver_id + json_data.getString("receiverid")+",";
                read_status =read_status + json_data.getString("readstatus")+",";
                sender_member_image = sender_member_image + "http://localhost/project/www/"+json_data.getString("memberimage")+",";
            }
        }
        Log.e("retrieved ", "~ "+content_type + "@ " +  " # "+ content_path + " $ "+ sender_id);
    }
    catch(JSONException e1){
        Toast.makeText(getApplicationContext(), "Tiada mesej", Toast.LENGTH_SHORT).show();
        Log.e("erroe ", e1.toString() );
        //Toast.makeText(getBaseContext(),e1.toString() ,Toast.LENGTH_LONG).show();
    } catch (ParseException e1) {
        Toast.makeText(getBaseContext(),e1.toString() ,Toast.LENGTH_LONG).show();
    }

    // Split the data retrieved from database
    content_type_split = content_type.split(",");
    content_path_split = content_path.split(",");
    content_id_split = content_id.split(",");
    content_date_split = content_date.split(",");
    sender_member_image_split = sender_member_image.split(",");
    uploaded_content_id_split = uploaded_content_id.split(",");
    imageAudio_image_split = imageAudio_image.split(",");

    mInflater = (LayoutInflater) getSystemService(
            Activity.LAYOUT_INFLATER_SERVICE);
    data = new Vector<RowData>();
    for(int i=0;i<content_type_split.length;i++){

        try {
            rd = new RowData(i,content_type_split[i],content_date_split[i]);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        data.add(rd);
    }
    CustomAdapter adapter = new CustomAdapter(this, R.layout.list,
            R.id.title, data);
    setListAdapter(adapter);
    //getListView().setTextFilterEnabled(true);
}

public void onListItemClick(ListView parent, View v, int position,
        long id) {
    // Show details in another page
    Intent viewMsgIntent = new Intent(ViewMsgMain.this, ViewMsgDetails.class);
    viewMsgIntent.putExtra("content_type", content_type_split[position]);
    viewMsgIntent.putExtra("content_path", content_path_split[position]);
    viewMsgIntent.putExtra("sender_member_image", sender_member_image_split[position]);
    viewMsgIntent.putExtra("imageAudio_image", imageAudio_image_split[position]);
    startActivity(viewMsgIntent);
}
private class RowData {
    protected int mId;
    protected String mTitle;
    protected String mDetail;
    RowData(int id,String title,String detail){
        mId=id;
        mTitle = title;
        mDetail=detail;
    }
    @Override
    public String toString() {
        Log.e("rowdata",mId+" "+mTitle+" "+mDetail);
        return mId+" "+mTitle+" "+mDetail;
    }
}
private class CustomAdapter extends ArrayAdapter<RowData> {

    public CustomAdapter(Context context, int resource,
            int textViewResourceId, List<RowData> objects) {               

        super(context, resource, textViewResourceId, objects);
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {   

        ViewHolder holder = null;
        TextView title = null;
        TextView detail = null;
        ImageView i11=null;
        ImageView i112=null;
        RowData rowData= getItem(position);
        Log.e("content_type_split.length ", "## "+content_type_split.length+"   ~~ row id~~   "+ rowData.mId);
        if(null == convertView){
            convertView = mInflater.inflate(R.layout.list, null);
            holder = new ViewHolder(convertView);
            convertView.setTag(holder);
        }
        holder = (ViewHolder) convertView.getTag();
        title = holder.gettitle();
        title.setText(rowData.mTitle);
        detail = holder.getdetail();
        detail.setText(rowData.mDetail);                                                     

        i11=holder.getImage();
        if(content_type_split[rowData.mId].equals("1"))
        {
            i11.setImageResource(R.drawable.imageicon2);
        }
        else if(content_type_split[rowData.mId].equals("2"))
        {
            i11.setImageResource(R.drawable.audioicon2);
        }
        else if(content_type_split[rowData.mId].equals("3"))
        {
            i11.setImageResource(R.drawable.videoicon2);
        }

        Log.e("get view ," , " ~ "+sender_member_image_split[rowData.mId]);

        i112=holder.getImage2();
        try {
            Log.e("enter bitmap ", "yes");
            bitmap = BitmapFactory.decodeStream((InputStream)new URL(sender_member_image_split[rowData.mId]).getContent());
            i112.setImageBitmap(bitmap);

        } catch (MalformedURLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return convertView;
    }
    private class ViewHolder {
        private View mRow;
        private TextView title = null;
        private TextView detail = null;
        private ImageView i11=null;
        private ImageView i112=null; 

        public ViewHolder(View row) {
            mRow = row;
        }
        public TextView gettitle() {
            if(null == title){
                title = (TextView) mRow.findViewById(R.id.title);
            }
            return title;
        }     

        public TextView getdetail() {
            if(null == detail){
                detail = (TextView) mRow.findViewById(R.id.detail);
            }
            return detail;
        }
        public ImageView getImage() {
            if(null == i11){
                i11 = (ImageView) mRow.findViewById(R.id.img);
            }
            return i11;
        }

        public ImageView getImage2() {
            if(null == i112){
                i112 = (ImageView) mRow.findViewById(R.id.img2);

            }
            return i112;
        }
    }
}

}

Log

            05-15 21:59:52.660: E/content_type_split.length(7388): ## 3   ~~ row id~~   0
            05-15 21:59:52.670: D/dalvikvm(7388): GC_CONCURRENT freed 352K, 11% free 9318K/10439K, paused 2ms+3ms
            05-15 21:59:52.690: E/get view ,(7388):  ~ http://localhost/project/www/username/kids.jpg
            05-15 21:59:52.690: E/enter bitmap(7388): yes
            05-15 21:59:52.720: W/System.err(7388): Error reading from ./org/apache/harmony/awt/www/content/image/jpeg.class
            05-15 21:59:52.740: E/content_type_split.length(7388): ## 3   ~~ row id~~   1
            05-15 21:59:52.740: E/get view ,(7388):  ~ http://localhost/project/www/username/1_2012512080548_DSC00701.JPG
            05-15 21:59:52.740: E/enter bitmap(7388): yes
            05-15 21:59:52.790: W/System.err(7388): Error reading from ./org/apache/harmony/awt/www/content/image/jpeg.class
            05-15 21:59:52.810: D/dalvikvm(7388): GC_FOR_ALLOC freed 562K, 14% free 9034K/10439K, paused 23ms
            05-15 21:59:52.810: I/dalvikvm-heap(7388): Grow heap (frag case) to 9.477MB for 614416-byte allocation
            05-15 21:59:52.850: D/dalvikvm(7388): GC_FOR_ALLOC freed <1K, 14% free 9633K/11079K, paused 22ms
            05-15 21:59:52.880: E/content_type_split.length(7388): ## 3   ~~ row id~~   2
            05-15 21:59:52.890: E/get view ,(7388):  ~ http://localhost/project/www/username/kids.jpg
            05-15 21:59:52.890: E/enter bitmap(7388): yes
            05-15 21:59:52.930: W/System.err(7388): Error reading from ./org/apache/harmony/awt/www/content/image/jpeg.class
            05-15 21:59:52.950: E/content_type_split.length(7388): ## 3   ~~ row id~~   0
            05-15 21:59:52.950: E/get view ,(7388):  ~ http://localhost/project/www/username/kids.jpg
            05-15 21:59:52.950: E/enter bitmap(7388): yes
            05-15 21:59:52.980: W/System.err(7388): Error reading from ./org/apache/harmony/awt/www/content/image/jpeg.class
            05-15 21:59:52.990: E/content_type_split.length(7388): ## 3   ~~ row id~~   1
            05-15 21:59:52.990: E/get view ,(7388):  ~ http://localhost/project/www/username/1_2012512080548_DSC00701.JPG
            05-15 21:59:52.990: E/enter bitmap(7388): yes
            05-15 21:59:53.040: W/System.err(7388): Error reading from ./org/apache/harmony/awt/www/content/image/jpeg.class
            05-15 21:59:53.060: D/dalvikvm(7388): GC_FOR_ALLOC freed 1064K, 19% free 9034K/11079K, paused 23ms
            05-15 21:59:53.100: E/content_type_split.length(7388): ## 3   ~~ row id~~   2
            05-15 21:59:53.100: E/get view ,(7388):  ~ http://localhost/project/www/username/kids.jpg
            05-15 21:59:53.100: E/enter bitmap(7388): yes
            05-15 21:59:53.130: W/System.err(7388): Error reading from ./org/apache/harmony/awt/www/content/image/jpeg.class
            05-15 21:59:53.170: D/dalvikvm(7388): GC_CONCURRENT freed 820K, 19% free 9033K/11079K, paused 2ms+2ms
            05-15 21:59:53.170: E/content_type_split.length(7388): ## 3   ~~ row id~~   0
            05-15 21:59:53.180: E/get view ,(7388):  ~ http://localhost/project/www/username/kids.jpg
            05-15 21:59:53.180: E/enter bitmap(7388): yes
            05-15 21:59:53.210: W/System.err(7388): Error reading from ./org/apache/harmony/awt/www/content/image/jpeg.class
            05-15 21:59:53.230: E/content_type_split.length(7388): ## 3   ~~ row id~~   1
            05-15 21:59:53.240: E/get view ,(7388):  ~ http://localhost/project/www/username/1_2012512080548_DSC00701.JPG
            05-15 21:59:53.240: E/enter bitmap(7388): yes
            05-15 21:59:53.280: W/System.err(7388): Error reading from ./org/apache/harmony/awt/www/content/image/jpeg.class
            05-15 21:59:53.320: D/dalvikvm(7388): GC_CONCURRENT freed 268K, 13% free 9640K/11079K, paused 3ms+2ms
            05-15 21:59:53.330: E/content_type_split.length(7388): ## 3   ~~ row id~~   2
            05-15 21:59:53.350: E/get view ,(7388):  ~ http://localhost/project/www/username/kids.jpg
            05-15 21:59:53.350: E/enter bitmap(7388): yes
Sally
  • 33
  • 2
  • 13

2 Answers2

2

As stated by Dheeresh Singh, the issue should not be how many times getView() is called. Although this does highlight the likely problem. Because getView() can and will be called countless times, the code within getView() needs to be efficient. There are several concerns with your implementation of getView().

1) The Out Of Memory issue (OOM) is most likely caused by the bitmaps.

  • The bitmap is not properly handled when you are done with it as it still resides in memory even after getView() is exited. This is the nature of Bitmaps in Android. Calling bitmap.recycle() will free this memory and should prevent OOM.

  • Resizing or reducing the resolution of the bitmaps could improve memory use.

2) Every time getView() is run, the desired bitmap is downloaded and decoded each time.

  • This is not desirable as these intensive tasks are running on the UI thread (which Activities run on and all ui changes must occur on). If the time to complete these tasks exceed ~5 seconds, Android will flag the app as stalled and issue an Application Not Responding (ANR). Even if an ANR does not occur, the scrolling performance of the ListView will be abysmal.

  • To solve this, these tasks of downloading, decoding, and bitmap altering (if desired) should happen in another thread (like AsyncTask or HandlerThread). A typical scenario would be that when you need a bitmap, you fire off an AsyncTask or issue a request to a bitmap processing thread. As part of a thread request, a listener should be provided so that when the bitmap is ready on the bitmap thread it will use the listener to signal the UI thread to update the ImageView. This implementation is not trivial but is required for robust Android apps.

  • An important side note: Do not leak the Activity to other threads. When a leak occurs and the Activity is destroyed by Android (which happens often and for a variety of reasons, orientation change is one), the Activity cannot be garbage collected because the instance exists in another thread. Android then fires up a new instance of the Activity with the dead Activity still around. An OOM will eventually occur. For example, when creating a listener for a bitmap thread, do not include an ImageView (as this has Activity context) as part of the listener -- unless you explicitly remove the listener from the bitmap thread in the Activity's onDestroy(). This is different for an AsyncTask - just process all ui changes in onPostExecute().

    Memory leakage in event listener

  • Caching should be used to avoid repeated downloading and processing of bitmaps. A possible implementation would use LruCache for a memory cache and DiskLruCache for a disk cache.

    http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html

3) ViewHolder use.

  • I'm not sure about the usefulness of having a reference to 'convertView' within it's ViewHolder. The only use for this cyclical reference is to implement the getters, though this is slightly inefficient for rapid execution getView(). This would be a typical implementation with ViewHolder as an inner class of the Activity instead of CustomAdapter.

    private class CustomAdapter extends ArrayAdapter<RowData> {
    
        public CustomAdapter(Context context, int resource,
                int textViewResourceId, List<RowData> objects) {               
    
            super(context, resource, textViewResourceId, objects);
        }
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {   
    
            ViewHolder holder = null;
            RowData rowData= getItem(position);
            Log.e("content_type_split.length ", "## "+content_type_split.length+"   ~~ row id~~   "+ rowData.mId);
            if(null == convertView){
                convertView = mInflater.inflate(R.layout.list, null);
                holder = new ViewHolder(convertView);
                convertView.setTag(holder);
                holder.title = (TextView) convertView.findViewById(R.id.title);
                holder.detail = (TextView) convertView.findViewById(R.id.detail);
                holder.i11 = (ImageView) convertView.findViewById(R.id.img);
                holder.i112 = (ImageView) convertView.findViewById(R.id.img2);
    
            } else {
                holder = (ViewHolder) convertView.getTag();
            }
    
            holder.title.setText(rowData.mTitle);
            holder.detail.setText(rowData.mDetail);                                                     
    
            if(content_type_split[rowData.mId].equals("1"))
            {
                holder.i11.setImageResource(R.drawable.imageicon2);
            }
            else if(content_type_split[rowData.mId].equals("2"))
            {
                holder.i11.setImageResource(R.drawable.audioicon2);
            }
            else if(content_type_split[rowData.mId].equals("3"))
            {
                holder.i11.setImageResource(R.drawable.videoicon2);
            }
    
            Log.e("get view ," , " ~ "+sender_member_image_split[rowData.mId]);
    
    
            try {
                Log.e("enter bitmap ", "yes");
                bitmap = BitmapFactory.decodeStream((InputStream)new URL(sender_member_image_split[rowData.mId]).getContent());
                holder.i112.setImageBitmap(bitmap);
    
            } catch (MalformedURLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    
            return convertView;
        }
    }
    
    private class ViewHolder {
        TextView title = null;
        TextView detail = null;
        ImageView i11=null;
        ImageView i112=null;
    }
    
Community
  • 1
  • 1
mindriot
  • 14,149
  • 4
  • 29
  • 40
1

Please look over this answer

custom listview adapter getView method being called multiple times, and in no coherent order

This is not an issue, there is absolutely no guarantee on the order in which getView() will be called nor how many times.

Community
  • 1
  • 1
Dheeresh Singh
  • 15,643
  • 3
  • 38
  • 36
  • Thanks for your reply. but after i change the layout_height to fill_parent, my list still out of memory. in fact, i only have 10 rows of data for another list. may i know why and any solution that can let me solve the problem? thanks – Sally May 15 '12 at 15:34