一行代码导致我的应用程序中的内存泄漏 [英] A line of code is causing memory leaks in my app

查看:77
本文介绍了一行代码导致我的应用程序中的内存泄漏的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

单行代码导致我的应用程序内存泄漏.我已经调试了200多次,但仍然找不到泄漏的原因.我已经将Content Provider实现与我拥有的其他实现进行了比较,并且代码是相同的.该应用程序运行良好,但当我旋转设备时,该应用程序LeakCanary会检测到泄漏.装载机似乎也构造良好.任何帮助将不胜感激.

A single line of code is causing memory leaks in my app. I've debugged more than 200 times but still cannot find the reason of the leak. I've compared the Content Provider implementation with other that i have and the code is the same. The app works well,except when i rotate the device the app LeakCanary detects a Leak. The Loaders seems to be well constructed also. Any help would be highly appreciated.

这是导致内存泄漏的代码行:(如果我注释掉这行代码,我的内存泄漏就会消失,显然我的列表中就没有数据了

This is the line of code causing the memory leak:(if I comment out this line of code my memory leaks disapperas, obviously my list is then not filled with data)

mAdapterIndexes.swapCursor(data);

这是我使用加载程序的片段:

This is the fragment where I use the 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);
    }
}

这是内容提供者中的查询方法:

This is the query methond in the Content Provider:

@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日志:

CanaryLeak log:

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类更新

UPDATED with NonScrollListView class

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:

UPDATED 2 WITH SOLUTION:

   @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();
}

推荐答案

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

I have been through similar situation but not in the case of Loader. The main reason behind this leak is Cursor is still open while orientation is getting changed. In your code I could see the same reason where you are returning a cursor on your query method. Try to implement cursor.close() on orientation change and re-query once the orientation change is complete.

这篇关于一行代码导致我的应用程序中的内存泄漏的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆