一行代码导致我的应用程序中出现内存泄漏

时间:2016-02-06 17:47:10

标签: android memory-leaks

单行代码导致我的应用中出现内存泄漏。我调试了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();
}

1 个答案:

答案 0 :(得分:2)

我遇到过类似的情况但不是Loader的情况。这种泄漏背后的主要原因是Cursor在方向变化时仍处于打开状态。在您的代码中,我可以看到您在查询方法上返回游标的相同原因。尝试在方向更改时实现cursor.close(),并在方向更改完成后重新查询。