Android Handler架构思考

Posted 冬天的毛毛雨

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 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<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获取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<? 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(以上是关于Android Handler架构思考的主要内容,如果未能解决你的问题,请参考以下文章

金蝶handler中 collection 代码片段理解

关于Handler的理解,子线程不能更新UI的纠正和回调的思考

从架构师的角度分析Android Handler 源码的正确姿势

什么时候可以在android中使用强引用,这个代码是否泄漏?

[Android]View的Handler机制

Android---Handler体系