前言

写这篇文章不是为了剖析Handler怎么应用,目标是想从设计的角度来看Handler的演进过程,以及为什么会呈现Looper,MessageQueue,Handler,Message这四个类。

一.线程通信的实质?

线程区别于过程的次要因素在于,线程之间是共享内存的。在android零碎中,堆中的对象能够被所有线程拜访。因而无论是哪种线程通信形式,思考到性能问题,肯定会选用持有对方线程的某个对象来实现通信。

1.1 AsyncTask

public AsyncTask(@Nullable Looper callbackLooper) {        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()            ? getMainHandler()            : new Handler(callbackLooper);        mWorker = new WorkerRunnable<Params, Result>() {            public Result call() throws Exception {                mTaskInvoked.set(true);                Result result = null;                try {                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);                    //noinspection unchecked                    result = doInBackground(mParams);                    Binder.flushPendingCommands();                } catch (Throwable tr) {                    mCancelled.set(true);                    throw tr;                } finally {                    postResult(result);                }                return result;            }        };        mFuture = new FutureTask<Result>(mWorker) {            @Override            protected void done() {                try {                    postResultIfNotInvoked(get());                } catch (InterruptedException e) {                    android.util.Log.w(LOG_TAG, e);                } catch (ExecutionException e) {                    throw new RuntimeException("An error occurred while executing doInBackground()",                            e.getCause());                } catch (CancellationException e) {                    postResultIfNotInvoked(null);                }            }        };    }private Result postResult(Result result) {        @SuppressWarnings("unchecked")        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,                new AsyncTaskResult<Result>(this, result));        message.sendToTarget();        return result;    }

从用法能够看出,AsyncTask也是间接通过handler机制实现从以后线程给Looper所对应线程发送音讯的,如果不传,默认选的就是主线程的Looper。

1.2 Handler

借助ThreadLocal获取thread的Looper,传输message进行通信。实质上也是持有对象线程的Looper对象。

public Handler(@Nullable 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;    }public final boolean post(@NonNull Runnable r) {       return  sendMessageDelayed(getPostMessage(r), 0);    }      public boolean sendMessageAtTime(@NonNull 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);    }  

1.3 View.post(Runnable)

public boolean post(Runnable action) {    final AttachInfo attachInfo = mAttachInfo;    if (attachInfo != null) {        return attachInfo.mHandler.post(action);    }    // Postpone the runnable until we know on which thread it needs to run.    // Assume that the runnable will be successfully placed after attach.    getRunQueue().post(action);    return true;}

getRunQueue().post(action)仅仅是在没有attachToWindow之前缓存了Runnable到数组中

private HandlerAction[] mActions;public void postDelayed(Runnable action, long delayMillis) {        final HandlerAction handlerAction = new HandlerAction(action, delayMillis);        synchronized (this) {            if (mActions == null) {                mActions = new HandlerAction[4];            }            mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);            mCount++;        }    }

等到attachToWindow时执行,因而实质上也是handler机制进行通信。

void dispatchAttachedToWindow(AttachInfo info, int visibility) {        mAttachInfo = info;        ....        // Transfer all pending runnables.        if (mRunQueue != null) {            mRunQueue.executeActions(info.mHandler);            mRunQueue = null;        }              ....         

1.4 runOnUiThread

public final void runOnUiThread(Runnable action) {        if (Thread.currentThread() != mUiThread) {            mHandler.post(action);        } else {            action.run();        }    }

通过获取UIThread的handler来通信。

从以上剖析能够看出,android零碎的四种常见通信形式实质上都是通过Handler技术进行通信。

二.handler解决什么问题?

handler解决线程通信问题,以及线程切换问题。实质上还是共享内存,通过持有其余线程的Looper来发送音讯。

咱们常提的Handler技术通常包含以下四局部

  • Handler
  • Looper
  • MessageQueue
  • Message

三.从架构的演进来看Handler

3.1 原始的线程通信

String msg = "hello world";Thread thread = new Thread(){    @Override    public void run() {        super.run();        System.out.println(msg);    }};thread.start();Thread thread1 = new Thread(){    @Override    public void run() {        super.run();        System.out.println(msg);    }};thread1.start();

3.2 结构化数据反对

为了发送结构化数据,因而设计了Message

Message msg = new Message();Thread thread = new Thread(){    @Override    public void run() {        super.run();        msg.content = "hello";        System.out.println(msg);    }};thread.start();Thread thread1 = new Thread(){    @Override    public void run() {        super.run();        System.out.println(msg);    }};thread1.start();

3.3 继续通信反对

Message msg = new Message();Thread thread = new Thread(){    @Override    public void run() {        for (;;){            msg.content = "hello";        }    }};thread.start();Thread thread1 = new Thread(){    @Override    public void run() {        super.run();        for (;;){            System.out.println(msg.content);        }    }};thread1.start();

通过有限for循环阻塞线程,Handler中对应的是Looper。

3.4 线程切换反对

上述办法都只能是thread1承受扭转,而无奈告诉thread。因而设计了Handler, 同时封装了发送和承受音讯的办法.

class Message{    String content = "123";    String from = "hch";}abstract class Handler{    public void sendMessage(Message message){        handleMessage(message);    }    public abstract void handleMessage(Message message);}Message msg = new Message();Thread thread = new Thread(){    @Override    public void run() {        for (;;){            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                e.printStackTrace();            }            msg.content = "hello";            if (handler != null){                handler.sendMessage(msg);            }        }    }};thread.start();Thread thread1 = new Thread(){    @Override    public void run() {        super.run();        handler = new Handler(){            @Override            public void handleMessage(Message message) {                System.out.println(message.content);            }        };    }};thread1.start();

3.5 对于线程音讯吞吐量的反对

abstract class Handler{    BlockingDeque<Message> messageQueue = new LinkedBlockingDeque<>();    public void sendMessage(Message message){        messageQueue.add(message);    }    public abstract void handleMessage(Message message);}...Thread thread1 = new Thread(){    @Override    public void run() {        super.run();        handler = new Handler(){            @Override            public void handleMessage(Message message) {                if (!handler.messageQueue.isEmpty()){                    System.out.println(messageQueue.pollFirst().content);                }            }        };    }};thread1.start();

减少音讯队列MessageQueue来缓存音讯,解决线程按程序生产。造成典型的生产者消费者模型。

3.6 对于多线程的反对

上述模型最大的不便之后在于Handler的申明和应用,通信线程单方必须可能十分不便的获取到雷同的Handler。

同时思考到应用线程的便利性,咱们不能限度Handler在某个固定的中央申明。如果可能十分不便的获取到对应线程的音讯队列,而后往里面塞咱们的音讯,那该如许美妙。

因而Looper和ThreadLocal闪亮退场。

  • Looper形象了有限循环的过程,并且将MessageQueue从Handler中移到Looper中。
  • ThreadLocal将每个线程通过ThreadLocalMap将Looper与Thread绑定,保障可能通过任意Thread获取到对应的Looper对象,进而获取到Thread所需的要害MessageQueue.

//ThreadLocal获取Looperpublic T get() {    Thread t = Thread.currentThread();    ThreadLocalMap map = getMap(t);    if (map != null) {        ThreadLocalMap.Entry e = map.getEntry(this);        if (e != null) {            @SuppressWarnings("unchecked")            T result = (T)e.value;            return result;        }    }    return setInitialValue();}//Looper写入到ThreadLocalprivate 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));}// 队列形象private Looper(boolean quitAllowed) {    mQueue = new MessageQueue(quitAllowed);    mThread = Thread.currentThread();}//Handler获取Looperpublic Handler(@Nullable 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;}

3.7 google对于Handler的无奈斗争

思考一个问题,因为Handler能够在任意地位定义,sendMessage到对应的线程能够通过线程对应的Looper--MessageQueue来执行,那handleMessage的时候,如何能找到对应的Handler来解决呢?咱们可没有好的方法能间接检索到每个音讯对应的Handler

两种解决思路

  • 通过公共总线,比方定义Map<Message,Handler>来索引,这种形式要求map必须定义到所有的线程都能不便获取到的中央,比方能够定义为static
  • 通过音讯带Message来携带属性target到对应线程,当音讯被生产后,能够通过Message来取得Handler.

第一种形式的问题比拟显著,公共总线须要手动保护它的生命周期,google采纳的是第二种形式。

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

3.8.斗争造成Handler泄露问题的本源

因为Message持有了Handler的援用,当咱们通过外部类的模式定义Handler时,持有链为

Thread->MessageQueue->Message->Handler->Activity/Fragment

长生命周期的Thread持有了短生命周期的Activity.

解决形式: 应用动态外部类定义Handler,动态外部类不持有外部类的援用,所以应用动态的handler不会导致activity的泄露。

四.总结

  • 1.线程通信实质上通过共享内存来实现
  • 2.android零碎罕用的四种通信形式,理论都采纳Handler实现
  • 3.Handler机制蕴含四局部Handler,MessageQueue,Message,Looper,它是架构演进的后果。
  • 4.Handler泄露实质是因为长生命周期的对象Thead间接持有了短生命周期的对象造成。

    #### 相干视频举荐:

【2021最新版】Android studio装置教程+Android(安卓)零基础教程视频(适宜Android 0根底,Android初学入门)含音视频_哔哩哔哩_bilibili

【 Android进阶教程】——Framework面试必问的Handler源码解析_哔哩哔哩_bilibili

Android进阶零碎学习——Gradle入门与我的项目实战_哔哩哔哩_bilibili

Android架构设计原理与实战——Jetpack联合MVP组合利用开发一个优良的APP!_哔哩哔哩_bilibili

本文转自 https://juejin.cn/post/7045473726929829918,如有侵权,请分割删除。