Android common memory leaks and optimization

Brief introduction

If a useless objects (objects do not need to use) still holds references to other objects, causing the target system can not be recovered, so that the object can not be released in the heap memory unit occupied memory space caused by waste, which in the situation is the memory leak. In Android development, some bad programming habits will lead to the development of our app there is a memory leak. Here are some common summary of memory leaks in Android development scene and optimization.

Singleton memory leak

Singleton will be frequently used in Android development, but if used improperly can lead to memory leaks. Because the static characteristics of a single case makes its life cycle as long as the same application life cycle, if an object has been useless, but it also holds a singleton references, then the entire application life cycle it can not be normal recovery, leading to memory leaks.

public class AppSettings {
    private static AppSettings sInstance;
    private Context mContext;

    private AppSettings(Context context) {
        this.mContext = context;
    }

    public static AppSettings getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new AppSettings(context);
        }
        return sInstance;
    }
}

 Like the code above this single case, if you're calling getInstance (Context context) method when the parameters are passed in context Activity, Service and other contexts, will lead to memory leaks.
      Activity in order to, for example, when we start an Activity, and call getInstance (Context context) method to obtain a single case AppSettings, passing Activity.this as a context, such AppSettings class singleton sInstance to hold the Activity of reference, when when we exit the Activity, the Activity no use, but because sIntance as a static singleton (presence throughout the life cycle of the application) will continue to hold a reference to this Activity, leading to the release of Activity objects can not be recovered, which cause a memory leak.
To avoid this single case leads to memory leaks, we can change the context parameters of the global context:

    private AppSettings(Context context) {
        this.mContext = context.getApplicationContext();
    }

Global Context Application Context is the context of the application, and the life cycle of a single case as long, thus avoiding memory leaks. Singleton pattern corresponding to the life cycle of the application, we try to avoid using Activity context, but when used in the context of Application example of a single configuration.

Static variables resulting in memory leaks

Static variables are stored in the method area, its life-cycle load classes from beginning to end the whole process. Once the static variables are initialized, it holds a reference only until the end of the process will be released. For example, under such circumstances,
       in order to avoid duplication Activity in the creation of info, will sInfo as static variables:  

  public class MainActivity extends AppCompatActivity {
        private static Info sInfo;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            if (sInfo != null) {
                sInfo = new Info(this);
            }
        }
    }

    class Info {
        public Info(Activity activity) {
        }
    }

      Info Activity as a static member, and holds Activity references, but sInfo as a static variable, certainly longer than the life cycle of Activity. So when the Activity quit, sInfo still cited Activity, Activity can not be recycled, which leads to memory leaks.
     In Android development, may still hold a lot of time there because of inconsistent use of its life cycle and cause memory leaks, so when we create a new static variable holds references to the need to take account of the relationship between the individual members,
     and as far as possible less use of static variables held, to avoid memory leaks. Of course, we can also speak a static amount is reset to null, it no longer holds a reference to such memory leaks can be avoided at the appropriate time.


Non-static inner classes lead to memory leaks


Non-static inner classes (including anonymous inner classes) will hold a reference to an external default class, when the life cycle of a non-static inner class object is longer than the external object life cycle, it will lead to memory leaks. Non-static inner classes lead to memory leaks One typical scenario is to use Handler in Android development, many small partners in the use of Handler is written like this: 

public class MainActivity extends AppCompatActivity {
	      @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            start();
        }

	      private void start() {
            Message msg = Message.obtain();
            msg.what = 1;
            mHandler.sendMessage(msg);
        }

private Handler mHandler = new Handler() {
          @Override
            public void handleMessage(Message msg) {
          if (msg.what == 1) {
         //做 相 应 逻 辑
    }
      }
   };
    
}

   Some might say, mHandler not hold Activity cited as a static variable, may not be longer than the life cycle of Activity, it should not necessarily lead to memory leaks it is clearly not the case! Familiar Message Handler mechanism knows, mHandler will be saved in the message msg sent in as a member variable that holds a reference mHandler msg, and mHandler is non-static inner class Activity instance, that mHandler hold a reference Activity, then we Activity can be understood indirectly holds a reference to msg. msg after being sent into the message queue MessageQueue first, and then wait for the polling processing Looper (MessageQueue and
Looper are associated with the thread, and Looper MessageQueue reference member variables, and Looper is stored in the ThreadLocal). So when the Activity quit, msg may still be present in the news column MessageQueue being processed or unprocessed, then this will lead Activity can not be recovered, so that the memory leak occurs Activity. In general, if you want to use an inner class, but have to avoid memory leaks in Android development, usually static inner classes + mode weak references.  

 public class MainActivity extends AppCompatActivity {
        private Handler mHandler;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mHandler = new MyHandler(this);
            start();
        }

        private void start() {
            Message msg = Message.obtain();
            msg.what = 1;
            mHandler.sendMessage(msg);
        }

        private static class MyHandler extends Handler {
            private WeakReference<MainActivity> activityWeakReference;

            public MyHandler(MainActivity activity) {
                activityWeakReference = new WeakReference<>(activity);
            }

            @Override
            public void handleMessage(Message msg) {
                MainActivity activity=activityWeakReference.get();if(activity!=null){if(msg.what==1){
                // 做 相 应 逻 辑}}
            }
        }
    }

mHandler holds Activity by way of weak references, when GC garbage collection, recycling and encounter Activity will release the memory occupied by the unit. This does not happen a memory leak. The above approach is indeed to avoid memory leaks caused Activity, msg sent no longer have to hold the referenced Activity, but there may still msg message queue MessageQueue, the better it will be destroyed when mHandler Activity callbacks and messages sent out to remove.

  @Overrideprotected
    void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }

Non-static inner classes cause a memory leak Another case is the use of Thread or AsyncTask. For example, a new direct child thread Thread in the Activity:

 public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);new Thread(new Runnable(){@Override public void run(){
         //模 拟 相 应 耗 时 逻 辑 try{Thread.sleep(2000);}catch(InterruptedException e){e.printStackTrace();}}}).start();
        }
    }

New AsyncTask directly or asynchronous tasks:

public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);new AsyncTask<Void,Void,Void>(){@Override protected Void doInBackground(Void...params){
            //模 拟 相 应 耗 时 逻 辑 try{Thread.sleep(2000);}catch(InterruptedException e){e.printStackTrace();}return null;}}.execute();
        }
    }

Many beginners are so new threads and asynchronous tasks like the above, not knowing that such wording very unfriendly, this way the new child thread Thread AsyncTask anonymous inner class object, the default implicit reference to the holder of the external Activity, Activity lead to memory leaks. To avoid memory leaks, then still need to use the same way as above Handler static inner classes + weak application (code is not listed, and reference the correct wording of the above Hanlder).

Registration is not canceled or callback cause a memory leak

For example, we broadcast in Activity registration, if the registration is not canceled after the Activity destruction, then this will always exist just broadcast system, the same holds Activity cited the above mentioned non-static inner classes, leading to memory leaks. So after registering Activity broadcast after the destruction must be deregistered.    

public class MainActivity extends AppCompatActivity {
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            this.registerReceiver(mReceiver, new IntentFilter());
        }

private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
//接 收 到 广 播 需 要 做 的 逻 辑
}
};

        @Override
        protected void onDestroy() {
            super.onDestroy();
            this.unregisterReceiver(mReceiver);
        }
    }

At the time of registration of the observation mode, if not canceled can also cause memory leaks. Such as the use Retrofit + RxJava network registration request observer callback, the same holds external reference as an anonymous inner class, we need to remember to cancel the registration when not in use or destroyed.

 Timer and TimerTask lead to memory leaks

Timer and TimerTask in Android would normally be used to make some of the timing or cycle tasks, such as unlimited carousel ViewPager:  

  public class MainActivity extends AppCompatActivity {
        private ViewPager mViewPager;
        private PagerAdapter mAdapter;
        private Timer mTimer;
        private TimerTask mTimerTask;

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            init();
            mTimer.schedule(mTimerTask, 3000, 3000);
        }

        private void init() {
            mViewPager = (ViewPager) findViewById(R.id.view_pager);
            mAdapter = new ViewPagerAdapter();
            mViewPager.setAdapter(mAdapter);
            mTimer = new Timer();
            mTimerTask = new TimerTask() {
                @Override
                public void run() {
                    MainActivity.this.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            loopViewpager();
                        }
                    });
                }
            };
        }

        private void loopViewpager() {
            if (mAdapter.getCount() > 0) {
                int curPos = mViewPager.getCurrentItem();
                curPos = (++curPos) % mAdapter.getCount();
                mViewPager.setCurrentItem(curPos);
            }
        }

        private void stopLoopViewPager() {
            if (mTimer != null) {
                mTimer.cancel();
                mTimer.purge();
                mTimer = null;
            }
            if (mTimerTask != null) {
                mTimerTask.cancel();
                mTimerTask = null;
            }
        }

        @Override
        protected void onDestroy() {
            super.onDestroy();
            stopLoopViewPager();
        }
    }

When we Activity destroyed, it is possible Timer continues to wait for the implementation of TimerTask, it holds Activity of reference can not be recovered, so when we want to cancel out the Activity destroyed immediately Timer and TimerTask, to avoid a memory leak.

Objects in the collection not cause a memory leak cleanup

This is better understood, if an object is placed into the ArrayList, HashMap and other collections, this set will be holding the object. When we no longer need this object, it does not remove it from the collection, so long as the collection is still in use (but this object is already useless), this object will cause a memory leak. And if the set is still quoted, those objects inside a collection of no use but will cause a memory leak. So from the collection remove, or clear the set when using the set will not be timely object to avoid memory leaks.

Resources are not close or release a memory leak

When using IO, File stream or Sqlite, Cursor and other resources must be promptly closed. These resources during read and write operation typically use the buffer, if not timely closed, these buffer object would have been released and are not occupied, so that the memory leak occurs. So we do not need to promptly shut down when they are used, so that the buffer can be released in time to avoid a memory leak.

Property animation cause a memory leak

Animation is also a time-consuming tasks, such as starting a property animation (ObjectAnimator) in an Activity, but at the time of the destruction, there is no call to cancle method, although we can not see the movie, but the movie still will continue to play down, animation refer to the control resides, where the references Activity controls, which resulted Activity not release properly. Therefore, the same time to cancel out the Activity destroyed property animation, to avoid a memory leak.

  @Overrideprotected
    void onDestroy() {
        super.onDestroy();
        mAnimator.cancel();
    }

    WebView cause a memory leak

  About WebView leak memory, because WebView after loading the page will take up long-term memory can not be released, so we want to call it destory () method to destroy it to free up memory after the Activity destroyed. After Webview following references Callback held Activity, causing Webview memory can not be released, even called Webview.destory () and other methods can not solve the problem (Android5.1: In addition to checking to see this WebView memory leaks related information ).
The final solution is: WebView need to first be removed from the parent container before destroying the WebView, and then destroyed WebView. Detailed analysis, please refer to this article: ( (http://blog.csdn.net/xygy8860/article/details http://blog.csdn.net/xygy8860/article/details/53334476?utm_source=itdadao&utm_medium = Referral) / 53,334,476 ) [WebView memory leaks solution].

   @Overrideprotected
    void onDestroy() {
        super.onDestroy();
        //先 从 父 控 件 中 移 除 WebView mWebViewContainer.removeView(mWebView);mWebView.stopLoading();mWebView.getSettings().setJavaScriptEnabled(false);mWebView.clearHistory();mWebView.removeAllViews();mWebView.destroy();
}

to sum up

Memory leaks in Android memory optimization is a more important aspect, the program took place many times we may not be able to notice a memory leak, all in the process of encoding to develop good habits. Summary down as long as these points can be avoided in most cases less memory leaks:

  1. When referring to the configuration of a single embodiment Activity of try not to use;
  2. Note that static application object reference blanking or less static reference;
  3. + Class using static inner soft references instead of the non-static inner classes;
  4. Promptly cancel broadcast or registered observers;
  5. Time-consuming task, remember to cancel when a property animation destroyed Activity;
  6. File stream, Cursor and other resources in a timely manner is closed;
  7. WebView removal and destruction when the Activity is destroyed.
发布了40 篇原创文章 · 获赞 70 · 访问量 9万+

Guess you like

Origin blog.csdn.net/weixin_35959554/article/details/103873886