RxHarmony - 鸿蒙开发也可以用上 RxJava

3,182 阅读3分钟

鸿蒙 2.0 beta 版发布了,小伙伴又双叕有新的学习目标了。(掀桌子!有完没完了!!) 既然是用 Java 编程,怎么能少了 RxJava 这尊大神呢!

目标:主线程任务调度器

说起 RxJava,便捷的线程调度是其非常重要的特性,在 Android 系统中,RxAndroid 库已经提供了成熟的主线程调度器,本文的目标就是参考其思路实现鸿蒙系统下的主线程调度器。

经在线设备测试,RxJava3 可以正常运行在当前鸿蒙系统中,且鸿蒙提供了和 Android 类似的 Handler Message 多线程通信框架,那就可以通过该框架实现 HarmonyScheduler(线程调度器)了。

实现方案

参考 RxAndroid 思路,异步任务调度到主线程的核心逻辑就是包装待执行的 Runnable,然后将其通过 EventHandler 发送到主线程中执行。

核心的调度和结束调度的方法。

private static final class HandlerWorker extends Worker {
        private final RunnableObjHandler handler;
        private final boolean async;

        private volatile boolean disposed;

        private static AtomicLong count = new AtomicLong();
        // 每次新建一个 Worker 都定义 param 参数,用于移除该 Worker.dispose() 方法中移除所有该类创建的任务
        private final long paramForDispose;

        HandlerWorker(RunnableObjHandler handler, boolean async) {
            this.handler = handler;
            this.async = async;
            paramForDispose = count.incrementAndGet();
        }

        @Override
//        @SuppressLint("NewApi") // Async will only be true when the API is available to call.
        public Disposable schedule(Runnable run, long delay, TimeUnit unit) {
            if (run == null) throw new NullPointerException("run == null");
            if (unit == null) throw new NullPointerException("unit == null");

            if (disposed) {
                return Disposable.disposed();
            }

            run = RxJavaPlugins.onSchedule(run);
            // 对 runnable 参数进行包装,增加了 dispose() 方法
            ScheduledRunnable scheduled = new ScheduledRunnable(handler, run);

            // 这块注释是 RxAndroid 的实现,放这这里作为参考
//            Message message = Message.obtain(handler, scheduled);
//            message.obj = this; // Used as token for batch disposal of this worker's runnables.
//
//            if (async) {
//                message.setAsynchronous(true);
//            }
//
//            handler.sendMessageDelayed(message, unit.toMillis(delay));
//
//            // Re-check disposed state for removing in case we were racing a call to dispose().
//            if (disposed) {
//                handler.removeCallbacks(scheduled);
//                return Disposable.disposed();
//            }

            // 可以看到核心逻辑就是将传入 Runnable 参数进行包装,然后通过 handler 发到主线程

            // 比较麻烦的是 dispose 处理,因为目前鸿蒙的 InnerEvent 实例无法通过 Runnable 参数获取,所以各种 dispose 逻辑就无法按 Android 的那套实现。
            // 我的方案是将 Runnable 传入 InnerEvent.object,然后自定义 Handler,处理 InnerEvent 时强转 object 参数,然后直接调用 run 方法
            // paramForDispose 是 long 类型的标志位(HandlerWorker 实例化定义的固定值),用于标志该 Worker 所调度的所有 Runnable,设置到 param 参数,
            // 这样 Dispose 时,可根据 object 移除单任务,也可通过 param 移除该 Worker 创建的所以任务
            InnerEvent message = InnerEvent.get(handler.getInnerEventId());
            message.object = scheduled;
            message.param = paramForDispose;
            handler.sendEvent(message, unit.toMillis(delay));

            // Re-check disposed state for removing in case we were racing a call to dispose().
            if (disposed) {
                // 通过 object 参数去移除当前构建的 ScheduledRunnable 的 InnerEvent,该方法必须指定 InnerEventId,所以在自定义 Handler 中提供
                handler.removeEvent(handler.getInnerEventId(), scheduled);
                return Disposable.disposed();
            }

            return scheduled;
        }

        @Override
        public void dispose() {
            disposed = true;
//            handler.removeCallbacksAndMessages(this /* token */);
            // 通过 param 参数移除该 Worker 已调度的所有 InnerEvent
            handler.removeEvent(handler.getInnerEventId(), paramForDispose);
        }

        @Override
        public boolean isDisposed() {
            return disposed;
        }
    }

ScheduledRunnable 包装类

private static final class ScheduledRunnable implements Runnable, Disposable {
        private final RunnableObjHandler handler;
        private final Runnable delegate;

        private volatile boolean disposed; // Tracked solely for isDisposed().

        ScheduledRunnable(RunnableObjHandler handler, Runnable delegate) {
            this.handler = handler;
            this.delegate = delegate;
        }

        @Override
        public void run() {
            try {
                delegate.run();
            } catch (Throwable t) {
                RxJavaPlugins.onError(t);
            }
        }

        @Override
        public void dispose() {
//            handler.removeCallbacks(this);
            handler.removeEvent(handler.getInnerEventId(), this);
            disposed = true;
        }

        @Override
        public boolean isDisposed() {
            return disposed;
        }
    }

自定义 Handler

static class RunnableObjHandler extends EventHandler {

        private static AtomicInteger count = new AtomicInteger();
        // 每个 Handler 实例提供固定的 innerEventId
        private final int innerEventId;

        public RunnableObjHandler(EventRunner runner) throws IllegalArgumentException {
            super(runner);
            innerEventId = HandlerScheduler.class.hashCode() + count.incrementAndGet();
        }

        private int getInnerEventId() {
            return innerEventId;
        }

        @Override
        protected void processEvent(InnerEvent event) {
            super.processEvent(event);
            if (event.eventId != innerEventId) return;
            Object obj = event.object;
            if (obj instanceof Runnable) {
                ((Runnable)obj).run();
            }
        }
    }

这样,写鸿蒙 app 时,也可以“流”动起来了。

完整项目地址,如果对你有帮助的话请点个赞,点个 star,谢谢