Common cases that cause memory problems

1. Use of Handler

    private val handler = Handler(Looper.getMainLooper(), object : Handler.Callback {
        override fun handleMessage(msg: Message): Boolean {
            // Message in Looper -> Handler -> Anonymous inner class -> Activity
            Log.d(TAG, "handleMessage")
            TAG = "Callback"
            return true; }})private val handler1 = object:Handler(Looper.getMainLooper()){
        override fun handleMessage(msg: Message) {
            TAG = "handler1"
            super.handleMessage(msg)
        }
    }
    
    inner class InnerHandler(context: Context?) : Handler(Looper.getMainLooper()) {
        override fun handleMessage(msg: Message) {
            TAG = "inner class"
        }
    }
    
    handler.postDelayed(Runnable {
        TAG = ""
    }, 200000)

    handler.sendEmptyMessageDelayed(0.200000)
Copy the code

In the Handler code android.os.handler #enqueueMessage clearly shows that message holds a reference to Handler. Because a thread has only one Looper, but can have multiple handlers, Message needs to hold Handler references to distribute to the specified Handler.

However, all three have references that cause the Handler to hold an external Activity.

The first is an anonymous inner class that implements the Handler.Callback interface

The second is that Object is an anonymous inner class that inherits from Handler

The third is InnerHandler, which is an inner class that inherits from a Handler

So as long as the message stays on the main thread, the Activity cannot be released, resulting in a memory leak.

Solutions:

// Static inner class Kotlin also wants to avoid frequent memory leaks caused by inner class holding, so by default a class represents a static inner class and does not hold external references
// Inner class is the inner class
class Handler2(context: Context?) : Handler(Looper.getMainLooper()) {
  private val weakReference: WeakReference<Context> = WeakReference(context)
    override fun handleMessage(msg: Message) {
    //TAG = "weakReference"
    weakReference.get()? .let { Log.e(""."")}}}// If Message can be ignored after exiting the interface, you can remove the Message
override fun onDestroy(a) {
  super.onDestroy()
    handler.removeCallbacksAndMessages(null)}Copy the code

2. Static/ singleton mode

Singleton.getInstance(MainActivity@this)

public class Singleton {
    private static Singleton mInstance;

    private Context context;

    private Singleton(Context context) {
        this.context = context;
    }

    public static Singleton getInstance(Context context){
        if(mInstance == null){
            mInstance = new Singleton(context);
        }
        returnmInstance; }}Copy the code

In general, static variables and classes are consistent with the life cycle of the ClassLoader and process. The appellate version causes the activity passed in the first call to getInstance to not be released, resulting in a memory leak. So the Static keyword modifies member variables by reference.

Solution:

  1. Try not to use context in a singleton constructor. If you must, you can pass in ApplicationContext because it is consistent with the lifecycle of the process. In most cases no interface changes will be involved; ApplicationContext will suffice.
  2. If you really must use the Activity context, you can use a soft reference.

3. Non-static anonymous inner classes

val thread = Thread(mRunnable)
thread.start()
  
private val mRunnable = object :Runnable {
  override fun run(a) {
    try {
      TAG = "mRunnable"
        Thread.sleep(200000);
    }catch (e: Exception){
      e.printStackTrace()
    }
  }
}
Copy the code

MRunnable is a non-static anonymous inner class instance that holds an external reference, similar to the handler example that caused the leak.

Solution:

JAVA set mRunnable to static.

The Kotlin compilation phase is optimized so that references are not held as long as external variables are not used.

4. Property animation

anim = ValueAnimator.ofFloat(0f.1f)
anim.duration = 30000

animator = ObjectAnimator.ofFloat(textview, "alpha".1f.0f.1f)
animator.duration = 500000
animator.start()
Copy the code

The main reason lies in the android. Animation. AnimationHandler# sAnimatorHandler is static, led to a memory leak.

The code can be followed in the following order:

android.animation.ValueAnimator#cancel
android.animation.ValueAnimator#endAnimation
android.animation.ValueAnimator#removeAnimationCallback
android.animation.ValueAnimator#getAnimationHandler
android.animation.AnimationHandler#getInstance
android.animation.AnimationHandler#sAnimatorHandler
Copy the code

Solution:

    override fun onDestroy(a) {
        super.onDestroy()
        anim.cancel()
        animator.cancel()
    }
Copy the code

5. The resource is not closed

Resources such as BraodcastReceiver, ContentObserver, File, Cursor, Stream, etc. are not unregistered and released in time

Solution:

Unregister in time. Resources are released in a timely manner. Some syntactic sugar, such as the try-with-Resource that Java 1.7 provides. And Kotlin FileReadWrite. Kt mostly implements kotlin.io.CloseableKt#use

6. Customize View onDraw every new object

@Override
protected void onDraw(Canvas canvas) {
		Paint paint = new Paint();
}
Copy the code

OnDraw is executed very frequently, and new objects in it cause frequent memory requisition and freeing. On the other hand, frequent GC can also cause stalling.

Solution:

private Paint mPaint;
 
public TestView(Context context) {
		super(context);
  	mPaint = new Paint();
}
Copy the code

7. All images are preloaded

 //mJustView is added to the window only after some models receive the broadcast, but it is directly loaded into memory at initialization.
private void makeJustView(a) {
        RelativeLayout root = (RelativeLayout) View.inflate(mContext, R.layout.eye_layout, null);
        mConfrim = (Button) root.findViewById(R.id.confirm);
        mConfrim.setOnClickListener(this);
        mJustView = root;
}
private void init(a) {
        makeJustView();
}
Copy the code

Solution:

// With lazy loading, only some models will load the image after receiving the broadcast. For other models, this part of memory is saved
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context conmTextView, Intent intent) { showProtectView(getJustView()); }}};private View getJustView(a){
		if(mJustView == null){
				makeJustView();
		}
		return mJustView;
}
private void init(a) {}Copy the code

8. Pictures in the wrong directory

If the image that should be placed in xHDPI is placed in the MDPI directory, when running on an XHDPI machine, the length and width will be doubled and the corresponding memory will be doubled.

The probability of a direct error is less, it’s more likely to happen in a map, the UI only provides a map or something.

9. The XML layout

Some views that may only be displayed once in the layout, such as bootstrap diagrams, can actually be used with the ViewStub. Instead of VISIBLE/GONE, reload as needed.

imageView.setVisibility(View.VISIBLE);
imageView.setVisibility(View.GONE);
Copy the code

The Bitmap is used

Mp.weixin.qq.com/s?__biz=MzA…

  • InSampleSize Sampling loads bitmap
BitmapFactory.Options options = new Options();
options.inSampleSize = 2;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resId, options);
Copy the code
  • Matrix small image enlargement, the memory is only the size of the sample, but the display effect is enlarged.

    Matrix matrix = new Matrix();
    matrix.postScale(2, 2, 0, 0);
    imageView.setImageMatrix(matrix);
    imageView.setScaleType(ScaleType.MATRIX);
    imageView.setImageBitmap(bitmap);
    Copy the code
  • The pixel format of the Bitmap

    format describe
    ALPHA_8 There is only one alpha channel
    ARGB_4444 This is not recommended starting with API 13 due to poor quality
    ARGB_8888 ARGB four channels, each channel 8bit, default.
    RGB_565 Each pixel accounts for 2 bytes, with red accounting for 5 bits, green accounting for 6 bits, and blue accounting for 5 bits. This is ideal for JPG resources when alpha is not required
  • Frame animations such as Loading animations // use custom views instead