详解Android Handler机制和Looper Handler Message关系

概述

我们就从以下六个问题来探讨Handler 机制和Looper、Handler、Message之前的关系?

1.一个线程有几个Handler?

2.一个线程有几个Looper?如何保证?

3.Handler内存泄漏原因?为什么其他的内部类没有说过这个问题?

4.为何主线程可以new Handler?如果在想要在子线程中new Handler 要做些什么准备?

5.子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?

6.Looper死循环为什么不会导致应用卡死?

一、源码解析

1.Looper

对于Looper主要是prepare()和loop()两个方法

首先看prepare()方法

private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

可以看出sThreadLocal是一个ThreadLocal对象,ThreadLocal 并不是线程,而是一个线程内部的存储类,可以在线程内存储数据.在第5行可以看到,将一个Looper实例放入了

ThreadLocal,并且在第2~4行判断了sThreadLocal是否为空,否则抛出异常.这也Looper.prepare()方法不能被调用两次.这也对应了上面的第二个问题.

下面来看Looper的构造方法:

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

在Looper的构造方法中创建了一个MessageQueue(消息队列)

然后我们在看loop()方法:

public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        // Allow overriding a threshold with a system prop. e.g.
        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
        final int thresholdOverride =
                SystemProperties.getInt("log.looper."
                        + Process.myUid() + "."
                        + Thread.currentThread().getName()
                        + ".slow", 0);

        boolean slowDeliveryDetected = false;

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long traceTag = me.mTraceTag;
            long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
            long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
            if (thresholdOverride > 0) {
                slowDispatchThresholdMs = thresholdOverride;
                slowDeliveryThresholdMs = thresholdOverride;
            }
            final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
            final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);

            final boolean needStartTime = logSlowDelivery || logSlowDispatch;
            final boolean needEndTime = logSlowDispatch;

            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }

            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
            final long dispatchEnd;
            try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (logSlowDelivery) {
                if (slowDeliveryDetected) {
                    if ((dispatchStart - msg.when) <= 10) {
                        Slog.w(TAG, "Drained");
                        slowDeliveryDetected = false;
                    }
                } else {
                    if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
                            msg)) {
                        // Once we write a slow delivery log, suppress until the queue drains.
                        slowDeliveryDetected = true;
                    }
                }
            }
            if (logSlowDispatch) {
                showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
            }

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }

第2行:final Looper me = myLooper();

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

第6行:拿到改Looper实例中的mQueue(消息队列)

第23~98行:进入了一个死循环,

第24行:Message msg = queue.next(); next()方法里会一直去取消息,然后会加锁,就会一直堵塞进程,这也就是我们经常说的Looper死循环为什么不会导致死机.在这next()源码就不粘贴了,后面会说这个为什么不会死机的问题.

第57行: 调用msg.target.dispatchMessage(msg); 把消息交给msg的target的dispatchMessage()方法去处理.msg的target是什么呢?其实就是handler对象,下面会分析.

第97行:释放消息占用的资源

Looper的主要作用:

与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也是只有一个MessageQueue.

loop()方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage()去处理.

2.Handler

使用Handler之前,我们都是初始化一个实例,比如用于更新UI线程,我们会在声明的时候直接初始化,或者在onCreate中初始化Handler实例.所以我们首先看Handler的构造方法,

看其如何与MessageQueue联系上的,它的子线程中发送的消息(一般发送的消息都是在非UI线程)怎么发送到MessageQueue中的.

public Handler(Callback callback) {
        this(callback, false);
    }

    public Handler(Callback callback, boolean async) {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

第15行:通过Looper.myLooper()获取了当前线程保存的Looper实例,然后在19行又获取了这个Looper实例中保存的MessageQueue(消息队列)

这样就保证了handler的实例与我们Looper实例中MessageQueue关联上了,

然后我们再看最常用的sendMessage方法:

public final boolean sendMessage(Message msg)
{
    return sendMessageDelayed(msg, 0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }

看到最后我们发现最后调用了sendMessageAtTime,在此方法内部有直接获取MessageQueue然后调用了enqueueMessage方法,我们再来看此方法:

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

enqueueMessage中首先为meg.target赋值为this, 在Looper的loop()方法会取出每个msg然后交给msg,target.dispatchMessage(msg)去处理消息,也就是把当前的Handler作为

msg的target属性,最终会调用queue的enqueueMessage的方法,也就是说Handler发出饿消息,最终会保存到消息队列中去.

现在已经很清楚了:Looper会调用Prepare()和loop()方法,在当前执行的线程中保存一个Looper实例,这个实例会保存一个MessageQueue对象,然后在当前的线程进入一个

无限循环中去,不断地从MessageQueue中读取Handler发来的消息.然后在回调创建这个消息的handler的dispatchMessage()方法.下面看一下dispathMessage方法:

public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

第10行: 调用了handleMessage()方法,下面我们看这个方法:

/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}

可以看到这个是一个空方法,为什么呢?因为消息的最终回调是由我们控制的,我们在创建handler的时候都是重写handleMessage方法,然后根据msg.what进行消息处理的

例如:

private Handler mHandler = new Handler()
    {
        public void handleMessage(android.os.Message msg)
        {
            switch (msg.what)
            {
            case value:

                break;

            default:
                break;
            }
        };
    };

整个流程已经说完了,总结一哈:

1.首先Looper,prepare()方法在本线程中保存了一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,

所以MessageQueue在一个线程中只会存在一个.

2.Looper.loop()会让当前的线程进入一个无限循环,不断地从MessageQueue的实例中读取消息,然后回调,msg.target.dispatchMessage(msg)方法.

3.Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例的MessageQueue相关联.

4.Handler的sendMessage()方法,会给msg的target赋值为handler自身,然后加入MessageQueue中.

5.在构造Handler实例时,我们会重写handlerMessage方法.也就是msg.target,dispatchMessage(msg)最终调用的方法.

回过头来来看我们的之前的六个问题:

二、分析问题

1.一个线程有几个Handler?

我相信大家应该都使用过Handler,所以这个问题的答案:多个

这个问题没有什么好分析的,大家也亲身使用过!

2.一个线程有几个Looper?如何保证?

一个线程能有多个Handler,那么会产生多少个Looper呢? 答案: 1个

为什么?如何保证呢?

在源码分析中,可以看到sTheadLocal会实例一个Looper,如果在同一个线程中再次调用Looper.prepare方法,会抛出异常:Only one Looper may be created per thread

说明了同一个线程只能实例Looper对象.

3.Handler内存泄漏原因?

为什么其他的内部类没有说过这个问题?

Handler内存泄漏原因? 答案: 内部类引用外部类方法

private Handler mHandler =new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 0:
                    setLog();
                    break;
                default:
                    break;
            }

        }
    };

 private void setLog() {
        Log.d(TAG,"This is Log!");
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.create_xml:
                Log.d(TAG,"create_xml");
                mHandler.sendMessageDelayed(0,1000*60);
                break;
             default:
                break;
    }

创建一个匿名内部类Handler, 这时候我发延迟sendMessageDelayed()执行setLog()方法,但这个时候我如果强行关闭Activity,这个时候Activity会被销毁,但是这个Handler得不到

释放,因为还要延迟一分钟才能执行setLog()方法,这个时候就会造成内存泄漏.

其他的内部类为什么不会?

很简单,比如ListView的ViewHolder这个常用的匿名内部类,如果当主Activity销毁,这个时候ViewHolder内部类,也是直接被销毁的!所以不会出现内存泄漏问题!

4.为何主线程可以new Handler?

如果在想要在子线程中new Handler 要做些什么准备?

由前面的讲解,可以看出new Handler的条件是需要一个Looper对象,而Looper对象需要调用两个方法prepare()和loop()方法,大家可以看下面主线程的Main方法

public static void main(String[] args) {
        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

        // Install selective syscall interception
        AndroidOs.install();

        // CloseGuard defaults to true and can be quite spammy.  We
        // disable it here, but selectively enable it later (via
        // StrictMode) on debug builds, but using DropBox, not logs.
        CloseGuard.setEnabled(false);

        Environment.initForCurrentUser();

        // Make sure TrustedCertificateStore looks in the right place for CA certificates
        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
        TrustedCertificateStore.setDefaultUserDirectory(configDir);

        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();

        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
        // It will be in the format "seq=114"
        long startSeq = 0;
        if (args != null) {
            for (int i = args.length - 1; i >= 0; --i) {
                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                    startSeq = Long.parseLong(
                            args[i].substring(PROC_START_SEQ_IDENT.length()));
                }
            }
        }
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

这个Main方法,是所有的程序启动之前,都要走的这个main方法

第20行:调用了一个Looper.prepareMainLooper();

第47行:调用了一个Looper.loop();

而Looper.prepareMainLooper()源码:

public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

第2行:可以看到调用了Looper里的prepare()方法;

所以说可以在一个主线程中直接new Handler

那如果在一个子线程new Handler的话,需要做什么准备?

当然是要需要:调用一个Looper.prepar()和Looper.loop()方法了。

5.子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?

在子线程使用Handler时,调用Looper.loop()方法,在上面的源码中,可以看到【Message msg = queue.next(); // might block】会一直卡死在这个地方?那我们怎么解决这个问题呢?

在Looper方法中有个QuitSafely()方法,这个方法会干掉MessageQueue(消息队列)中的所有消息而释放内存和释放线程。

这个时候回到第四个问题,在子线程中创建Handler,需要准备什么?

调用三个方法:

  • looper.prepare()
  • Looper.loop()
  • handler.getLooper().quit();

6.Looper死循环为什么不会导致应用卡死?

了解这个问题,首先我们要了解,什么情况下才会导致应用卡死?

卡死也就会会出现应用无响应,也就是我们常说的ANR,出现ANR问题有两种:

  • 在5秒内没有响应输入事件,如:按键按下,屏幕触摸
  • BroadcastReceiver在10秒内没有执行完毕

了解这个了我们就会发现,在导致Looper死循环的问题是Message msg = queue.next()这个方法,看了next()源码,简单的可以说这个程序是在睡眠,从而在next()方法中调用Wake()方法可以唤醒程序,从而不会导致应用出现ANR问题.

以上就是详解Android Handler机制和Looper Handler Message关系的详细内容,更多关于Android Handler机制和Looper Handler Message关系的资料请关注我们其它相关文章!

(0)

相关推荐

  • android的消息处理机制(图文+源码分析)—Looper/Handler/Message

    这篇文章写的非常好,深入浅出,关键还是一位大三学生自己剖析的心得.这是我喜欢此文的原因.下面请看正文: 作为一个大三的预备程序员,我学习android的一大乐趣是可以通过源码学习google大牛们的设计思想.android源码中包含了大量的设 计模式,除此以外,android sdk还精心为我们设计了各种helper类,对于和我一样渴望水平得到进阶的人来说,都太值得一读了.这不,前几天为了了解android的消息处理机 制,我看了Looper,Handler,Message这几个类的源码,结果又

  • 深入Android HandlerThread 使用及其源码完全解析

    关联篇:深入Android的消息机制源码详解-Handler,MessageQueue与Looper关系 关联篇:Handler内存泄漏及其解决方案 本篇我们将来给大家介绍HandlerThread这个类,以前我们在使用线程执行一个耗时任务时总会new一个Thread的线程去跑,当任务执行完后,线程就会自动被销毁掉,如果又由新的任务,我们又得新建线程.....我们假设这样的一个情景,我们通过listview去加载图文列表,当我们往下滑动时,这时需要不断去请求网络资源,也就是需要不断开线程去加载

  • Android Handler实现闪屏页倒计时代码

    我就废话不多说了,大家还是直接看代码吧~ package com.zjx.todayinfomation; import android.os.Handler; public class CustomCountDownTimer implements Runnable{ // 1.实时去回调 这个时候是什么时间 倒计时到几点 观察者设计模式 // 2.支持传入总时间 动态传入 // 3.每过一秒 总秒数 -1 // 4.总时间倒计时为0时候 回调完成状态 private int time; //

  • Android使用Handler实现打地鼠游戏

    本文实例为大家分享了Android使用Handler实现打地鼠的具体代码,供大家参考,具体内容如下 1.实现效果 如下图所示: 2.代码实现 新建一个名为DiglettDemo的项目,activity_main.xml代码如下: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/a

  • 深入Android Handler,MessageQueue与Looper关系

    关联篇:HandlerThread 使用及其源码完全解析 关联篇:Handler内存泄漏详解及其解决方案 一说到Android的消息机制,自然就会联想到Handler,我们知道Handler是Android消息机制的上层接口,因此我们在开发过程中也只需要和Handler交互即可,很多人认为Handler的作用就是更新UI,这也确实没错,但除了更新UI,Handler其实还有很多其他用途,比如我们需要在子线程进行耗时的I/O操作,可能是读取某些文件或者去访问网络等,当耗时操作完成后我们可能需要在U

  • Android Handler内存泄漏详解及其解决方案

    关联篇:深入Android的消息机制源码详解-Handler,MessageQueue与Looper关系 关联篇:HandlerThread 使用及其源码完全解析 在android开发过程中,我们可能会遇到过令人奔溃的OOM异常,面对这样的异常我们是既熟悉又深恶痛绝的,因为造成OOM的原因有很多种情况,如加载图片过大,某已不再使用的类未被GC及时回收等等......本篇我们就来分析其中一种造成OOM的场景,它就是罪恶的内存泄漏.对于这样的称呼,我们并不陌生,甚至屡次与之"并肩作战",

  • android利用handler实现打地鼠游戏

    本文实例为大家分享了android利用handler实现打地鼠游戏的具体代码,供大家参考,具体内容如下 xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"

  • 详解Android Handler机制和Looper Handler Message关系

    概述 我们就从以下六个问题来探讨Handler 机制和Looper.Handler.Message之前的关系? 1.一个线程有几个Handler? 2.一个线程有几个Looper?如何保证? 3.Handler内存泄漏原因?为什么其他的内部类没有说过这个问题? 4.为何主线程可以new Handler?如果在想要在子线程中new Handler 要做些什么准备? 5.子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用? 6.Looper死循环为什么不会导致应用卡死? 一.

  • 详解Android 消息处理机制

    摘要 Android应用程序是通过消息来驱动的,当Android主线程启动时就会在内部创建一个消息队列.然后进入一个无限循环中,轮询是否有新的消息需要处理.如果有新消息就处理新消息.如果没有消息,就进入阻塞状态,直到消息循环被唤醒. 那么在Android系统中,消息处理机制是怎么实现的呢?在程序开发时,我们经常会使用Handler处理Message(消息).所以可以知道Handler是个消息处理者,Message是消息主体.除此之外还有消息队列和消息轮询两个角色.它们分别是MessageQueu

  • 详解Android中Handler的内部实现原理

    本文主要是对Handler和消息循环的实现原理进行源码分析,如果不熟悉Handler可以参见博文<详解Android中Handler的使用方法>,里面对Android为何以引入Handler机制以及如何使用Handler做了讲解. 概括来说,Handler是Android中引入的一种让开发者参与处理线程中消息循环的机制.我们在使用Handler的时候与Message打交道最多,Message是Hanlder机制向开发人员暴露出来的相关类,可以通过Message类完成大部分操作Handler的功

  • 详解Android Handler的使用

    目录 Handler 概要 构造器 sendMessageAtTime dispatchMessage ThreadLocal Looper MessageQueue IdleHandler AsyncMessage和SyncBarrier 阻塞和唤醒机制 Handler内存泄漏分析 Handler 概要 Handler用于线程间的消息传递,它可以将一个线程中的任务切换到另一个线程执行.切换的目标线程与Handler内部持有的Looper所在线程一致.若初始化Handler时未手动设置Loope

  • 详解Android创建Handler的必备知识点

    目录 普通Handler创建 异步Handler的使用及创建 普通Handler创建 一般下面这样创建Handler时AS会进行告警: 删除线提示Handler的无参构造方法已经被@Deprecated标记,正确的做法都是使用带Looper构造参数的构造方法进行Handler的创建: 这样做的好处是直接指定消息调度在哪个Looper,而Looper是通过ThreadLocal和线程绑定在一起,也就是说,指明了Looper就能知道当前消息Message最终会在哪个线程中执行. 而之所以Handle

  • 详解Android中Handler的使用方法

    在Android开发中,我们经常会遇到这样一种情况:在UI界面上进行某项操作后要执行一段很耗时的代码,比如我们在界面上点击了一个"下载"按钮,那么我们需要执行网络请求,这是一个耗时操作,因为不知道什么时候才能完成.为了保证不影响UI线程,所以我们会创建一个新的线程去执行我们的耗时的代码.当我们的耗时操作完成时,我们需要更新UI界面以告知用户操作完成了.所以我们可能会写出如下的代码: package ispring.com.testhandler; import android.app.

  • 详解Android应用沙盒机制

    前言 Android使用沙盒来保护用户不受恶意应用的侵害,同时也将应用隔离开来,防止他们互相访问其数据,本文主要对Android应用沙盒中的几种技术做简要的总结. 一.Android应用DAC沙盒 稍微了解Android一点的人都知道,Android上的App并不像Linux上的用户程序那样,启动应用的uid默认就是登录用户的uid,除非你使用sudo或者setuid等机制.而是每个Android应用都对应了一个uid,也就是一个用户,通过Linux系统的DAC机制将应用的数据严格隔离开来. A

  • 详解Android中的ActivityThread和APP启动过程

    ActiviryThread ActivityThread的初始化 ActivityThread即Android的主线程,也就是UI线程,ActivityThread的main方法是一个APP的真正入口,MainLooper在它的main方法中被创建. //ActivityThread的main方法 public static void main(String[] args) { ... Looper.prepareMainLooper(); ActivityThread thread = ne

  • 详解Android内存泄露及优化方案一

    目录 一.常见的内存泄露应用场景? 1.单例的不恰当使用 2.静态变量导致内存泄露 3.非静态内部类导致内存泄露 4.未取消注册或回调导致内存泄露 5.定时器Timer 和 TimerTask 导致内存泄露 6.集合中的对象未清理造成内存泄露 7.资源未关闭或释放导致内存泄露 8.动画造成内存泄露 9.WebView 造成内存泄露 总结 一.常见的内存泄露应用场景? 1.单例的不恰当使用 单例是我们开发中最常见和使用最频繁的设计模式之一,所以如果使用不当就会导致内存泄露.因为单例的静态特性使得它

  • 详解Android内存泄露及优化方案

    目录 一.常见的内存泄露应用场景? 1.单例的不恰当使用 2.静态变量导致内存泄露 3.非静态内部类导致内存泄露 4.未取消注册或回调导致内存泄露 5.定时器Timer 和 TimerTask 导致内存泄露 6.集合中的对象未清理造成内存泄露 7.资源未关闭或释放导致内存泄露 8.动画造成内存泄露 9.WebView 造成内存泄露 总结 一.常见的内存泄露应用场景? 1.单例的不恰当使用 单例是我们开发中最常见和使用最频繁的设计模式之一,所以如果使用不当就会导致内存泄露.因为单例的静态特性使得它

随机推荐