关于android:面试官简述下-Handler-机制的总体原理

1次阅读

共计 8423 个字符,预计需要花费 22 分钟才能阅读完成。

前言

写这篇文章不是为了剖析 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(@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,如有侵权,请分割删除。

正文完
 0