单行代码导致我的应用中出现内存泄漏。我调试了200多次但仍无法找到泄漏的原因。我已经将Content Provider实现与我拥有的其他实现进行了比较,代码是相同的。该应用程序运行良好,除非我旋转设备时,应用程序LeakCanary检测到泄漏。装载机似乎也很好。任何帮助都将受到高度赞赏。
这是导致内存泄漏的代码行:(如果我注释掉这行代码,我的内存泄漏就会消失,显然我的列表没有填充数据)
mAdapterIndexes.swapCursor(data);
这是我使用Loader的片段:
public class FragmentAmerica extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
private NonScrollListView mNewsListView;
private NonScrollListView mIndexesListView;
private ScrollView mScrollView;
private boolean mRotationDone=false;
private static final String KEY_POSX="x_pos";
private static final String KEY_POSY="y_pos";
private static final int NEWS_LOADER=0;
private static final int INDEXES_LOADER=1;
private RssNewsAdapter mAdapter;
private IndexesAdapter mAdapterIndexes;
// private IndexesAdapter mAdapterIndexes;
private static final String selectionNews =CapstoneContract.NewsEntity.REGION + " =?";
private static final String[] selectionArgsNews =new String[]{"AMERICA"};
private static final String selectionIndexes=CapstoneContract.IndexesEntity.REGION + " =?";
private static final String[] selectionArgsIndexes=new String[]{"AMERICA"};
private static final String sortByDateDesc = CapstoneContract.NewsEntity.DATE + " DESC";
private int x;
private int y;
LinearListView.OnItemClickListener mListener = new LinearListView.OnItemClickListener() {
@Override
public void onItemClick(LinearListView parent, View view, int position,long id) {
final TextView tvLink,tvTitle;
tvLink= (TextView) view.findViewById(R.id.txtUrlLink);
tvTitle= (TextView) view.findViewById(R.id.txtTitulo);
Uri uri=null;
uri = Uri.parse(tvLink.getText().toString());
//customization possibilities
CustomTabsIntent customTabsIntent = new CustomTabsIntent.Builder()
.setToolbarColor(ContextCompat.getColor(getActivity(),R.color.colorPrimary))
.setShowTitle(true)
.build();
CustomTabActivityHelper.openCustomTab(getActivity(), customTabsIntent,uri,
//in case the user doen't have chromium v 45 installed, offer an alternative
//browser experience with WebView
new CustomTabActivityHelper.CustomTabFallback() {
@Override
public void openUri(Activity activity, Uri uri) {
Intent intent=new Intent(getActivity(),DetailsNewsActivity.class);
intent.putExtra("url",tvLink.getText());
intent.putExtra("title",tvTitle.getText());
startActivity(intent);
}
});
}
};
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
//getActivity().getSupportLoaderManager().initLoader(INDEXES_LOADER, null, this);
getLoaderManager().initLoader(NEWS_LOADER,null,this);
//getActivity().getSupportLoaderManager().initLoader(NEWS_LOADER, null, this);
getLoaderManager().initLoader(INDEXES_LOADER,null,this);
super.onActivityCreated(savedInstanceState);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.fragment_america,container,false);
mNewsListView = (NonScrollListView) view.findViewById(R.id.newsList);
mIndexesListView= (NonScrollListView) view.findViewById(R.id.indexesList);
mScrollView= (ScrollView) view.findViewById(R.id.scrollViewAm);
mScrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
x=mScrollView.getScrollX();
Log.d("VILLANUEVA","x pos:"+mScrollView.getScrollX()+",y pos:"+mScrollView.getScrollY());
y=mScrollView.getScrollY();
}
});
mAdapter =new RssNewsAdapter(getActivity(),null,NEWS_LOADER);
mAdapterIndexes=new IndexesAdapter(getActivity(),null,INDEXES_LOADER);
//set both adapters
mIndexesListView.setAdapter(mAdapterIndexes);
mNewsListView.setAdapter(mAdapter);
// mNewsListView.setOnItemClickListener(mListener);
if(savedInstanceState==null) {
} else {
mRotationDone=true;
//scroll to saved position
mScrollView.scrollTo(savedInstanceState.getInt(KEY_POSX),savedInstanceState.getInt(KEY_POSY));
}
Toast.makeText(getActivity(),"onCreateViewAmerica",Toast.LENGTH_SHORT).show();
return view;
}
@Override
public void onResume() {
super.onResume();
Toast.makeText(getActivity(),"onResumeAmerica",Toast.LENGTH_SHORT).show();
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(KEY_POSX,mScrollView.getScrollX());
outState.putInt(KEY_POSY,mScrollView.getScrollY());
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
if (id==INDEXES_LOADER) {
return new CursorLoader(getActivity().getApplicationContext(),
CapstoneContract.IndexesEntity.CONTENT_URI,null,selectionIndexes,selectionArgsIndexes,null);
} else if(id==NEWS_LOADER) {
return new CursorLoader(getActivity().getApplicationContext(),
CapstoneContract.NewsEntity.CONTENT_URI, null, selectionNews, selectionArgsNews, sortByDateDesc);
}
return null;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
switch(loader.getId()) {
case INDEXES_LOADER:
mAdapterIndexes.swapCursor(data); //if i comment out this line
//line of code the app works fine
break;
case NEWS_LOADER:
mAdapter.swapCursor(data);
break;
}
// mScrollView.postDelayed(new Runnable() {
// @Override
// public void run() {
// mScrollView.scrollTo(x,y);
// }
// },200);
Log.d("VILLANUEVA","move to x pos:"+x+",y pos:"+y);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
switch(loader.getId()) {
case NEWS_LOADER:
mAdapter.swapCursor(null);
break;
case INDEXES_LOADER:
mAdapterIndexes.swapCursor(null);
break;
}
}
@Override
public void onDestroy() {
super.onDestroy();
// ExampleApplication application = (ExampleApplication) getActivity().getApplicationContext();
// application.mustDie(this);
}
}
这是内容提供商中的查询方法:
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Cursor retCursor=null;
final int match = mUriMatcher.match(uri);
switch (match) {
case NEWS:
retCursor=mDbHelper.getReadableDatabase().query(
CapstoneContract.NewsEntity.TABLE_NAME,
projection,
selection,
selectionArgs,
null, //group by
null, //having
sortOrder);
break;
case INDEX:
retCursor=mDbHelper.getReadableDatabase().query(
CapstoneContract.IndexesEntity.TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
sortOrder);
break;
default: throw new UnsupportedOperationException("Unknown Uri" + uri);
}
retCursor.setNotificationUri(getContext().getContentResolver(),uri);
return retCursor;
}
CanaryLeak日志:
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: In com.carlos.capstone:1.0:1.
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * com.carlos.capstone.MainActivity has leaked:
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * GC ROOT static android.view.WindowManagerGlobal.sDefaultWindowManager
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * references android.view.WindowManagerGlobal.mViews
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * references java.util.ArrayList.array
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * references array java.lang.Object[].[0]
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * references com.android.internal.policy.impl.PhoneWindow$DecorView.mAttachInfo
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * references android.view.View$AttachInfo.mScrollContainers
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * references java.util.ArrayList.array
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * references array java.lang.Object[].[1]
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * references com.carlos.capstone.customcomponents.NonScrollListView.mAdapter
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * references com.carlos.capstone.adapters.IndexesAdapter.mCursor
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * references android.content.ContentResolver$CursorWrapperInner.mCursor
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * references android.database.sqlite.SQLiteCursor.mDataSetObservable
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * references android.database.DataSetObservable.mObservers
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * references java.util.ArrayList.array
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * references array java.lang.Object[].[0]
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * references android.widget.CursorAdapter$MyDataSetObserver.this$0
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * references com.carlos.capstone.adapters.IndexesAdapter.mContext
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * leaks com.carlos.capstone.MainActivity instance
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * Reference Key: 08e1a3e5-78cd-4f09-8cc7-1c307201d763
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * Device: Genymotion generic Google Nexus 5 - 5.1.0 - API 22 - 1080x1920 vbox86p
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * Android Version: 5.1 API: 22 LeakCanary: 1.3.1
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * Durations: watch=6235ms, gc=114ms, heap dump=983ms, analysis=4339ms
02-06 12:22:41.195 7809-8716/com.carlos.capstone D/LeakCanary: * Details:
使用NonScrollListView类更新
public class NonScrollListView extends ListView {
public NonScrollListView(Context context) {
super(context);
}
public NonScrollListView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NonScrollListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int heightMeasureSpec_custom = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec_custom);
ViewGroup.LayoutParams params = getLayoutParams();
params.height = getMeasuredHeight();
}
}
更新2解决方案:
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mCursor=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);
}
}
以及以下内容:
public void onDestroy() {
if(mCursor!=null && !mCursor.isClosed()) {
mCursor.close();
}
super.onDestroy();
}
答案 0 :(得分:2)
我遇到过类似的情况但不是Loader的情况。这种泄漏背后的主要原因是Cursor在方向变化时仍处于打开状态。在您的代码中,我可以看到您在查询方法上返回游标的相同原因。尝试在方向更改时实现cursor.close(),并在方向更改完成后重新查询。