Android Handler整体梳理以及热点问题解析

1.Handler整体概括

在Android开发中会经常在子线程中进行一些操作,当操作完成后会通过Handler发送一些数据到主线程,通知主线程做相应的操作,Handler原理:子线程 Handler 主线程 其实构成了线程模型中的经典问题 生产者-消费者 模型。 生产者-消费者模型:生产者和消费者在同一时间段内用同一个存储空间,生产者往存储空间添加数据,消费者从存储空间中取走数据。

handler

 

优点:保证数据生产消费的顺序(通过MessageQueue先进先出)-不管是生产者(子线程)还是消费者(主线程)都只依赖缓冲区(Handler),生产者和消费者之间不会相互持有,使他们之间没有任何耦合。

1.1Handler机制相关类

Handler:发消息和收消息

Looper:用于轮寻消息队列,一个线程只能有一个Looper

Message : 消息实体

MessageQueue : 消息队列用于存储信息和管理消息

ThreadLocal : 为每个线程提供单独的存储空间

ThreadLocalMap: ThreadLocal的内部类,set方法控制 对于每一个ThreadLocal只有一个value(looper)

Thread: 线程中持有一个threadLocals变量(ThreadLocal.ThreadLocalMap)

类图

类图

1.2创建Looper

子线程:子线程中如果需要自己创建Looper调用Looper.prepare()方法,然后调用Looper.loop()方法。

主线程: 主线程ActivityThread中的main方法已经为我们创建了Looper

public static void main(String[] args) {
    //省略其它代码.......
    Looper.prepareMainLooper(); //初始化Looper以及MessageQueue
    ActivityThread thread = new ActivityThread();
    thread.attach(false);
    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }
    Looper.loop(); //开始轮寻
    throw new RuntimeException("Main thread loop unexpectedly exited");
}

Looper.prepareMainLooper()

public static void prepareMainLooper() {
    prepare(false); // 消息队列不允许quit,并创建Main Looper
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

prepare(boolean quitAllowed)

private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {//不为null表示当前线程已经创建了Looper
        throw new RuntimeException("Only one Looper may be created per thread");
        //每个线程只能创建一个Looper
    }
    sThreadLocal.set(new Looper(quitAllowed)); //将looper存储到ThreadLocal中,这样get的时候就不会为null,并且MessageQueue不允许quit
}

1.3创建MessageQueue以及Looper与当前线程绑定

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed); //创建MessageQueue
    mThread = Thread.currentThread(); //当前线程绑定
}

MessageQueue的构造方法


MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed; //mQuitAllowed决定了队列是否可以销毁,主线程的队列不可以被销毁需要传入false,在MessageQueue的quit方法中判断的
    mPtr = nativeInit();
}

Looper.loop() 轮寻

public static void loop() {
    final Looper me = myLooper(); //通过sThreadLocal.get()方法获取ThreadLocal中保存的Looper对象
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue; //把Looper对象中的消息队列取出

    // 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();

    for (;;) {
        //死循环,不断的去消息队列中读取消息
        Message msg = queue.next(); // might block
        //刚创建的MessageQueue中是没有消息的,等到Handler sendMessage enqueueMessage后队列里才有消息。
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
            //return之后会执行ActivityThread中main的 throw new RuntimeException(“Main thread loop unexpectedly exited”)
            //接着整个主线程就会退出了。所以就是说,在主线程中当loop()方法没有轮询到消息的时候。主线程就直接退出了
            //所以在这里我们知道了Looper.loop()消息循环,一旦退出消息循环,那么应用也就退出了。
            // 什么时候在进入呢??????
        }
        // This must be in a local variable, in case a UI event sets the logger
        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }
        msg.target.dispatchMessage(msg);// msg.target就是绑定的Handler,调用Handler的dispatchMessage去处理消息
        //后面代码省略......
    }
}

创建Handler

最常见的handler创建方式

Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
    }
};

创建方式2:在内部调用this(null, false)

public Handler(Callback callback, boolean async) {
    //前面代码省略......
    mLooper = Looper.myLooper(); //获取ThreadLocal中的looper对象,子线程中使用Handler需要调用Looper.prapare,或者使用getMainLooper来使用主线程looper
                                 //否则会抛出RunTimeException
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue; //获取消息队列
    mCallback = callback; // 初始化回调接口
    mAsynchronous = async;
}

创建Message

可以直接new Message,但是更好的方式是Message.obtain. 因为可以检查是否有可以复用的Message,避免过多的创建、销毁Message对象达到优化内存和性能的目的。

public static Message obtain(Handler h) {
    Message m = obtain(); //Message重载的obtain方法
    m.target = h; //Message的handler和我们的handler绑定
    return m;
}

obtain()

public static Message obtain() {
    synchronized (sPoolSync) { //同步锁,保证线程安全
        if (sPool != null) {
            Message m = sPool; //sPool就是Handler dispatchMessage后通过recycleUncheckd回收以复用的Message
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

Message和Handler的绑定

创建Message的时候可以通过Message.obtain(Handler h)这个构造方法进行绑定。当然在Handler中的enqueueMessage也进行了绑定,所有发送Message的方法都会调用此方法入队,所以在创建Message的时候可以不绑定

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this; //Message和Handler 进行绑定
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

Handler发送消息

Handler发送消息的重载方法有很多,常用的主要有两个。1.sendMessage(Message), 它通过一系列重载方法的调用,sendMessage调用sendMessageDelayed,继续调用sendMessageAtTime,继续调用enqueueMessage,继续调用messageQueue的enqueueMessage方法,将Msg保存到消息队列中,最终由Looper取出,交给Handler的dispatchMessage进行处理。

dispatchMessage方法中,message中的callback是一个Runnable对象,如果callback不为空,则直接调用callback的run方法,否则判断Handler自己的mCallback是否为null,mCallback在Handler构造方法中初始化,在主线程中直接通过无参构造方法new出来的为null,所以会直接执行后面的handleMessage()方法。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg); //Msg中的Runnable是否为null, callback在message的构造方法中初始化或者时候使用handler.post(Runnable)时才不为null
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {//mCallback是一个callback对象,通过无参构造函数创建出来的handler该属性为null,所以不执行
                return;
            }
        }
        handleMessage(msg); //最终执行这里
    }
}
private static void handleCallback(Message message) {
    message.callback.run();
}

Handler处理消息

最后调用handleMessage(Message)来处理消息,整个Handler机制的流程就结束了。

总结

Handler sendMessage发送消息到消息队列MessageQueue, 然后looper调用自己的loop方法轮寻MessageQueue里面的每一个Message。当Message达到了可以执行的时候就开始执行,执行后调用Message绑定的Hander来处理消息。

整体流程如下图

2.Handler中的难点问题

线程同步问题

Handler是用于线程之间通信的,但是它产生的根本不只是用于UI处理,而更多的是handler是真个app通信的框架,可以在ActivityThread里面感受到,整个app都是用它来进行线程见的协调。Handler是如何保证自己的线程安全的呢????

 

Handler机制里面最主要的类是MessageQueue,这个类是所有Message的存储仓库,消息的管理其实就两点:1.消息入库(enqueueMessage),2.消息出库(next)。所以只要在进出口确保线程安全就可以了。

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {  //msg必须有绑定的Handler,否则不允许入列
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) { //当前的msg是否已经有其它Handler在使用
        throw new IllegalStateException(msg + " This message is already in use.");
    }
    synchronized (this) { //添加同步锁,保证在同一时刻只有一个Msg插入到MessageQueue中
        if (mQuitting) { //当前的Thread时候已经被销毁
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w("MessageQueue", e.getMessage(), e);
            msg.recycle();
            return false;
        }
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // Inserted within the middle of the queue.  Usually we don't have to wake
            // up the event queue unless there is a barrier at the head of the queue
            // and the message is the earliest asynchronous message in the queue.
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;
                p = p.next;
                if (p == null || when < p.when) {
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; // invariant: p == prev.next
            prev.next = msg;
        }
        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    } //锁结束
    return true;
}

synchronized锁是一个内置锁,也就是由系统控制锁的lock unlock时机。

sychronized(this) 说明对所有调用MessageQueue对象的线程来说,他们都是互斥的,然而在我们Handler里面,一个线程是对应着一个唯一的Looper对象,然而Looper中又只有一个唯一的MessageQueue,所以我们主线程就只有一个MessageQueue对象,也就是说,所有的子线程向主线程发送消息的时候,主线程一次都只会处理一个消息,其它都需要等待,这样消息队列就不会出现混乱。

next函数

Message next() {
    //上面代码省略.......
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }

        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {  //读取消息时加锁,锁仍然是MessageQueue对象
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
            if (msg != null && msg.target == null) {
                // Stalled by a barrier.  Find the next asynchronous message in the queue.
                do {
                    prevMsg = msg;
                    msg = msg.next;
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
                if (now < msg.when) {
                    // Next message is not ready.  Set a timeout to wake up when it is ready.
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                    // Got a message.
                    mBlocked = false;
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;
                    }
                    msg.next = null;
                    if (false) Log.v("MessageQueue", "Returning message: " + msg);
                    return msg;
                }
            } else {
                // No more messages.
                nextPollTimeoutMillis = -1;
            }

            // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }

            // If first time idle, then get the number of idlers to run.
            // Idle handles only run if the queue is empty or if the first message
            // in the queue (possibly a barrier) is due to be handled in the future.
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run.  Loop and wait some more.
                mBlocked = true;
                continue;
            }

            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        } //锁结束
        //省略 后面代码........
}

enqueueMessage 和 next 函数,它们都使用了synchronized(this), 保证next函数和enqueueMessage函数能够实现互斥,保证多线程访问的时候messageQueue的有序进行

消息机制之同步屏障

同步屏障,view绘制中用 同步屏障

在Handler机制中,线程的消息都是放在同一个MessageQueue里面,取消息的时候是互斥取消息,而且只能从头部取消息,而添加消息是按照消息的执行的先后顺序进行排序的。这时候存在一个问题,同一个时间范围内的消息,如果它是需要立刻执行的,怎么处理?? 按照常规的方法,我们需要等待队列轮寻到这条消息的时候才能执行,那就不符合需求了。所以需要给这条消息开一个绿色通道,这个绿色通道就是同步屏障的概念。

同步屏障是什么?

屏障字面意思就是阻碍的意思,同步屏障就是阻碍同步消息,只让异步消息通过。

如何开启同步屏障?

 /**
     * @hide
     */
    public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }

    private int postSyncBarrier(long when) {
        // Enqueue a new sync barrier token.
        // We don't need to wake the queue because the purpose of a barrier is to stall it.
        synchronized (this) {
            final int token = mNextBarrierToken++;
            //在消息池获取msg
            final Message msg = Message.obtain();
            msg.markInUse();
            
            //就是这里!!!!! 初始化msg对象的时候,并没有给target赋值,因此target == null
            msg.when = when;
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    // 如果开启同步屏障的时间(假设为T)T不为0,且当前的同步消息里有时间小于T的,则prev也不为null
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) { // invariant: p == prev.next
                msg.next = p; //如果prev不为null,执行顺序就是prev -> msg -> p
                prev.next = msg;
            } else {
                msg.next = p; //如果prev为null,执行顺序就是msg-> p
                mMessages = msg;
            }
            return token;
        }
    }

可以看到,Message对象初始化的时候并没有给target赋值,因此 target == null的来源就找到了。一条target == null的消息就进入了消息队列

那么开启了同步屏障之后,所谓的异步消息又是如何被处理的呢??

Handler消息机制,消息最终处理是在消息轮寻器Looper#loop()中,而loop()循环中会调用MessageQueue#next从消息队列中取出消息。

Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        //如果nextPollTimeoutMillis=-1, 一直阻塞不会超时
        //如果nextPollTimeoutMillis=0, 不会阻塞,直接返回
        //如果nextPollTimeoutMillis>0, 最长阻塞nextPollTimeoutMillis毫秒(超时)
        // 如果期间有程序唤醒会立即返回
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            nativePollOnce(ptr, nextPollTimeoutMillis);
            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis(); //获取系统开机到现在的时间
                Message prevMsg = null;
                Message msg = mMessages; //当前链表的头节点
                //关键点!!!!!!!!!!!!!! 如果target == null,那么他就是屏障,需要循环遍历一直往后找到第一个异步消息
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    //如果有消息需要处理,先判断时间有没有到,如果没有到的话设置一下阻塞时间,场景如常用的postDelay
                    if (now < msg.when) {
                        // Next message is not ready.  Set a timeout to wake up when it is ready.
                        //计算出离执行时间还要多久,然后赋值给nextPollTimeoutMillis
                        //表示nativePollOnce方法要等待nextPollTimeoutMillils时长后返回
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // Got a message.
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

从上面就可以看出,当消息队列开启同步屏障的时候(即标识为msg.target == null),消息机制在处理消息的时候有限处理异步消息,这样同步屏障就起到了一种过滤和优先级的作用。

如上图所示,在消息队列中有同步消息和异步消息(黄色部分)以及一道墙---同步屏障(红色部分)。有了同步屏障的存在,msg_2和msg_M这两个异步消息就可以优先处理,而后面的msg_3等同步消息则不会被处理。那么这些同步消息什么时候被处理??? 就需要先移除这个同步屏障removeSyncBarrier

同步屏障应用场景

更新View时相关的消息即为异步消息,需要优先处理

void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); //开启同步屏障
            //发送异步消息
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

postCallback最终走到

private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {
        if (DEBUG_FRAMES) {
            Log.d(TAG, "PostCallback: type=" + callbackType
                    + ", action=" + action + ", token=" + token
                    + ", delayMillis=" + delayMillis);
        }
        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis();
            final long dueTime = now + delayMillis;
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

            if (dueTime <= now) {
                scheduleFrameLocked(now);
            } else {
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true); //设置为异步消息类型
                mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }

这样就开启了同步屏障,并且发送了异步消息,这样系统就会有限处理这些异步消息。

最后,在处理完成后需要移除同步屏障

void unscheduleTraversals() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); //移除同步屏障
            mChoreographer.removeCallbacks(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        }
    }

一个线程有几个Handler

一个线程可以有n个Handler,可以new很多个,没有限制。

一个线程可以有几个Looper

一个线程只允许有一个looper,在子线程中创建looper的方法Looper.prepare 创建的looper对象保存在当前线程的ThreadLocal的ThreadLocalMap中(threadlocal, looper)键值对的形式存储下来。

同一个线程再次调用prepare时会抛出异常,并且map中存储时对于key threadlocal只有唯一的一个looper与之对应

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

在java中非静态内部类默认可以访问外部类的所有变量和方法,因为非静态内部类持有了外部类的引用。还有匿名内部类

当我们在Activity中实现一个非静态Handler内部类,来处理一些延时事件,这种情况下就会出现内存泄漏的情况

当Activity销毁时,Handler还有延迟消息没有处理完,这时候因为Handler持有activity的引用,导致activity无法被回收,造成内存泄漏

 //匿名内部类    
    Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };
 
 //非静态内部类
    protected class AppHandler extends Handler {

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                // TODO: 2019/4/30  
            }
        }
    }

解决方法:

1.在activity销毁时,清空Handler中正在执行或未执行的Callback和Message

// 清空消息队列,移除对外部类的引用
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }
  
    //Handler源码中removeCallbacksAndMessages()注释含义
    /**
     * Remove any pending posts of callbacks and sent messages whose
     * <var>obj</var> is <var>token</var>.  If <var>token</var> is null,
     * all callbacks and messages will be removed.
     */
    public final void removeCallbacksAndMessages(Object token) {
        mQueue.removeCallbacksAndMessages(this, token);
    }

2.声明为静态内部类,静态内部类不会持有外部类的引用,所以不会影响GC时activity的回收,然后在静态内部类中手动持有activity的弱引用,这样gc的时候即使被持有也会被回收

private static class AppHandler extends Handler {
        //弱引用,在垃圾回收时,被回收
        WeakReference<Activity> activity;

        AppHandler(Activity activity){
            this.activity=new WeakReference<Activity>(activity);
        }

        public void handleMessage(Message message){
            switch (message.what){
                //todo
            }
        }
    }

3.即使内存泄漏了,在handler处理完后,下次GC时也会回收本次未被回收的内存

其它内部类中一般不会有延时的操作,所以没有遇到过内存泄漏的问题

主线程中可以直接new Handler, 子线程中如果想new Handler需要做哪些准备

1.因为主线程在ActivityThread的main函数中已经做了prepare,并且主线程的MessageQueue是不允许退出的

2.子线程中new 一个handler之前,需要Looper.prepare 这里面会创建一个looper对象,并将这个looper对象和当前子线程绑定

Looper.loop

Looper.quitSafely

子线程中处理完了消息就要调用quit 来跳出loop循环

Loop死循环为什么不会导致应用卡死

应用卡死也就是ANR的原因有两点:5s内没有响应输入的事件,比如按键或者屏幕触摸等,2.广播接收器处理超时 10s内没完成

首先我们需要知道,每一个应用都有一个属于自己的虚拟机,也就是说每个应用都有自己的Main函数,这个main函数就是ActivityThread.java中的main函数

为什么每个应用都有自己的main函数?

当我们在launcher界面开启一个app时,系统会用zygote给我们分配一个虚拟机,应用就运行在这个虚拟机上,之后执行的就是ActivityThread的main函数,在main函数中创建主线程的Looper对象

之后就Looper.loop一直轮寻整个主线程的消息

当MessageQueue中没有消息时,会阻塞在queue.next(),在next()函数中阻塞在nativePollOnce(ptr, nextPollTimeoutMillis)

这时不会导致ANR,这里就涉及到Linux pipe/epoll nativePollOnce()被阻塞时,主线程会释放CPU资源,进入休眠状态. 直到下个消息到达或者有事务发生,会通过pipe管道写入数据来唤醒主线程工作,就可以继续工作了.

这里主线程进入休眠状态和死循环是有区别的.

死循环是指主线程死锁在这里,一直执行某一块代码,无法再响应其他事件.

休眠状态是指在内核状态里,主线程被挂起,线程状态转移到休眠状

很多线程有很多Handler往MessagQueue添加数据,MessageQueue如何确保线程安全的

MessageQueue队列入队enqueueMessage和出队next都使用了同步锁 synchronized,并且锁对象是同一个 this也就是当前的MessageQueue对象

所以首先 入队和出队 不会同一时间发生,然后就是多个线程同时enqueueMessage时也不会出问题

使用Message时该如何创建它

使用Message的obtain方法,因为可以检查是否有可以复用的Message,避免过多的创建、销毁Message对象达到优化内存和性能的目的

并且obtain方法内也添加了同步锁,保证了线程安全

HandlerThread存在的意义

HandlerThread是Thread的子类,就是一个线程,只是它在自己的线程里面帮我们创建了Looper

优点:

1.使用方便,方便初始化,方便获取线程looper, 在HandlerThread的run方法中做了prepare, 获取looper对象,和loop()的操作,不需要自己操作

2.保证了线程安全

用法:

public class MainActivity extends AppCompatActivity {
    private HandlerThread myHandlerThread ;
    private Handler handler ;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //创建一个线程,线程名字:handler-thread
        myHandlerThread = new HandlerThread( "handler-thread") ; //这里就避免自己new thread并且获取looper对象更容易
        //开启一个线程
        myHandlerThread.start();
        //在这个线程中创建一个handler对象
        handler = new Handler( myHandlerThread.getLooper() ){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //这个方法是运行在 handler-thread 线程中的 ,可以执行耗时操作
                Log.d( "handler " , "消息: " + msg.what + "  线程: " + Thread.currentThread().getName()  ) ;
            }
        };
        //在主线程给handler发送消息
        handler.sendEmptyMessage( 1 ) ;
            new Thread(new Runnable() {
            @Override
            public void run() {
             //在子线程给handler发送数据
             handler.sendEmptyMessage( 2 ) ;
            }
        }).start() ;
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //释放资源
        myHandlerThread.quit() ;
    }
}

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值