面试官(简述下 Handler 机制的总体原理())

前言 写这篇文章不是为了分析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() { 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(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(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 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) { returnsendMessageDelayed(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();

【面试官(简述下 Handler 机制的总体原理())】通过无限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 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.
面试官(简述下 Handler 机制的总体原理())
文章图片

//ThreadLocal获取Looper public 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写入到ThreadLocal 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)); }// 队列抽象 private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }//Handler获取Looper public Handler(@Nullable Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class 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来索引,这种方式要求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,如有侵权,请联系删除。

    推荐阅读