basically I have a gridview, in which I show my products (from 10 or 20 up to 1K+ items) each item has a couple of custom textviews, and an Imageview.
I use ViewHolder pattern and also Picasso library to load images asynchronously. I have no problem with showing items, speed or whatsoever, so I think it's safe to say that my getview function works quite nice, although I have one major problem which is with scrolling the gridview, up and down, my memory monitor only shows increase of course the increment rate depends on the item-view contents as when I commented the image loading part for example, the rate decreased.
what I guess is somehow the generated views are always staying in the memory and something prevents them from getting collected by the GC. although sometimes, when I scroll slowly GC comes and collects and memory gets free, but scrolling fast will always result in OOM, sooner or later.
my main question is that if I have a memory leak or not?! since the gc sometimes works (slow scrlling), and sometimes lets the app crashes with OOM Exception.
and clearly I hope to find out my problem and every little bit of help would be appreciated in advance.
xml of the fragment-layout which hosts the Gridview:
<GridView
android:id="@+id/gv_Main"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_marginLeft="2dp"
android:layout_marginRight="2dp"
android:animationCache="false"
android:clickable="true"
android:descendantFocusability="beforeDescendants"
android:gravity="center"
android:scrollingCache="false"
android:verticalSpacing="10dp"
/>
my adapter(this extends CursorAdapter, although I have exactly same problem whit baseadapter as well):
public class ShygunInventoryGridCursorAdapter extends CursorAdapter {
private Context mContext;
private Cursor cursor;
private int numColumns;
private int listItemId, listItemNoImageId, gridItemId;
private String[] type;
private String[] columnTitle;
private int[] viewId;
private String textTemp;
private CyberSetting cyberSetting;
private String unitDesc = "";
private boolean isMain;
private int size;
private LayoutInflater inflater;
private int thumbSize = 200;
private File pic;
private TPictureCatalog pictureCatalog;
private TProductCatalog productCatalog;
//private ViewGroup hostActivity;
//int idTemp;
private LayoutInflater cursorInflater;
public ShygunInventoryGridCursorAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
this.mContext = context;
cursorInflater = (LayoutInflater) context.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
//this.cursor = c;
productCatalog=TProductCatalog.getInstance();
cyberSetting=CyberSetting.getInstance();
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View view = cursorInflater.inflate( R.layout.listview_inventory_product_griditems, parent, false);
ViewHolder holder = new ViewHolder();
holder.tvAttachment = (ImageView) view.findViewById(R.id.iv_inventory_products_griditems_attachment);
holder.imageCount = (TextView) view.findViewById(R.id.imagecount);
holder.tvItemCode = (TextView) view.findViewById(R.id.tv_inventory_products_griditems_ItemCode);
holder.tvProductName = (TextView) view.findViewById(R.id.tv_inventory_products_griditems_Title);
holder.tvPrice = (RialTextView) view.findViewById(R.id.tv_inventory_products_griditems_Price);
holder.tvRemain = (TextView) view.findViewById( R.id.tv_inventory_products_griditems_Remain);
holder.btnMore =(com.rey.material.widget.Button) view.findViewById(R.id.btn_inventory_products_griditems_More);
holder.btnPlus = (com.rey.material.widget.Button) view.findViewById(R.id.btn_inventory_products_griditems_addOne);
view.setTag(holder);
Log.d("CurAd","newView");
return view;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
Log.d("CurAd","bindView");
ViewHolder holder;
holder = (ViewHolder) view.getTag();
holder.itemId = cursor.getString(cursor.getColumnIndex(DatabaseColumnContent.COL_PRODUCT_ITEM_ID.toString()));
TSimpleProduct tempProduct = productCatalog.getSimpleProductById(Integer.parseInt(holder.itemId));
holder.itemGuId = cursor.getString(cursor.getColumnIndex(DatabaseColumnContent.COL_PRODUCT_GUID.toString()));
holder.tvItemCode.setText(cursor.getString(cursor.getColumnIndex(DatabaseColumnContent.COL_PRODUCT_ITEMCODE.toString())));
holder.tvProductName.setText(cursor.getString(cursor.getColumnIndex(DatabaseColumnContent.COL_PRODUCT_ITEMDESC.toString())));
//Remain
if (cyberSetting.getSettingValue(TCyberSettingKey.SHOWITEMREMAIN).equals("1")) {
textTemp = (mContext.getString(R.string.restrictedInfo));
} else {
if (tempProduct.getDefaultUnitValue() == 2 && tempProduct.isUnitDependent()) {
String titleRemain2 = DatabaseColumnContent.COL_PRODUCT_CURSOR_REMAIN2.toString();
textTemp = cursor.getString(cursor.getColumnIndex(titleRemain2));
}
if (cyberSetting.getSettingValue(TCyberSettingKey.SHOWITEMREMAIN).equals("2")) {
if (textTemp == null) {
textTemp = "0";
}
int t = Integer.parseInt(textTemp);
if (t > 0) {
textTemp = mContext.getString(R.string.productAvailable);
} else {
textTemp = mContext.getString(R.string.productUnAvailable);
}
}
}
holder.tvRemain.setText(textTemp);
//Price
String priceLevel = "0";
try {
Register register = Register.getInstance();
priceLevel = register.getPriceLevel();
} catch (NoDaoSetException e) {
e.printStackTrace();
}
if(!priceLevel.equals("0"))
textTemp = cursor.getString(cursor.getColumnIndex(priceLevel));
else
textTemp = "0.0";
if (tempProduct.getDefaultUnitValue() == 2 && tempProduct.isUnitDependent()) {
double price2;
price2 = TLineItem.convertPrice1ToPrice2(Double.parseDouble(textTemp), tempProduct.isUnit1Bigger(), tempProduct.getUnitCoef());
textTemp = TGeneralTools.ConvertDoubleToEnglishString(price2);
if (tempProduct.getUnitDesc2() != null && !tempProduct.getUnitDesc2().equals(""))
unitDesc = " (" + tempProduct.getCompleteUnitDesc2() + ")";
} else {
if (tempProduct.getUnitDesc1() != null && !tempProduct.getUnitDesc1().equals(""))
unitDesc = " (" + tempProduct.getCompleteUnitDesc1() + ")";
}
holder.priceDef = textTemp;
holder.tvPrice.setText(textTemp + unitDesc);
holder.tvRemain.setText(holder.tvRemain.getText() + unitDesc);
//image
pictureCatalog = TPictureCatalog.getInstance();
String defGuid = "";
if (tempProduct.getHasAttachContent() >= 1 && pictureCatalog.isDownloadedAlbumAvailable()) {
defGuid = pictureCatalog.getDefaultPictureGuid(holder.itemGuId);
if (tempProduct.getHasAttachContent() == 1) {
holder.imageCount.setVisibility(View.GONE);
} else {
holder.imageCount.setVisibility(View.VISIBLE);
holder.imageCount.setText(String.valueOf(tempProduct.getHasAttachContent()));
}
} else {
holder.imageCount.setVisibility(View.GONE);
}
String filename = Environment.getExternalStorageDirectory().getPath()
+ FileAddressContent.APPLICATION_HOME_DIRECTORY
+ FileAddressContent.PICTURES_ROOT_DIRECTORY
//+ FileAddressContent.PICTURES_THUMBS_DIRECTORY.toString()
+ defGuid + FileAddressContent.PICTURES_EXTENSION;
pic = new File(filename);
if (pic.exists())
Picasso.with(mContext)
.load(pic)
.error(R.drawable.noimage)
//.placeholder(R.drawable.loading)
.resize(thumbSize, thumbSize)
.centerInside()
.into(holder.tvAttachment);
else
Picasso.with(mContext)
.load(R.drawable.noimage)
.resize(thumbSize, thumbSize)
.centerInside()
.into(holder.tvAttachment);
holder.tvAttachment.setMinimumHeight(thumbSize);
setupGridView(view, holder);
}
private void setupGridView(View view, final ViewHolder holder) {
/*final ImageView iv = (ImageView) view.findViewById(R.id.iv_inventory_products_griditems_attachment);
com.rey.material.widget.Button btMore = (com.rey.material.widget.Button) view.findViewById(R.id.btn_inventory_products_griditems_More);
com.rey.material.widget.Button btPlus = (com.rey.material.widget.Button) view.findViewById(R.id.btn_inventory_products_griditems_addOne);
//final TextView tvTitle = (TextView) view.findViewById(R.id.tv_inventory_products_griditems_Title);
final RialTextView tvPrice = (RialTextView) view.findViewById(R.id.tv_inventory_products_griditems_Price);
final TextView tvItemId = (TextView) view.findViewById(R.id.tv_inventory_products_griditems_ItemId);
final TextView tvGuId = (TextView) view.findViewById(R.id.tv_inventory_products_griditems_GuId);
//final TextView tvRemain = (TextView) view.findViewById(R.id.tv_inventory_products_griditems_Remain);*/
holder.tvAttachment.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/* BitmapDrawable bitmapDrawable = ((BitmapDrawable) iv.getDrawable());*/
showImageDialog(holder.itemGuId);
}
});
holder.btnMore.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/*addToInvoiceDialog(tvTitle.getText().toString(), tvPrice.getText().toString(), tvItemId.getText().toString(),
tvRemain.getText().toString(),iv.getDrawable(),tvGuId.getText().toString());*/
showAddToInvoiceFragment(holder.itemId, holder.priceDef);
}
});
holder.btnPlus.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//InventoryFragment.AddToInvoice(holder.itemId, 1, holder.priceDef, null, null);
}
});
}
public Object getItem(int position) {
return position;
}
public long getItemId(int position) {
return position;
}
private void showImageDialog(String ReferenceGuId) {
//InventoryFragment.openAlbum(ReferenceGuId);
}
private void showAddToInvoiceFragment(String itemId, String priceDef) {
//InventoryFragment.showAddToInvoiceFragment(itemId, priceDef);
}
}
this is the part in my java code where I set the adapter:
ShygunInventoryGridCursorAdapter listAdapter1 = new ShygunInventoryGridCursorAdapter(context,cursor, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
gridView.setNumColumns(colCount);
gridView.setAdapter(listAdapter1);
MAT screen( I could not reach anywhere here, I mean non of roots or ... was my own code)
Not to mention, adding System.gc()
at the end of get View
method in the adapter will solve everything, but I'm sure it's not the right thing to do