概述
概述 | |
---|---|
EventHandler | EventHandler是HarmonyOS用于处理线程间通信的一种机制,一种用户在当前线程上投递InnerEvent事件或者Runnable任务到异步线程上处理的机制,可以通过EventRunner创建新线程,将耗时的操作放到新线程上执行。 |
Handler | Handler是Android中通过线程中的消息队列发送和处理Message或Runnable对象的消息机制。 |
初看EventHandler,很容易联想到Eventbus和Handler,这里InnerEvent其实相当于Android中Handler的Message,所以两者的概述差别不大。
关于两者的定义,相关官方文档中也没有说得很清晰,实际中也不容易说清楚,因为两者的作用太过重要,而定义的存在感没那么高,开发者理解不同,对其定义也就不同,但消息机制的作用则不容忽视:
作用 | |
---|---|
EventHandler | 1. 在不同线程间分发和处理InnerEvent事件或Runnable任务。2.延迟处理InnerEvent事件或Runnable任务。 |
Handler | 1. 执行定时任务 。2.在不同线程中执行任务 |
两者的作用基本相同。当有耗时任务的时候,在主线程中创建子线程,在子线程中做耗时操作,当子线程任务执行完后将结果通过消息机制反馈到主线程中,主线程更新UI。来回切换线程是因为:
- 主线程中执行耗时操作会导致ANR(请求无响应)。
- 主线程属于不安全线程,如果不加以控制,多任务同时执行时安全问题将会暴露。
运行机制
EventHandler和Handler运行机制如上图所示:
- EventHandler实现线程间通信的主要流程:
首先EventHandler投递具体的InnerEvent事件或者Runnable任务到EventRunner所创建的线程的事件队列。然后EventRunner循环从事件队列中获取InnerEvent事件或者Runnable任务。最后处理事件或任务:如果EventRunner取出的事件为InnerEvent事件,则触发EventHandler的回调方法并触发EventHandler的处理方法,在新线程上处理该事件;如果EventRunner取出的事件为Runnable任务,则EventRunner直接在新线程上处理Runnable任务。
注意:在进行线程间通信的时候,EventHandler只能和EventRunner所创建的线程进行绑定,EventRunner创建时需要判断是否创建成功,只有确保获取的EventRunner实例非空时,才可以使用EventHandler绑定EventRunner。一个EventHandler只能同时与一个EventRunner绑定,一个EventRunner上可以创建多个EventHandler。 - Handler消息机制:
Handler创建完毕后,通过Handler的post方法将一个Runnable投递到Handler内部的Looper中去处理,也可以通过send方法发送一条消息,这条消息同样会在Looper中处理,其实post方法最终也是通过send方法完成的。当send方法被调用的时候,MessageQueue中的enqueueMessage方法也会被调用。enqueueMessage方法就是将接收到的消息放到消息队列中,然后Looper发现有新的消息到来,就会处理这个消息。最终消息中的Runnable或handleMessage方法就会被调用。
注意:Looper运行在Handler创建所在的线程中,所以Handler中的业务逻辑是在创建Handler的线程中执行的。Looper是是以无限循环的形式去查询是否有消息的,如果有消息就处理,如果没有就一直等到。MessageQueue称之为消息队列,实际上是消息单链表,方便插入和删除消息。
使用
如果开发者需要切换线程,在不同线程中通信,通常会使用EventHandler或Handler,具体使用如下:
EventHandler
- 首先创建EventHandler的子类,在子类中重写实现方法processEvent()来处理事件:
public class HMEventHandler extends EventHandler {
public HMEventHandler(EventRunner runner) throws IllegalArgumentException {
super(runner);
}
@Override
protected void processEvent(InnerEvent event) {
super.processEvent(event);
int id = event.eventId;
long param = event.param;
...
}
}
创建完子类后,在processEvent方法中通常根据event的id消息的来源,根据消息的来源或类型的不同做出相对应的处理。关于EventHandler的创建方式,源代码中关于构造只给出了一个方法:
public class EventHandler {
public EventHandler(EventRunner runner) throws IllegalArgumentException {
throw new RuntimeException("Stub!");
}
...
}
因此要使用EventHandler只有这一种方法,估计这样设计是为了便于消息和线程管理。new EventHandler的时候需要EventRunner对象,所以需要提前创建EventRunner对象:
EventRunner runner = EventRunner.create(false);
EventRunner.create方法中还需要传Boolean类型的值,因为EventRunner的工作模式可以分为托管模式和手动模式。false为手动模式:需要开发者自行调用EventRunner的run()方法和stop()方法来确保线程的启动和停止。true为托管模式:不需要开发者调用run()和stop()方法去启动和停止EventRunner。当EventRunner实例化时,系统调用run()来启动EventRunner;当EventRunner不被引用时,系统调用stop()来停止EventRunner,相当于自动模式。
创建完EventRunner,在使用前还是需要判空:
if (runner == null) {
return;jianzhuang
}
...
判空是因为创建EventRunner可能失败,如创建线程失败时,创建EventRunner失败。只要是Java,在使用对象前基本都需要判空,否则容易出现空指针异常等错误,这估计是Java最大的劣势之一,也成了判断程序健壮性和编写水平高低的标准之一。最终将runner传入,一个EventHandler对象才算创建完毕:
HMEventHandler mHandler = new HMEventHandler(runner);
如果感觉步骤繁琐,可以使用内部类。内部类可能导致程序混乱、重用率低、效率低、容易泄露等问题,根据个人习惯来吧。
- 创建消息
- 创建InnerEvent:
int eventId1 = 0;
long param = 0;
Object object = null;
InnerEvent event1 = InnerEvent.get(eventId1, param, object);
消息的创建方式有多种:
public final class InnerEvent implements Sequenceable {
...
public static InnerEvent get() {
throw new RuntimeException("Stub!");
}
public static InnerEvent copyFrom(InnerEvent oldInnerEvent) throws CloneNotSupportedException {
throw new RuntimeException("Stub!");
}
public static InnerEvent get(int eventId, long param, Object object) {
throw new RuntimeException("Stub!");
}
public static InnerEvent get(int eventId) {
throw new RuntimeException("Stub!");
}
...
}
从上述代码中不难发现创建的消息可以是空消息,复制的消息,只要有eventId的其他的可有可无的消息。具体消息体是什么样的却决于实际需要。
- 创建Runnable:
Runnable task1 = new Runnable() {
@Override
public void run() {
...// 任务
}
};
具体任务开发者自定义。
- 发送消息
- 发送InnerEvent:
mHandler.sendEvent(event1, 0, EventHandler.Priority.IMMEDIATE);
Priority是消息的优先级,优先级分为四种:
public static enum Priority {
IMMEDIATE,
HIGH,
LOW,
IDLE;
...
}
属性 | 描述 |
---|---|
Priority.IMMEDIATE | 立即投递 |
Priority.HIGH | 先于LOW优先级投递 |
Priority.LOW | 优于IDLE优先级投递,事件的默认优先级是LOW |
Priority.IDLE | 在没有其他事件的情况下,才投递该事件 |
发送InnerEvent的形式有多种:
public class EventHandler {
...
public void sendEvent(InnerEvent event, long delayTime, EventHandler.Priority priority) throws IllegalArgumentException {
throw new RuntimeException("Stub!");
}
...
public void sendEvent(int eventId) throws IllegalArgumentException {
throw new RuntimeException("Stub!");
}
...
public void sendEvent(int eventId, long delayTime, EventHandler.Priority priority) throws IllegalArgumentException {
throw new RuntimeException("Stub!");
}
...
}
根据上述代码可以看出消息的优先级, 是否延时,延时时长可灵活配置,实际使用视具体情况而定。
- 发送Runnable
mHandler.postTask(task1,0, EventHandler.Priority.IMMEDIATE);
同样postTask的形式也是多样的:
...
public void postTask(Runnable task, long delayTime, EventHandler.Priority priority) throws IllegalArgumentException {
throw new RuntimeException("Stub!");
}
public void postTask(Runnable task) throws IllegalArgumentException {
throw new RuntimeException("Stub!");
}
public void postTask(Runnable task, long delayTime) throws IllegalArgumentException {
throw new RuntimeException("Stub!");
}
public void postTask(Runnable task, EventHandler.Priority priority) throws IllegalArgumentException {
throw new RuntimeException("Stub!");
}
...
- 启动和停止(如果是托管模式,此步不需要)
runner.run(); // 启动EventRunner
...
runner.stop();// 停止EventRunner
除了启动和停止,EventHandler还有其他功能:
- 发送同步消息:
...
public void sendEvent(int eventId, long delayTime, EventHandler.Priority priority) throws IllegalArgumentException {
throw new RuntimeException("Stub!");
}
...
public void postSyncTask(Runnable task, EventHandler.Priority priority) throws IllegalArgumentException {
throw new RuntimeException("Stub!");
}
...
- 发送定时消息:
...
public void sendTimingEvent(InnerEvent event, long taskTime, EventHandler.Priority priority) throws IllegalArgumentException {
throw new RuntimeException("Stub!");
}
...
public void postTimingTask(Runnable task, long taskTime, EventHandler.Priority priority) throws IllegalArgumentException {
throw new RuntimeException("Stub!");
}
...
- 移除消息:
public void removeAllEvent() {
throw new RuntimeException("Stub!");
}
...
public void removeEvent(int eventId, long param, Object object) {
throw new RuntimeException("Stub!");
}
...
具体功能还有详细的划分,如果需要仔细了解,请查看EventHandler.class源码或参考HarmonyOS开发文档。
Handler
- 创建Handler对象。
...
@Deprecated
public Handler() {
this(null, false);
}
@Deprecated
public Handler(@Nullable Callback callback) {
this(callback, false);
}
public Handler(@NonNull Looper looper) {
this(looper, null, false);
}
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;
}
@UnsupportedAppUsage
public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
...
}
...
相对于EventHandler,Handler的构造方法比较丰富,创建方法也是多种多样。一般比较简单的使用方式是使用无参的构造方法:
private Handler mHandler = new Handler(){
{
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
if(msg.what == 1){
...
}
}
};
消息逻辑处理在handleMessage方法中进行。如果Handler的创建是在主线程中创建的,这样写是没有问题的,但是如果Handler的创建是在子线程中创建,这样写就会抛出"Cant`t create handler inside thread that has not called Looper.prepare()"异常,因为在主线程中默认实现了 Looper.prepare(),而子线程中没有,所以在子线程中使用要加上 Looper.prepare():
Looper.prepare();
Handler mHandler = new Handler();
Looper.loop();
只有这样才算是开启了消息循环机制,才能查看消息,处理消息,完成消息通讯。当然Looper中还有其他比较有用的方法,比如:
Looper.getMainLooper();
Looper.myLooper();
通过 Looper.getMainLooper()可以在任何地方获取主线程的Looper。通过Looper.myLooper()则获取当前线程的Looper。建议在不需要的时候终止Looper,有两种方案供选择:
Looper.getMainLooper().quit();
Looper.getMainLooper().quitSafely();
使用quit()是直接退出Looper,而quitSafely()是等当前的消息队列中的消息都处理完毕后才会安全退出。在子线程中如果创建了Looper,那么在所有消息都处理完后应该调用quit()终止消息循环,否则这个子线程会一直处于等待状态会造成消息阻塞,因为之前开启消息循环调用loop()是一个死循环,结束死循环的唯一方法就是MessageQueue的next方法(查看是否有新的消息返回)返回了null,当Looper退出后,Handler发送消息时调用send()会返回false,Handler发送消息就会失败,此时next()就会返回null,否在会一直阻塞在那里,具体代码如下:
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
if (me.mInLoop) {
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}
me.mInLoop = true;
final MessageQueue queue = me.mQueue;
// 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();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
boolean slowDeliveryDetected = false;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
// Make sure the observer won't change while processing a transaction.
final Observer observer = sObserver;
final long traceTag = me.mTraceTag;
long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;
if (thresholdOverride > 0) {
slowDispatchThresholdMs = thresholdOverride;
slowDeliveryThresholdMs = thresholdOverride;
}
final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);
final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);
final boolean needStartTime = logSlowDelivery || logSlowDispatch;
final boolean needEndTime = logSlowDispatch;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
Object token = null;
if (observer != null) {
token = observer.messageDispatchStarting();
}
long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
try {
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} catch (Exception exception) {
if (observer != null) {
observer.dispatchingThrewException(token, msg, exception);
}
throw exception;
} finally {
ThreadLocalWorkSource.restore(origWorkSource);
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logSlowDelivery) {
if (slowDeliveryDetected) {
if ((dispatchStart - msg.when) <= 10) {
Slog.w(TAG, "Drained");
slowDeliveryDetected = false;
}
} else {
if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",
msg)) {
// Once we write a slow delivery log, suppress until the queue drains.
slowDeliveryDetected = true;
}
}
}
if (logSlowDispatch) {
showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
当MessageQueue的next()返回新的消息,msg.target.dispatchMessage(msg)就会将消息切换到创建Handler时使用的Looper中去处理。关于MessageQueue的工作原理和源码就不解释了,虽然MessageQueue叫消息队列,但是实际上是通过单链表的数据结构来维护消息队列。
- 创建消息
- 创建Message:
Message message = new Message();
message.what = ...;
message.obj = ...;
Message长相如下:
public final class Message implements Parcelable {
...
public int what;
public int arg1;
public int arg2;
public Object obj;
public Messenger replyTo;
...
}
通常我们会使用what属性来标记消息的来源,what和InnerEvent的eventId基本是一个意思,都是识别消息,然后让开发者进行消息处理,如果一个what无法识别,可以再添加arg1、arg2帮助识别。obj就是我们需要传递的具体消息内容,如果没有内容,可以不传。Messenger就是可以跨进程的信使,详细使用这里就不解释了。
- 创建Runnable:
Runnable runnable = new Runnable() {
@Override
public void run() {
...
}
};
run()中一般是具体消息处理的实现。
- 发送消息
- Message:
mHandler.sendMessage(messag);
发送消息也有其他的形式:
public final boolean sendMessage(@NonNull Message msg) {
...
}
public final boolean sendEmptyMessage(int what)
{
...
}
...
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
...
}
public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) {
...
}
关于消息是否是空消息,是否延时,是否定时,是否先执行,Handler中都给出了选择,开发者可根据实际需要自由组合。
- Runnable:
mHandler.post(runnable);
发送runnable形式也不止一种:
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r), uptimeMillis);
}
public final boolean postAtTime(
@NonNull Runnable r, @Nullable Object token, long uptimeMillis) {
return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);
}
...
}
public final boolean postDelayed(
@NonNull Runnable r, @Nullable Object token, long delayMillis) {
return sendMessageDelayed(getPostMessage(r, token), delayMillis);
}
public final boolean postAtFrontOfQueue(@NonNull Runnable r) {
return sendMessageAtFrontOfQueue(getPostMessage(r));
}
关于定时,延时,优先级,标识开发者自由组合。
- 其他
...
public void dispatchMessage(@NonNull Message msg) {
...
}
...
@NonNull
public static Handler createAsync(@NonNull Looper looper) {
...
}
...
public final boolean runWithScissors(@NonNull Runnable r, long timeout) {
...
}
关于是否同步,是否移除,分发消息,获取消息等等Handler都提供了选择,如何使用根据实际情况来。
总结
- 关于消息机制,HarmonyOS和Android相差不大,大致相同。
- HarmonyOS中给消息机制提供了手动模式和托管模式,虽Android没有多种模式,但使用模式相当于HarmonyOS的托管模式。
- HarmonyOS给消息进行了优先级的划分,Android虽没有,但是对消息是否能排在消息队列前面做了处理,结果相似。
- HarmonyOS自动化程度较高,无需开发者过多操作。Android则是给开发者提供了丰富的API供调用,开发者参与感更高。
- HarmonyOS的分布式做的比较好,数据及任务渠道高效、敏捷,而Android则在横向上比较有优势,服务和功能更加丰富、灵活。
- 任何代码有开头必须有结尾,若程序未实现,则需开发者手动实现。
说明
- 有些代码展示不全,读者若需了解请自行查阅。
- 本文中HarmonyOS相关素材来自于HarmonyOS Developer 开发文档。
- 本文中Android相关素材来自于Android Developer 开发指南和《Android开发艺术探索》。
- 若有侵权或错误,请发送邮件至alphabetadata@163.com。