I'm having a memory leak in a cursor Loader. This is the log of the memory Leak: For this line:
* references com.carlos.capstone.adapters.SuggestionsAdapter.mContext
I deduce that the leak is happening in the "handler" of the SuggestionAdapter and because int the log there are references to a Cursor. For some reason a Cursor cannot be closed but I don't know why.
This is the LeakCanary log:
03-24 20:11:29.929 9939-12585/com.carlos.capstone D/LeakCanary: In com.carlos.capstone:1.0:1.
03-24 20:11:29.929 9939-12585/com.carlos.capstone D/LeakCanary: * com.carlos.capstone.MainActivity has leaked:
03-24 20:11:29.929 9939-12585/com.carlos.capstone D/LeakCanary: * GC ROOT android.app.ActivityThread$ApplicationThread.this$0
03-24 20:11:29.929 9939-12585/com.carlos.capstone D/LeakCanary: * references android.app.ActivityThread.mActivities
03-24 20:11:29.929 9939-12585/com.carlos.capstone D/LeakCanary: * references android.util.ArrayMap.mArray
03-24 20:11:29.929 9939-12585/com.carlos.capstone D/LeakCanary: * references array java.lang.Object[].[1]
03-24 20:11:29.929 9939-12585/com.carlos.capstone D/LeakCanary: * references android.app.ActivityThread$ActivityClientRecord.activity
03-24 20:11:29.929 9939-12585/com.carlos.capstone D/LeakCanary: * references com.carlos.capstone.MainActivity.mFragments
03-24 20:11:29.929 9939-12585/com.carlos.capstone D/LeakCanary: * references android.support.v4.app.FragmentController.mHost
03-24 20:11:29.929 9939-12585/com.carlos.capstone D/LeakCanary: * references android.support.v4.app.FragmentActivity$HostCallbacks.mAllLoaderManagers
03-24 20:11:29.929 9939-12585/com.carlos.capstone D/LeakCanary: * references android.support.v4.util.SimpleArrayMap.mArray
03-24 20:11:29.929 9939-12585/com.carlos.capstone D/LeakCanary: * references array java.lang.Object[].[1]
03-24 20:11:29.929 9939-12585/com.carlos.capstone D/LeakCanary: * references android.support.v4.app.LoaderManagerImpl.mLoaders
03-24 20:11:29.929 9939-12585/com.carlos.capstone D/LeakCanary: * references android.support.v4.util.SparseArrayCompat.mValues
03-24 20:11:29.930 9939-12585/com.carlos.capstone D/LeakCanary: * references array java.lang.Object[].[2]
03-24 20:11:29.930 9939-12585/com.carlos.capstone D/LeakCanary: * references android.support.v4.app.LoaderManagerImpl$LoaderInfo.mData
03-24 20:11:29.930 9939-12585/com.carlos.capstone D/LeakCanary: * references android.content.ContentResolver$CursorWrapperInner.mCursor
03-24 20:11:29.930 9939-12585/com.carlos.capstone D/LeakCanary: * references android.database.sqlite.SQLiteCursor.mDataSetObservable
03-24 20:11:29.930 9939-12585/com.carlos.capstone D/LeakCanary: * references android.database.DataSetObservable.mObservers
03-24 20:11:29.930 9939-12585/com.carlos.capstone D/LeakCanary: * references java.util.ArrayList.array
03-24 20:11:29.930 9939-12585/com.carlos.capstone D/LeakCanary: * references array java.lang.Object[].[0]
03-24 20:11:29.930 9939-12585/com.carlos.capstone D/LeakCanary: * references android.support.v4.widget.CursorAdapter$MyDataSetObserver.this$0
03-24 20:11:29.930 9939-12585/com.carlos.capstone D/LeakCanary: * references com.carlos.capstone.adapters.SuggestionsAdapter.mContext
03-24 20:11:29.930 9939-12585/com.carlos.capstone D/LeakCanary: * leaks com.carlos.capstone.MainActivity instance
03-24 20:11:29.930 9939-12585/com.carlos.capstone D/LeakCanary: * Reference Key: c38cc8de-15d0-4c6a-8a72-7e759cb7be0a
03-24 20:11:29.930 9939-12585/com.carlos.capstone D/LeakCanary: * Device: LGE google Nexus 5 hammerhead
03-24 20:11:29.930 9939-12585/com.carlos.capstone D/LeakCanary: * Android Version: 5.1.1 API: 22 LeakCanary: 1.3.1
03-24 20:11:29.930 9939-12585/com.carlos.capstone D/LeakCanary: * Durations: watch=6704ms, gc=151ms, heap dump=6915ms, analysis=24691m
This is the structure of the onLoadFinished I have now:
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mCursor=data;
mCursorSearchView=data;
switch(loader.getId()) {
case INDEXES_LOADER:
Log.d(LOG_TAG,"onLoadFinished INDEXES_LOADER");
mAdapterIndexes.swapCursor(mCursor);
break;
case NEWS_LOADER:
Log.d(LOG_TAG,"onLoadFinished NEWS_LOADER");
mAdapterNews.swapCursor(mCursor);
break;
case SUGGESTIONS_LOADER:
Log.d(LOG_TAG,"onLoadFinished SUGGESTION_LOADER");
if (mSuggestionsAdapter != null) {
mSuggestionsAdapter.swapCursor(mCursorSearchView);
}
}
mScrollView.postDelayed(new Runnable() {
@Override
public void run() {
mScrollView.scrollTo(x, y);
}
}, 100);
}
And the onDestroy():
public void onDestroy() {
Log.d(LOG_TAG,"FragmentRegion on Destroy");
if(mCursor!=null && !mCursor.isClosed()) {
mCursor.close();
}
if(mCursorSearchView!=null && !mCursorSearchView.isClosed()) {
mCursorSearchView.close();
}
super.onDestroy();
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
switch(loader.getId()) {
case INDEXES_LOADER:
mAdapterIndexes.swapCursor(null);
break;
case NEWS_LOADER:
mAdapterNews.swapCursor(null);
break;
case SUGGESTIONS_LOADER:
mSuggestionsAdapter.swapCursor(null);
}
}
SuggestionAdapter:
public class SuggestionsAdapter extends CursorAdapter {
public static final int COL_TICKER=1;
public static final int COL_NAME=2;
public static final int COL_EXCHANGE=3;
public static final int COL_SECURITY_TYPE=4;
public SuggestionsAdapter(Context context, Cursor c, int flags) {
super(context, c, flags);
}
public static class ViewHolder{
public TextView tvSymbol;
public TextView tvName;
public TextView tvStockExchange;
public ViewHolder(View v) {
tvSymbol= (TextView) v.findViewById(R.id.symbol);
tvName= (TextView) v.findViewById(R.id.name);
tvStockExchange= (TextView) v.findViewById(R.id.stockExchange);
}
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
View v=LayoutInflater.from(context).inflate(R.layout.suggestion_item,parent,false);
ViewHolder viewHolder=new ViewHolder(v);
v.setTag(viewHolder);
return v;
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
ViewHolder viewHolder= (ViewHolder) view.getTag();
viewHolder.tvSymbol.setText(cursor.getString(COL_TICKER));
String name = Normalizer.normalize(cursor.getString(COL_NAME), Normalizer.Form.NFC);
viewHolder.tvName.setText(name);
viewHolder.tvStockExchange.setText(cursor.getString(COL_EXCHANGE));
}
}
Thanks in advance