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

Android Handler整体梳理以及热点问题解析,第1张

概述1.Handler整体概括在Android开发中会经常在子线程中进行一些 *** 作,当 *** 作完成后会通过Handler发送一些数据到主线程,通知主线程做相应的 *** 作,Handler原理:子线程Handler主线程其实构成了线程模型中的经典问题生产者-消费者模型。生产者-消费者模型:生产者和消费者在同一时间段 1.Handler整体概括

在AndroID开发中会经常在子线程中进行一些 *** 作,当 *** 作完成后会通过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              }        }    }

@R_502_6120@:

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

 

 

总结

以上是内存溢出为你收集整理的Android Handler整体梳理以及热点问题解析全部内容,希望文章能够帮你解决Android Handler整体梳理以及热点问题解析所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://www.54852.com/web/1039856.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-24
下一篇2022-05-24

发表评论

登录后才能评论

评论列表(0条)

    保存