This article is a series of articles, I am in the long-distance development of Android a little thoughts and records, I will try to follow the first easy after the difficult sequence to write the series. The series cited the "Android Development art exploration" and "in-depth understanding of Android volume Ⅰ,ⅱ,ⅲ" in the relevant knowledge, in addition to learn from other high-quality blog, here to the great God to thank you, worship!!! In addition, this article series of knowledge may require a certain Android Development Foundation and project experience to better understand, that is, the series of articles for Android Advanced Development engineer.
Objective
In the previous article we have mainly an example to bring the reader into the door of the custom viewgroup, just bring into the door, custom view of the content and many, I later encountered some good custom view must also come here to share. In this article we will analyze the memory leaks and how to resolve them during the app run.
Memory leak concept and its impact
memory leaks in layman's words, an object that is supposed to be recycled is not recycled for some reason. we all know that the object is life cycle, from birth to death, when the object's task is completed, the Android system for garbage collection. We know that the Android system allocates a limited amount of memory for an app (which may vary depending on the model), and when a memory leak occurs in an application, it inevitably causes the application to exceed the memory limit allocated by the system, eventually causing Oom (OutOfMemory ) causes the program to crash.
Introduction to the Memory leak checker tool
We knew about the mat performance tool when I was using eclipse, and of course we can check for memory leaks using the mat, but with a little bit of trouble, I'm going to introduce another tool here, and we've abandoned Eclipse and embraced Android Studio. This tool is called leakcanary. Why use this tool, of course, because of its simple, fool-like operation. This tool is open source in GitHub, is produced by the Square company, not a word, square produced must be a boutique, https://github.com/square/leakcanary we can easily quote it
In your build.gradle:
dependencies { debugCompile ‘com.squareup.leakcanary:leakcanary-android:1.5.4‘ releaseCompile ‘com.squareup.leakcanary:leakcanary-android-no-op:1.5.4‘ }
In your application class:
public class ExampleApplication extends Application { @Override public void onCreate() { super.onCreate(); if (LeakCanary.isInAnalyzerProcess(this)) { // This process is dedicated to LeakCanary for heap analysis. // You should not init your app in this process. return; } LeakCanary.install(this); // Normal app init code... }}
It's so simple, so let's use it to combine the following memory leak scenario application.
Common memory leaks
In our usual development may have caused a memory leak and do not know, the following list of some of them, see if your program has such code.
Memory leaks due to static variables
public class MainActivity extends Activity{ private static final String TAG = "MainActivity"; private static Context sContext; private static View sView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //这里直接把当前Activity赋值给了静态变量sContext sContext = this; //这种写法和上面的类似 sView = new View(this); }}
The above method estimates that primary school students are aware of a memory leak, because when a Mainactivity object completes a task that needs to be recycled, there is a static variable referencing it (the static variable has the same life cycle as application), causing a memory leak. We use leakcanary analysis as
When a memory leak occurs in our app, a notification is displayed in the notification bar, click the notification to get the details of the memory leak, or click on the leaks icon to get all the memory leaks during the app run, as shown in the example above
A memory leak caused by a singleton mode
The above memory leak is too obvious, it is estimated that people will not write like this, but the singleton mode is different, we tend to ignore the error in the use of a singleton mode of leakage. For example, we often use in the development of the DP-to-PX,PX DP, etc. will be encapsulated into a singleton class. As follows
public class DisplayUtils { private static volatile DisplayUtils instance = null; private Context mContext; private DisplayUtils(Context context){ this.mContext = context; } public static DisplayUtils getInstance(Context context){ if (instance != null){ synchronized (DisplayUtils.class){ if (instance !=null){ instance = new DisplayUtils(context); } } } return instance; } public int dip2px(float dpValue) { final float scale = mContext.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); }}
And then we go to call it
public class SingleActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_single); //这里我们把当前SingleActivity传入 DisplayUtils.getInstance(this).dip2px(5); }}
So the memory leaks are generated, and we can look at the graphs.
This diagram is similar to the memory leak diagram above. However, we often ignore this memory leak because we do not directly use static variables to point to the passed in parameters, the solution to ensure that the context and application life cycle, the modified code as follows:
public class DisplayUtils { private static volatile DisplayUtils instance = null; private Context mContext; private DisplayUtils(Context context){ //这里变化了,把当前Context指向个应用程序的Context this.mContext = context.getApplicationContext(); } public static DisplayUtils getInstance(Context context){ if (instance != null){ synchronized (DisplayUtils.class){ if (instance !=null){ instance = new DisplayUtils(context); } } } return instance; } public int dip2px(float dpValue) { final float scale = mContext.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); }}
Non-static inner class creates a memory leak caused by a static instance
We basically can't avoid using the ListView or Recyclerview in the program, talking about the classes shown in these lists, then our adapter is basically indispensable, We use Viewholder when optimizing the adapter of the ListView (Recyclerview itself has been optimized), and we use a static inner class for the use of Viewholder. So why would you suggest this? That's what we're going to talk about here. non-static internal classes Create a static instance of a memory leak that could be caused
public class NonStaticActivity extends AppCompatActivity { private static Config sConfig; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_non_static); //Config类并不是静态类, sConfig = new Config(); } class Config { }}
The reason for the memory leak is that the inner class implicitly holds a reference to the outer class, where the outer class is nonstaticactivity, but the inner class sconfig is also the static variable whose life cycle is the same as the application life cycle. Therefore, when the nonstaticactivity is closed, the internal class static instance still holds a reference to the nonstaticactivity, which causes the nonstaticactivity to be released from the collection, causing a memory leak.
The solution is to decouple the inner class life into a static inner class that is decoupled from the outer class. , which is why the use of Viewholder is recommended for use with static internal classes .
Memory leaks caused by webview
For memory leaks caused by WebView using Android. I suggest using Https://github.com/delight-im/Android-AdvancedWebView, using this optimized webview, follow the instructions.
Memory leaks caused by handler
I used handler in my project, and Mhandler implicitly holds an external class object reference here is Mainactivity, when the Postdelayed method is executed, the method loads your handler into a message, And put this message into MessageQueue, MessageQueue is in a looper thread constantly polling processing messages, then when the activity exits the message queue has an unhandled message or is processing a message, Messages in Message Queuing hold a reference to the Mhandler instance, Mhandler also holds the activity reference, so that the activity's memory resources cannot be reclaimed in a timely manner, causing a memory leak.
public class HandlerActivity extends AppCompatActivity { private Handler mHandler = new Handler(); private TextView mTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler); mTextView = (TextView) findViewById(R.id.text);//模拟内存泄露 mHandler.postDelayed(new Runnable() { @Override public void run() { mTextView.setText("test"); } }, 5 * 60 * 1000); }}
With Leakcanary you can see similar
The workaround is to remove all messages and all runnable from the message queue in the Handleractivity OnDestroy.
@Overrideprotected void onDestroy() { super.onDestroy(); mHandler.removeCallbacksAndMessages(null); mHandler = null;}
Memory leaks due to other causes
There are a number of reasons for memory leaks, and here we just list some of the more typical, of course, there are many reasons for memory leaks, such as resource open but not closed, multithreading and so on. But we have leakcanary this weapon, huh.
This article summarizes
This article only slightly introduces the next leakcanary and several common memory leaks, memory leaks, and memory performance optimizations are a lasting process. I'm just here to introduce you to one of these methods. Programming is endless, performance optimization is also.
Next trailer
OK, let's next introduce the message mechanism of Android Looper, Handler, Messagequeue,message
Sincerely, salute.
The long-distance development of Android--memory leak analysis and solution