Android EventBus粘性事件实现机制探究

Android EventBus粘性事件实现机制探究

什么是EventBus粘性事件?

在使用EventBus时,除了普通的事件外,还有一种特殊的事件——粘性事件。

所谓的粘性事件,是指在post一个事件时,如果当前并没有订阅者,那么这个事件会被保存下来(以队列的形式),等这个订阅者再次订阅时,这个事件会再次被发送出去,这就是EventBus的粘性事件实现机制。

EventBus粘性事件的实现

在EventBus的源码中,其实现机制就是优先调用订阅者的onEvent方法来订阅事件,如果没有找到该方法,则再判断是否有onEventSticky方法,如果有则将事件添加到一个类似于队列的结构中,并在后续的订阅操作中将该事件发送出去。

/**
 * Posts the given event to the event bus.
 */
public void post(Object event) {
    PostingThreadState postingState = currentPostingThreadState.get();
    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);

    if (!postingState.isPosting) {
        postingState.isMainThread = isMainThread();
        postingState.isPosting = true;
        if (postingState.canceled) {
            throw new EventBusException("Internal error. Abort state was not reset");
        }
        try {
            while (!eventQueue.isEmpty()) {
                postSingleEvent(eventQueue.remove(0), postingState);
            }
        } finally {
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}

同时,在移除粘性事件队列中的内容时,也需要对事件进行特判处理,以保证EventBus的粘性事件实现机制能够顺利执行。

/**
 * Dispatches the given {@code event} to the currently registered subscribers according to its
 * type. This method may be called from any thread, but the event will be dispatched on the main
 * thread using the {@link Handler} specified during construction.
 */
protected void dispatch(Object event, Handler handler) throws EventBusException {
    if (event == null) {
        throw new EventBusException("Event to dispatch must not be null.");
    }

    try {
        Class<?> eventType = event.getClass();
        if (eventInheritance) {
            List<Class<?>> eventTypes = lookupAllEventTypes(eventType);
            boolean dispatched = false;
            for (int i = 0, count = eventTypes.size(); i < count; i++) {
                Class<?> clazz = eventTypes.get(i);
                CopyOnWriteArrayList<Subscription> subscriptions;
                synchronized (this) {
                    subscriptions = subscriptionsByEventType.get(clazz);
                }
                if (subscriptions != null && !subscriptions.isEmpty()) {
                    for (Subscription subscription : subscriptions) {
                        enqueueEvent(event, subscription);
                    }
                    dispatched = true;
                }
            }
            if (!dispatched) {
                Log.d(TAG, "No subscribers registered for event " + eventType);
                if (eventClassBySubscriber.containsKey(eventType)) {
                    // For efficiency, we will avoid generating a stack trace since we should't
                    // have an error, this is just a warning!
                    Log.d(TAG, "(OK, don't worry, you are not using ProGuard.)");
                } else {
                    Log.d(TAG, "The eventClass " + eventType + " is not a subclass of any registered events");
                }
            }
        } else {
            CopyOnWriteArrayList<Subscription> subscriptions;
            synchronized (this) {
                subscriptions = subscriptionsByEventType.get(eventType);
            }
            if (subscriptions != null && !subscriptions.isEmpty()) {
                for (Subscription subscription : subscriptions) {
                    enqueueEvent(event, subscription);
                }
            } else {
                Log.d(TAG, "No subscribers registered for event " + eventType);
                if (eventClassBySubscriber.containsKey(eventType)) {
                    // For efficiency, we will avoid generating a stack trace since we should't
                    // have an error, this is just a warning!
                    Log.d(TAG, "(OK, don't worry, you are not using ProGuard.)");
                } else {
                    Log.d(TAG, "The eventClass " + eventType + " is not registered");
                }
            }
        }
    } finally {
        eventQueue.clear(); // 清空粘性事件队列
    }
}

EventBus粘性事件的使用

使用EventBus的粘性事件也非常简单,在EventBus.post()方法中,直接传入粘性事件即可。

EventBus.getDefault().postSticky(new TestEvent("sticky event"));

同时,在订阅的时候,和普通事件的订阅方式一致。

@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onStickyEvent(TestEvent event) {
    // do something
}

需要注意的是,在使用粘性事件的时候,需要使用sticky属性将事件标记为粘性事件,同时订阅者的onEvent方法也需要用sticky属性进行标记,以确保能够正确地接收到粘性事件。

示例

示例一

在第一个示例中,我们可以定义一个事件类TestEvent,以及在MainActivity中订阅这个事件,并且在Fragment中进行post,代码如下:

public class TestEvent {
    public String message;

    public TestEvent(String message) {
        this.message = message;
    }
}

public class MainActivity extends AppCompatActivity {

    private MyFragment mFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mFragment = new MyFragment();
        getSupportFragmentManager().beginTransaction().add(R.id.container, mFragment).commit();
        EventBus.getDefault().register(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }

    @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
    public void onStickyEvent(TestEvent event) {
        Toast.makeText(this, "sticky event msg : " + event.message, Toast.LENGTH_SHORT).show();
    }
}

public class MyFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_my, container, false);
        rootView.findViewById(R.id.btn_send_sticky).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                EventBus.getDefault().postSticky(new TestEvent("sticky event"));
            }
        });
        return rootView;
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        EventBus.getDefault().removeAllStickyEvents();
    }
}

在MainActivity中的onStickyEvent()方法中订阅了TestEvent这个事件,并且在MyFragment的点击事件中post了这个事件,这时候事件并没有被订阅者接收到,但是当我们返回MainActivity时,这个事件会被后续的订阅者接收到。

示例二

在第二个示例中,我们可以定义一个粘性事件,然后在两个不同的Activity中订阅和post这个事件,来证明EventBus粘性事件的机制是全局的。

代码如下:

public class StickyEvent {
    public String message;

    public StickyEvent(String message) {
        this.message = message;
    }
}

public class FirstActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_first);

        EventBus.getDefault().register(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }

    @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
    public void onStickyEvent(StickyEvent event) {
        Toast.makeText(this, "FirstActivity sticky event msg : " + event.message, Toast.LENGTH_SHORT).show();
    }

    public void startSecondActivity(View view) {
        startActivity(new Intent(this, SecondActivity.class));
    }
}

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        // post sticky event
        EventBus.getDefault().postSticky(new StickyEvent("sticky event"));
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().removeAllStickyEvents();
    }

    @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
    public void onStickyEvent(StickyEvent event) {
        Toast.makeText(this, "SecondActivity sticky event msg : " + event.message, Toast.LENGTH_SHORT).show();
    }

    public void startFirstActivity(View view) {
        startActivity(new Intent(this, FirstActivity.class));
    }
}

在FirstActivity和SecondActivity中都订阅了StickyEvent这个粘性事件,并且在SecondActivity中post了这个事件,随后在FirstActivity中启动SecondActivity,返回FirstActivity后,我们可以看到之前post的事件被接收到了。

总结

EventBus的粘性事件机制,可以帮助我们在事件分发时,使得那些尚未订阅的订阅者能够在之后订阅时被及时地接收到,不错过任何一个事件。使用起来也十分简单,并且支持全局的粘性事件分发。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Android EventBus粘性事件实现机制探究 - Python技术站

(0)
上一篇 2023年6月13日
下一篇 2023年6月13日

相关文章

  • 解决python中文乱码问题方法总结

    解决Python中文乱码问题方法总结 在Python中,中文乱码问题是一个常见的问题。本文将介绍解决Python中文乱码问题方法,包括设置文件编码、使用Unicode字符串、以及两个示例说明。 1. 设置文件编码 在Python中,我们可以通过文件编码来解决中文乱问题。我们可以在Python文件的开头添加以下代码: # -*- coding: utf-8 -…

    python 2023年5月13日
    00
  • 简单了解python元组tuple相关原理

    让我们来详细讲解一下“简单了解Python元组(tuple)相关原理”的完整攻略。 什么是元组(tuple) 元组(tuple)是Python中的一种序列类型,可以理解为是不可变的列表,其元素按照顺序排列。元组内的元素可以是任意数据类型,包括数字、字符串、列表、元组等等。元组一旦被创建,就不能再进行修改。 元组的创建 我们可以使用圆括号()创建一个元组,元素…

    python 2023年5月14日
    00
  • python追加元素到列表的方法

    在Python中,列表是一种非常常见的数据类型。在实际编程中,经常需要向列表中添加元素。本文将详细讲解Python中追加元素的方法。 使用append方法 可以使用append()方法向列表末尾添加一个元素。下面是一个示例: # 示例1:append()方法向列表中添加元素 lst = [1, 2, 3] lst.append(4) print(lst) #…

    python 2023年5月13日
    00
  • python自定义线程池控制线程数量的示例

    下面就是Python自定义线程池控制线程数量的完整攻略: 1. 什么是线程池? 线程池是一种线程管理方式,它可以减少线程创建和销毁的开销,提高线程的复用率。线程池在应用程序中大量使用,可有效减轻计算机资源的消耗,提高计算机系统的性能。 2. Python中的线程池 在Python中,我们可以使用标准库中的ThreadPoolExecutor类创建自定义线程池…

    python 2023年5月19日
    00
  • 腾讯出品小程序自动化测试框架【Minium】系列(七)测试框架的设计和开发

    前言 整个框架的开发及调通是在3月27日晚上22点完成,如下: 这篇文章真的是拖了太久了,久到我居然把代码部分完成后,彻底给忘了,这记性,真的是年纪大了! 框架的设计开发 1、框架搭建设计要素 日志&测试步骤 报告&失败截图 配置文件&数据源设计 公共函数&API封装 测试数据&参数化、解耦 测试套件&测试用例…

    python 2023年4月18日
    00
  • 跟老齐学Python之开始真正编程

    跟老齐学Python之开始真正编程 1. 背景介绍 本攻略是针对初学者的Python入门编程指南,旨在引导初学者开始真正编写Python代码。在学习本攻略之前,读者应该已经掌握了Python的基本语法和语言规范。 2. 开始编程 2.1 建立项目目录结构 在开始编程之前,建立一个清晰的项目目录结构对于之后的开发将非常重要。建议结构如下: project_na…

    python 2023年5月30日
    00
  • python做图片搜索引擎并保存到本地详情

    作为Python网站的作者,实现图片搜索引擎并保存到本地,需要以下步骤: 1. 安装依赖包 实现图片搜索引擎需要使用到Python的一些第三方库,比如requests、Pillow等等。使用以下命令可以通过pip安装这些依赖包: pip install requests Pillow 2. 确定搜索目标 接下来需要确定搜索的目标网站或API接口,以供获取图片…

    python 2023年6月6日
    00
  • Python 概率生成问题案例详解

    Python 概率生成问题案例详解 本文将详细讲解如何使用Python进行概率生成问题,示例说明有两条,下面我们来一步步详细介绍。 1. 确定问题 首先,我们需要明确问题的场景。测试场景通常需要我们随机生成一些数据,然后对其进行测试。因此,我们需要生成测试数据,以便对其进行测试。我们将使用Python的random库来生成测试数据。这使得我们可以生成随机数、…

    python 2023年6月3日
    00
合作推广
合作推广
分享本页
返回顶部